diff options
Diffstat (limited to '')
59 files changed, 210 insertions, 13664 deletions
diff --git a/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch b/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch deleted file mode 100644 index f39fa72..0000000 --- a/debian/patches/0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch +++ /dev/null @@ -1,120 +0,0 @@ -From: Eric Covener <covener@apache.org> -Date: Sun, 5 Mar 2023 20:22:52 +0000 -Subject: CVE-2023-27522: HTTP Response Smuggling mod_proxy_uwsgi - -HTTP Response Smuggling vulnerability in Apache HTTP Server via mod_proxy_uwsgi. -This issue affects Apache HTTP Server: from 2.4.30 through 2.4.55. -Special characters in the origin response header can truncate/split the response forwarded to the client. - -mod_proxy_uwsgi: Stricter backend HTTP response parsing/validation - -Reviewed By: ylavic, covener, gbechis, rpluem - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908094 13f79535-47bb-0310-9956-ffa450edef68 -origin: https://github.com/apache/httpd/commit/d753ea76b5972a85349b68c31b59d04c60014f2d.patch -bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 -bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-27522 -bug-cve: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27522 ---- - .../proxy_uwsgi_response_validation.txt | 2 + - modules/proxy/mod_proxy_uwsgi.c | 49 +++++++++++++++------- - 2 files changed, 37 insertions(+), 14 deletions(-) - create mode 100644 changes-entries/proxy_uwsgi_response_validation.txt - -diff --git a/changes-entries/proxy_uwsgi_response_validation.txt b/changes-entries/proxy_uwsgi_response_validation.txt -new file mode 100644 -index 0000000..2cdb6c6 ---- /dev/null -+++ b/changes-entries/proxy_uwsgi_response_validation.txt -@@ -0,0 +1,2 @@ -+ *) mod_proxy_uwsgi: Stricter backend HTTP response parsing/validation. -+ [Yann Ylavic] -diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c -index ebe16e8..9ba10b9 100644 ---- a/modules/proxy/mod_proxy_uwsgi.c -+++ b/modules/proxy/mod_proxy_uwsgi.c -@@ -303,18 +303,16 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, - pass_bb = apr_brigade_create(r->pool, c->bucket_alloc); - - len = ap_getline(buffer, sizeof(buffer), rp, 1); -- - if (len <= 0) { -- /* oops */ -+ /* invalid or empty */ - return HTTP_INTERNAL_SERVER_ERROR; - } -- - backend->worker->s->read += len; -- -- if (len >= sizeof(buffer) - 1) { -- /* oops */ -+ if ((apr_size_t)len >= sizeof(buffer)) { -+ /* too long */ - return HTTP_INTERNAL_SERVER_ERROR; - } -+ - /* Position of http status code */ - if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { - status_start = 9; -@@ -323,8 +321,8 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, - status_start = 7; - } - else { -- /* oops */ -- return HTTP_INTERNAL_SERVER_ERROR; -+ /* not HTTP */ -+ return HTTP_BAD_GATEWAY; - } - status_end = status_start + 3; - -@@ -344,21 +342,44 @@ static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, - } - r->status_line = apr_pstrdup(r->pool, &buffer[status_start]); - -- /* start parsing headers */ -+ /* parse headers */ - while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) { -+ if ((apr_size_t)len >= sizeof(buffer)) { -+ /* too long */ -+ len = -1; -+ break; -+ } - value = strchr(buffer, ':'); -- /* invalid header skip */ -- if (!value) -- continue; -- *value = '\0'; -- ++value; -+ if (!value) { -+ /* invalid header */ -+ len = -1; -+ break; -+ } -+ *value++ = '\0'; -+ if (*ap_scan_http_token(buffer)) { -+ /* invalid name */ -+ len = -1; -+ break; -+ } - while (apr_isspace(*value)) - ++value; - for (end = &value[strlen(value) - 1]; - end > value && apr_isspace(*end); --end) - *end = '\0'; -+ if (*ap_scan_http_field_content(value)) { -+ /* invalid value */ -+ len = -1; -+ break; -+ } - apr_table_add(r->headers_out, buffer, value); - } -+ if (len < 0) { -+ /* Reset headers, but not to NULL because things below the chain expect -+ * this to be non NULL e.g. the ap_content_length_filter. -+ */ -+ r->headers_out = apr_table_make(r->pool, 1); -+ return HTTP_BAD_GATEWAY; -+ } - - if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { - ap_set_content_type(r, apr_pstrdup(r->pool, buf)); diff --git a/debian/patches/0053-CVE-2023-25690-1.patch b/debian/patches/0053-CVE-2023-25690-1.patch deleted file mode 100644 index a7370c7..0000000 --- a/debian/patches/0053-CVE-2023-25690-1.patch +++ /dev/null @@ -1,170 +0,0 @@ -From 8789f6bb926fa4c33b4231a8444340515c82bdff Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Sun, 5 Mar 2023 20:28:43 +0000 -Subject: [PATCH] [1/2] Fix CVE-2023-25690: HTTP Request Smuggling in mod_proxy* - - don't forward invalid query strings - - Submitted by: rpluem - -Reviewed By: covener, fielding, rpluem, gbechis -bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2023-25690 -bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 -bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-25690 -origin: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908096 13f79535-47bb-0310-9956-ffa450edef68 -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908096 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/http2/mod_proxy_http2.c | 10 ++++++++++ - modules/mappers/mod_rewrite.c | 22 ++++++++++++++++++++++ - modules/proxy/mod_proxy_ajp.c | 10 ++++++++++ - modules/proxy/mod_proxy_balancer.c | 10 ++++++++++ - modules/proxy/mod_proxy_http.c | 10 ++++++++++ - modules/proxy/mod_proxy_wstunnel.c | 10 ++++++++++ - 6 files changed, 72 insertions(+) - -diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c -index 3faf03472bb..aa299b937a5 100644 ---- a/modules/http2/mod_proxy_http2.c -+++ b/modules/http2/mod_proxy_http2.c -@@ -158,6 +158,16 @@ static int proxy_http2_canon(request_rec *r, char *url) - path = ap_proxy_canonenc(r->pool, url, (int)strlen(url), - enc_path, 0, r->proxyreq); - search = r->args; -+ if (search && *(ap_scan_vchar_obstext(search))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() -+ "To be forwarded query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } - } - break; - case PROXYREQ_PROXY: -diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c -index 943996560e5..f6398f19386 100644 ---- a/modules/mappers/mod_rewrite.c -+++ b/modules/mappers/mod_rewrite.c -@@ -4729,6 +4729,17 @@ static int hook_uri2file(request_rec *r) - unsigned skip; - apr_size_t flen; - -+ if (r->args && *(ap_scan_vchar_obstext(r->args))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) -+ "Rewritten query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } -+ - if (ACTION_STATUS == rulestatus) { - int n = r->status; - -@@ -5013,6 +5024,17 @@ static int hook_fixup(request_rec *r) - if (rulestatus) { - unsigned skip; - -+ if (r->args && *(ap_scan_vchar_obstext(r->args))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411) -+ "Rewritten query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } -+ - if (ACTION_STATUS == rulestatus) { - int n = r->status; - -diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c -index 1449acad733..e46bd903a36 100644 ---- a/modules/proxy/mod_proxy_ajp.c -+++ b/modules/proxy/mod_proxy_ajp.c -@@ -69,6 +69,16 @@ static int proxy_ajp_canon(request_rec *r, char *url) - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); - search = r->args; -+ if (search && *(ap_scan_vchar_obstext(search))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10406) -+ "To be forwarded query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } - } - if (path == NULL) - return HTTP_BAD_REQUEST; -diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c -index f6fb6345ae3..7f990084336 100644 ---- a/modules/proxy/mod_proxy_balancer.c -+++ b/modules/proxy/mod_proxy_balancer.c -@@ -106,6 +106,16 @@ static int proxy_balancer_canon(request_rec *r, char *url) - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); - search = r->args; -+ if (search && *(ap_scan_vchar_obstext(search))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10407) -+ "To be forwarded query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } - } - if (path == NULL) - return HTTP_BAD_REQUEST; -diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c -index ec4e7fb06b5..51d19a0a21b 100644 ---- a/modules/proxy/mod_proxy_http.c -+++ b/modules/proxy/mod_proxy_http.c -@@ -125,6 +125,16 @@ static int proxy_http_canon(request_rec *r, char *url) - path = ap_proxy_canonenc(r->pool, url, strlen(url), - enc_path, 0, r->proxyreq); - search = r->args; -+ if (search && *(ap_scan_vchar_obstext(search))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10408) -+ "To be forwarded query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } - } - break; - case PROXYREQ_PROXY: -diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c -index bcbba42f9a4..88f86a49dbb 100644 ---- a/modules/proxy/mod_proxy_wstunnel.c -+++ b/modules/proxy/mod_proxy_wstunnel.c -@@ -114,6 +114,16 @@ static int proxy_wstunnel_canon(request_rec *r, char *url) - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); - search = r->args; -+ if (search && *(ap_scan_vchar_obstext(search))) { -+ /* -+ * We have a raw control character or a ' ' in r->args. -+ * Correct encoding was missed. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10409) -+ "To be forwarded query string contains control " -+ "characters or spaces"); -+ return HTTP_FORBIDDEN; -+ } - } - if (path == NULL) - return HTTP_BAD_REQUEST; - diff --git a/debian/patches/0054-CVE-2023-25690-2.patch b/debian/patches/0054-CVE-2023-25690-2.patch deleted file mode 100644 index 978be78..0000000 --- a/debian/patches/0054-CVE-2023-25690-2.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 8b93a6512f14f5f68887ddfe677e91233ed79fb0 Mon Sep 17 00:00:00 2001 -From: Ruediger Pluem <rpluem@apache.org> -Date: Mon, 6 Mar 2023 10:00:09 +0000 -Subject: [PATCH] [2/2] Fix CVE-2023-25690: HTTP Request Smuggling in mod_proxy* - -* modules/http2/mod_proxy_http2.c: Fix missing APLOGNO. - -Submitted by: jorton -Reviewed by: rpluem - -Note: mod_proxy_http2 is CTR on 2.4.x. - -bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2023-25690 -bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032476 -bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2023-25690 -origin: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908118 13f79535-47bb-0310-9956-ffa450edef68 -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1908118 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/http2/mod_proxy_http2.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c -index aa299b937a5..2a9967e5d57 100644 ---- a/modules/http2/mod_proxy_http2.c -+++ b/modules/http2/mod_proxy_http2.c -@@ -163,7 +163,7 @@ static int proxy_http2_canon(request_rec *r, char *url) - * We have a raw control character or a ' ' in r->args. - * Correct encoding was missed. - */ -- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10412) - "To be forwarded query string contains control " - "characters or spaces"); - return HTTP_FORBIDDEN; - diff --git a/debian/patches/0055-CVE-2023-25690-Regression-1.patch b/debian/patches/0055-CVE-2023-25690-Regression-1.patch deleted file mode 100644 index d57a71c..0000000 --- a/debian/patches/0055-CVE-2023-25690-Regression-1.patch +++ /dev/null @@ -1,131 +0,0 @@ -From 815cf05bb2d506f44a35b65e93de393d5410c779 Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Tue, 1 Mar 2022 13:26:03 +0000 -Subject: [PATCH] mod_rewrite: URI-to-filename rewrites to transparently handle - proxy mappings. - -Since mod_rewrite works on r->filename and mod_proxy's mapping=servlet|decoded -sets its "proxy:" URL there at pre_translate_name stage (i.e. before -mod_rewrite's translate_name hook), users have to match the full proxy URL in -their RewriteRules to handle proxy mappings, which is not very friendly nor -consistent with how proxy non-mapping requests have to be matched. - -Let's use r->filename = r->uri in hook_uri2file() for pre_trans'ed reverse -proxy requests, and restore r->filename to its original value if the request -was finally DECLINED (like in hook_fixup). - -But if a proxy mapping gets rewritten to a non-proxy request, clear any -proxy specific r->proxyreq or r->handler so that processing continues -accordingly. - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1898509 13f79535-47bb-0310-9956-ffa450edef68 ---- - changes-entries/rewrite_vs_proxy_mapping.txt | 2 ++ - modules/mappers/mod_rewrite.c | 38 +++++++++++++++----- - 2 files changed, 32 insertions(+), 8 deletions(-) - create mode 100644 changes-entries/rewrite_vs_proxy_mapping.txt - -Index: apache2/changes-entries/rewrite_vs_proxy_mapping.txt -=================================================================== ---- /dev/null -+++ apache2/changes-entries/rewrite_vs_proxy_mapping.txt -@@ -0,0 +1,2 @@ -+ *) mod_rewrite: Make URI-to-filename rewrites work transparently with -+ proxy early mappings (mapping=servlet/decoded). [Yann Ylavic] -\ No newline at end of file -Index: apache2/modules/mappers/mod_rewrite.c -=================================================================== ---- apache2.orig/modules/mappers/mod_rewrite.c -+++ apache2/modules/mappers/mod_rewrite.c -@@ -4575,6 +4575,7 @@ static int hook_uri2file(request_rec *r) - unsigned int port; - int rulestatus; - void *skipdata; -+ char *ofilename; - const char *oargs; - - /* -@@ -4628,7 +4629,10 @@ static int hook_uri2file(request_rec *r) - /* - * remember the original query string for later check, since we don't - * want to apply URL-escaping when no substitution has changed it. -+ * also, we'll restore original r->filename if we decline this -+ * request. - */ -+ ofilename = r->filename; - oargs = r->args; - - /* -@@ -4671,13 +4675,15 @@ static int hook_uri2file(request_rec *r) - apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var); - - if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) { -- /* if filename was not initially set, -- * we start with the requested URI -+ /* If r->filename was not initially set or if it's a pre_trans reverse -+ * "proxy:" scheme, we start with the requested URI. - */ -- if (r->filename == NULL) { -+ if (r->filename == NULL || (r->proxyreq == PROXYREQ_REVERSE && -+ strncmp(r->filename, "proxy:", 6) == 0)) { - r->filename = apr_pstrdup(r->pool, r->uri); -- rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s", -- r->filename)); -+ rewritelog((r, 2, NULL, "init rewrite engine with requested uri " -+ "%s. Original filename = %s", r->filename, -+ ((ofilename) ? ofilename : "n/a"))); - } - else { - rewritelog((r, 2, NULL, "init rewrite engine with passed filename " -@@ -4701,6 +4707,7 @@ static int hook_uri2file(request_rec *r) - if (rulestatus) { - unsigned skip; - apr_size_t flen; -+ int to_proxyreq; - - if (r->args && *(ap_scan_vchar_obstext(r->args))) { - /* -@@ -4721,7 +4728,19 @@ static int hook_uri2file(request_rec *r) - } - - flen = r->filename ? strlen(r->filename) : 0; -- if (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0) { -+ to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); -+ -+ /* If a pre_trans reverse "proxy:" filename gets rewritten to -+ * a non-proxy one this is not a proxy request anymore. -+ */ -+ if (r->proxyreq == PROXYREQ_REVERSE && !to_proxyreq) { -+ if (r->handler && strcmp(r->handler, "proxy-server") == 0) { -+ r->handler = NULL; -+ } -+ r->proxyreq = PROXYREQ_NONE; -+ } -+ -+ if (to_proxyreq) { - /* it should be go on as an internal proxy request */ - - /* check if the proxy module is enabled, so -@@ -4888,7 +4907,9 @@ static int hook_uri2file(request_rec *r) - } - } - else { -- rewritelog((r, 1, NULL, "pass through %s", r->filename)); -+ rewritelog((r, 1, NULL, "pass through %s, filename %s", -+ r->filename, ((ofilename) ? ofilename : "n/a"))); -+ r->filename = ofilename; - return DECLINED; - } - } -@@ -5234,7 +5255,8 @@ static int hook_fixup(request_rec *r) - } - } - else { -- rewritelog((r, 1, dconf->directory, "pass through %s", r->filename)); -+ rewritelog((r, 1, dconf->directory, "pass through %s, filename %s", -+ r->filename, ((ofilename) ? ofilename : "n/a"))); - r->filename = ofilename; - return DECLINED; - } diff --git a/debian/patches/0056-CVE-2023-25690-Regression-2.patch b/debian/patches/0056-CVE-2023-25690-Regression-2.patch deleted file mode 100644 index 55eaa6b..0000000 --- a/debian/patches/0056-CVE-2023-25690-Regression-2.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 07b802c934b841d376733a3e2ecfa55f6b0ee994 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Sat, 11 Mar 2023 20:57:52 +0000 -Subject: [PATCH] allow decoded chars when they will be escaped - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908296 13f79535-47bb-0310-9956-ffa450edef68 ---- - changes-entries/rewrite-escape.diff | 3 ++ - modules/mappers/mod_rewrite.c | 45 +++++++++++++++++------------ - 2 files changed, 30 insertions(+), 18 deletions(-) - create mode 100644 changes-entries/rewrite-escape.diff - -Index: apache2/changes-entries/rewrite-escape.diff -=================================================================== ---- /dev/null -+++ apache2/changes-entries/rewrite-escape.diff -@@ -0,0 +1,3 @@ -+ *) mod_rewrite: Re-allow some proxy and redirect substitutions flagged as -+ 403 errors in 2.4.56. [Eric Covener] -+ -Index: apache2/modules/mappers/mod_rewrite.c -=================================================================== ---- apache2.orig/modules/mappers/mod_rewrite.c -+++ apache2/modules/mappers/mod_rewrite.c -@@ -4705,14 +4705,19 @@ static int hook_uri2file(request_rec *r) - } - - if (rulestatus) { -- unsigned skip; -- apr_size_t flen; -- int to_proxyreq; -- -- if (r->args && *(ap_scan_vchar_obstext(r->args))) { -+ unsigned skip_absolute = is_absolute_uri(r->filename, NULL); -+ apr_size_t flen = r->filename ? strlen(r->filename) : 0; -+ int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); -+ int will_escape = (to_proxyreq || skip_absolute) -+ && (rulestatus != ACTION_NOESCAPE); -+ -+ if (r->args -+ && !will_escape -+ && *(ap_scan_vchar_obstext(r->args))) { - /* - * We have a raw control character or a ' ' in r->args. -- * Correct encoding was missed. -+ * Correct encoding was missed and we're not going to escape -+ * it before returning. - */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) - "Rewritten query string contains control " -@@ -4727,9 +4732,6 @@ static int hook_uri2file(request_rec *r) - return n; - } - -- flen = r->filename ? strlen(r->filename) : 0; -- to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); -- - /* If a pre_trans reverse "proxy:" filename gets rewritten to - * a non-proxy one this is not a proxy request anymore. - */ -@@ -4782,15 +4784,15 @@ static int hook_uri2file(request_rec *r) - r->filename)); - return OK; - } -- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { -+ else if (skip_absolute > 0) { - int n; - - /* it was finally rewritten to a remote URL */ - - if (rulestatus != ACTION_NOESCAPE) { - rewritelog((r, 1, NULL, "escaping %s for redirect", -- r->filename)); -- r->filename = escape_absolute_uri(r->pool, r->filename, skip); -+ r->filename)); -+ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); - } - - /* append the QUERY_STRING part */ -@@ -5016,9 +5018,17 @@ static int hook_fixup(request_rec *r) - */ - rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory); - if (rulestatus) { -- unsigned skip; -+ unsigned skip_absolute = is_absolute_uri(r->filename, NULL); -+ int to_proxyreq = 0; -+ int will_escape = 0; - -- if (r->args && *(ap_scan_vchar_obstext(r->args))) { -+ l = strlen(r->filename); -+ to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0; -+ will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); -+ -+ if (r->args -+ && !will_escape -+ && *(ap_scan_vchar_obstext(r->args))) { - /* - * We have a raw control character or a ' ' in r->args. - * Correct encoding was missed. -@@ -5036,8 +5046,7 @@ static int hook_fixup(request_rec *r) - return n; - } - -- l = strlen(r->filename); -- if (l > 6 && strncmp(r->filename, "proxy:", 6) == 0) { -+ if (to_proxyreq) { - /* it should go on as an internal proxy request */ - - /* make sure the QUERY_STRING and -@@ -5061,7 +5070,7 @@ static int hook_fixup(request_rec *r) - "%s [OK]", r->filename)); - return OK; - } -- else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { -+ else if (skip_absolute > 0) { - /* it was finally rewritten to a remote URL */ - - /* because we are in a per-dir context -@@ -5070,7 +5079,7 @@ static int hook_fixup(request_rec *r) - */ - if (dconf->baseurl != NULL) { - /* skip 'scheme://' */ -- cp = r->filename + skip; -+ cp = r->filename + skip_absolute; - - if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) { - rewritelog((r, 2, dconf->directory, -@@ -5114,8 +5123,8 @@ static int hook_fixup(request_rec *r) - /* now prepare the redirect... */ - if (rulestatus != ACTION_NOESCAPE) { - rewritelog((r, 1, dconf->directory, "escaping %s for redirect", -- r->filename)); -- r->filename = escape_absolute_uri(r->pool, r->filename, skip); -+ r->filename)); -+ r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); - } - - /* append the QUERY_STRING part */ diff --git a/debian/patches/0057-CVE-2023-25690-Regression-3.patch b/debian/patches/0057-CVE-2023-25690-Regression-3.patch deleted file mode 100644 index 431f145..0000000 --- a/debian/patches/0057-CVE-2023-25690-Regression-3.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 1a4aac3d209f4314bcb511d73cf12f8c25c8c984 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Sat, 11 Mar 2023 21:29:11 +0000 -Subject: [PATCH] followup to r1908296: only for redirects - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908299 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/mappers/mod_rewrite.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -Index: apache2/modules/mappers/mod_rewrite.c -=================================================================== ---- apache2.orig/modules/mappers/mod_rewrite.c -+++ apache2/modules/mappers/mod_rewrite.c -@@ -4708,8 +4708,7 @@ static int hook_uri2file(request_rec *r) - unsigned skip_absolute = is_absolute_uri(r->filename, NULL); - apr_size_t flen = r->filename ? strlen(r->filename) : 0; - int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); -- int will_escape = (to_proxyreq || skip_absolute) -- && (rulestatus != ACTION_NOESCAPE); -+ int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); - - if (r->args - && !will_escape diff --git a/debian/patches/CVE-2006-20001.patch b/debian/patches/CVE-2006-20001.patch deleted file mode 100644 index 0ba150b..0000000 --- a/debian/patches/CVE-2006-20001.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 7469547c3f617717ca545d0f7c56d01134703813 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Tue, 10 Jan 2023 13:21:48 +0000 -Subject: [PATCH] Merge r1906487 from trunk: - -* modules/dav/main/util.c (dav_process_if_header): Fix error - path for "Not" prefix parsing. - - -Submitted By: jorton -Reviewed By: jorton, covener, rpluem - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906543 13f79535-47bb-0310-9956-ffa450edef68 ---- - STATUS | 8 -------- - modules/dav/main/util.c | 8 +++++++- - 2 files changed, 7 insertions(+), 9 deletions(-) - ---- a/modules/dav/main/util.c -+++ b/modules/dav/main/util.c -@@ -746,8 +746,14 @@ - "for the same state."); - } - condition = DAV_IF_COND_NOT; -+ list += 2; -+ } -+ else { -+ return dav_new_error(r->pool, HTTP_BAD_REQUEST, -+ DAV_ERR_IF_UNK_CHAR, 0, -+ "Invalid \"If:\" header: " -+ "Unexpected character in List"); - } -- list += 2; - break; - - case ' ': diff --git a/debian/patches/CVE-2019-0196.patch b/debian/patches/CVE-2019-0196.patch deleted file mode 100644 index eaec989..0000000 --- a/debian/patches/CVE-2019-0196.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 8de3c6f2a0df79d1476c89ec480a96f9282cea28 Mon Sep 17 00:00:00 2001 -From: Stefan Eissing <icing@apache.org> -Date: Tue, 5 Feb 2019 11:52:28 +0000 -Subject: [PATCH] Merge of r1852986 from trunk: - -mod_http2: disentangelment of stream and request method. - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1852989 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/http2/h2_request.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c -index 8899c4feb75..5ee88e9679f 100644 ---- a/modules/http2/h2_request.c -+++ b/modules/http2/h2_request.c -@@ -266,7 +266,7 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) - - /* Time to populate r with the data we have. */ - r->request_time = req->request_time; -- r->method = req->method; -+ r->method = apr_pstrdup(r->pool, req->method); - /* Provide quick information about the request method as soon as known */ - r->method_number = ap_method_number_of(r->method); - if (r->method_number == M_GET && r->method[0] == 'H') { diff --git a/debian/patches/CVE-2019-0197.patch b/debian/patches/CVE-2019-0197.patch deleted file mode 100644 index 92d2943..0000000 --- a/debian/patches/CVE-2019-0197.patch +++ /dev/null @@ -1,93 +0,0 @@ -# https://svn.apache.org/r1855406 ---- apache2.orig/modules/http2/h2_conn.c -+++ apache2/modules/http2/h2_conn.c -@@ -305,6 +305,10 @@ conn_rec *h2_slave_create(conn_rec *mast - c->notes = apr_table_make(pool, 5); - c->input_filters = NULL; - c->output_filters = NULL; -+ c->keepalives = 0; -+#if AP_MODULE_MAGIC_AT_LEAST(20180903, 1) -+ c->filter_conn_ctx = NULL; -+#endif - c->bucket_alloc = apr_bucket_alloc_create(pool); - c->data_in_input_filters = 0; - c->data_in_output_filters = 0; -@@ -332,16 +336,15 @@ conn_rec *h2_slave_create(conn_rec *mast - ap_set_module_config(c->conn_config, mpm, cfg); - } - -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, -- "h2_stream(%ld-%d): created slave", master->id, slave_id); -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, -+ "h2_slave(%s): created", c->log_id); - return c; - } - - void h2_slave_destroy(conn_rec *slave) - { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave, -- "h2_stream(%s): destroy slave", -- apr_table_get(slave->notes, H2_TASK_ID_NOTE)); -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, slave, -+ "h2_slave(%s): destroy", slave->log_id); - slave->sbh = NULL; - apr_pool_destroy(slave->pool); - } -@@ -365,6 +368,7 @@ apr_status_t h2_slave_run_pre_connection - slave->keepalive = AP_CONN_CLOSE; - return ap_run_pre_connection(slave, csd); - } -+ ap_assert(slave->output_filters); - return APR_SUCCESS; - } - ---- apache2.orig/modules/http2/h2_mplx.c -+++ apache2/modules/http2/h2_mplx.c -@@ -327,7 +327,8 @@ static int stream_destroy_iter(void *ctx - && !task->rst_error); - } - -- if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) { -+ task->c = NULL; -+ if (reuse_slave) { - h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, - APLOGNO(03385) "h2_task_destroy, reuse slave"); - h2_task_destroy(task); -@@ -437,6 +438,8 @@ void h2_mplx_release_and_join(h2_mplx *m - apr_status_t status; - int i, wait_secs = 60; - -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, -+ "h2_mplx(%ld): start release", m->id); - /* How to shut down a h2 connection: - * 0. abort and tell the workers that no more tasks will come from us */ - m->aborted = 1; -@@ -977,6 +980,9 @@ static apr_status_t unschedule_slow_task - */ - n = (m->tasks_active - m->limit_active - (int)h2_ihash_count(m->sredo)); - while (n > 0 && (stream = get_latest_repeatable_unsubmitted_stream(m))) { -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, -+ "h2_mplx(%s): unschedule, resetting task for redo later", -+ stream->task->id); - h2_task_rst(stream->task, H2_ERR_CANCEL); - h2_ihash_add(m->sredo, stream); - --n; ---- apache2.orig/modules/http2/h2_task.c -+++ apache2/modules/http2/h2_task.c -@@ -504,7 +504,7 @@ static int h2_task_pre_conn(conn_rec* c, - (void)arg; - if (h2_ctx_is_task(ctx)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, -- "h2_h2, pre_connection, found stream task"); -+ "h2_slave(%s), pre_connection, adding filters", c->log_id); - ap_add_input_filter("H2_SLAVE_IN", NULL, NULL, c); - ap_add_output_filter("H2_PARSE_H1", NULL, NULL, c); - ap_add_output_filter("H2_SLAVE_OUT", NULL, NULL, c); -@@ -545,7 +545,6 @@ h2_task *h2_task_create(conn_rec *slave, - void h2_task_destroy(h2_task *task) - { - if (task->output.beam) { -- h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "task_destroy"); - h2_beam_destroy(task->output.beam); - task->output.beam = NULL; - } diff --git a/debian/patches/CVE-2019-0211.patch b/debian/patches/CVE-2019-0211.patch deleted file mode 100644 index 1b69f45..0000000 --- a/debian/patches/CVE-2019-0211.patch +++ /dev/null @@ -1,249 +0,0 @@ -From df7edb5ddae609ea1fd4285f7439f0d590d97b37 Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Wed, 13 Mar 2019 08:59:54 +0000 -Subject: [PATCH] Merge r1855306 from trunk: - -MPMs unix: bind the bucket number of each child to its slot number - -We need not remember each child's bucket number in SHM for restarts, for the -lifetime of the httpd main process the bucket number can be bound to the slot -number such that: bucket = slot % num_buckets. - -This both simplifies the logic and helps children maintenance per bucket in -threaded MPMs, where previously perform_idle_server_maintenance() could create -or kill children processes for the buckets it was not in charge of. - -Submitted by: ylavic -Reviewed by: ylavic, rpluem, jorton - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855378 13f79535-47bb-0310-9956-ffa450edef68 ---- - CHANGES | 3 +++ - include/scoreboard.h | 4 +++- - server/mpm/event/event.c | 13 ++++++++----- - server/mpm/prefork/prefork.c | 19 +++++++------------ - server/mpm/worker/worker.c | 10 ++++++---- - 5 files changed, 27 insertions(+), 22 deletions(-) - -#diff --git a/CHANGES b/CHANGES -#index e79251389d5..6b560802119 100644 -#--- a/CHANGES -#+++ b/CHANGES -#@@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- -# Changes with Apache 2.4.39 -# -#+ *) MPMs unix: bind the bucket number of each child to its slot number, for a -#+ more efficient per bucket maintenance. [Yann Ylavic] -#+ -# *) mod_auth_digest: Fix a race condition. Authentication with valid -# credentials could be refused in case of concurrent accesses from -# different users. PR 63124. [Simon Kappel <simon.kappel axis.com>] -diff --git a/include/scoreboard.h b/include/scoreboard.h -index 9376da246b0..92d198d6de1 100644 ---- a/include/scoreboard.h -+++ b/include/scoreboard.h -@@ -148,7 +148,9 @@ struct process_score { - apr_uint32_t lingering_close; /* async connections in lingering close */ - apr_uint32_t keep_alive; /* async connections in keep alive */ - apr_uint32_t suspended; /* connections suspended by some module */ -- int bucket; /* Listener bucket used by this child */ -+ int bucket; /* Listener bucket used by this child; this field is DEPRECATED -+ * and no longer updated by the MPMs (i.e. always zero). -+ */ - }; - - /* Scoreboard is now in 'local' memory, since it isn't updated once created, -diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c -index 4cfb09c5b28..5e5af339adc 100644 ---- a/server/mpm/event/event.c -+++ b/server/mpm/event/event.c -@@ -2696,7 +2696,6 @@ static int make_child(server_rec * s, int slot, int bucket) - - ap_scoreboard_image->parent[slot].quiescing = 0; - ap_scoreboard_image->parent[slot].not_accepting = 0; -- ap_scoreboard_image->parent[slot].bucket = bucket; - event_note_child_started(slot, pid); - active_daemons++; - retained->total_daemons++; -@@ -2735,6 +2734,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - * that threads_per_child is always > 0 */ - int status = SERVER_DEAD; - int child_threads_active = 0; -+ int bucket = i % num_buckets; - - if (i >= retained->max_daemons_limit && - free_length == retained->idle_spawn_rate[child_bucket]) { -@@ -2758,7 +2758,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - */ - if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting - && ps->generation == retained->mpm->my_generation -- && ps->bucket == child_bucket) -+ && bucket == child_bucket) - { - ++idle_thread_count; - } -@@ -2769,7 +2769,9 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - last_non_dead = i; - } - active_thread_count += child_threads_active; -- if (!ps->pid && free_length < retained->idle_spawn_rate[child_bucket]) -+ if (!ps->pid -+ && bucket == child_bucket -+ && free_length < retained->idle_spawn_rate[child_bucket]) - free_slots[free_length++] = i; - else if (child_threads_active == threads_per_child) - had_healthy_child = 1; -@@ -2962,13 +2964,14 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets) - retained->total_daemons--; - if (processed_status == APEXIT_CHILDSICK) { - /* resource shortage, minimize the fork rate */ -- retained->idle_spawn_rate[ps->bucket] = 1; -+ retained->idle_spawn_rate[child_slot % num_buckets] = 1; - } - else if (remaining_children_to_start) { - /* we're still doing a 1-for-1 replacement of dead - * children with new children - */ -- make_child(ap_server_conf, child_slot, ps->bucket); -+ make_child(ap_server_conf, child_slot, -+ child_slot % num_buckets); - --remaining_children_to_start; - } - } -diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c -index 8efda72ee18..7c006257301 100644 ---- a/server/mpm/prefork/prefork.c -+++ b/server/mpm/prefork/prefork.c -@@ -637,8 +637,9 @@ static void child_main(int child_num_arg, int child_bucket) - } - - --static int make_child(server_rec *s, int slot, int bucket) -+static int make_child(server_rec *s, int slot) - { -+ int bucket = slot % retained->mpm->num_buckets; - int pid; - - if (slot + 1 > retained->max_daemons_limit) { -@@ -716,7 +717,6 @@ static int make_child(server_rec *s, int slot, int bucket) - child_main(slot, bucket); - } - -- ap_scoreboard_image->parent[slot].bucket = bucket; - prefork_note_child_started(slot, pid); - - return 0; -@@ -732,7 +732,7 @@ static void startup_children(int number_to_start) - if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) { - continue; - } -- if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) { -+ if (make_child(ap_server_conf, i) < 0) { - break; - } - --number_to_start; -@@ -741,8 +741,6 @@ static void startup_children(int number_to_start) - - static void perform_idle_server_maintenance(apr_pool_t *p) - { -- static int bucket_make_child_record = -1; -- static int bucket_kill_child_record = -1; - int i; - int idle_count; - worker_score *ws; -@@ -789,6 +787,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p) - } - retained->max_daemons_limit = last_non_dead + 1; - if (idle_count > ap_daemons_max_free) { -+ static int bucket_kill_child_record = -1; - /* kill off one child... we use the pod because that'll cause it to - * shut down gracefully, in case it happened to pick up a request - * while we were counting -@@ -819,10 +818,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p) - idle_count, total_non_dead); - } - for (i = 0; i < free_length; ++i) { -- bucket_make_child_record++; -- bucket_make_child_record %= retained->mpm->num_buckets; -- make_child(ap_server_conf, free_slots[i], -- bucket_make_child_record); -+ make_child(ap_server_conf, free_slots[i]); - } - /* the next time around we want to spawn twice as many if this - * wasn't good enough, but not if we've just done a graceful -@@ -867,7 +863,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) - - if (one_process) { - AP_MONCONTROL(1); -- make_child(ap_server_conf, 0, 0); -+ make_child(ap_server_conf, 0); - /* NOTREACHED */ - ap_assert(0); - return !OK; -@@ -976,8 +972,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) - /* we're still doing a 1-for-1 replacement of dead - * children with new children - */ -- make_child(ap_server_conf, child_slot, -- ap_get_scoreboard_process(child_slot)->bucket); -+ make_child(ap_server_conf, child_slot); - --remaining_children_to_start; - } - #if APR_HAS_OTHER_CHILD -diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c -index 8012fe29d8d..a92794245c5 100644 ---- a/server/mpm/worker/worker.c -+++ b/server/mpm/worker/worker.c -@@ -1339,7 +1339,6 @@ static int make_child(server_rec *s, int slot, int bucket) - worker_note_child_lost_slot(slot, pid); - } - ap_scoreboard_image->parent[slot].quiescing = 0; -- ap_scoreboard_image->parent[slot].bucket = bucket; - worker_note_child_started(slot, pid); - return 0; - } -@@ -1388,6 +1387,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - int any_dead_threads = 0; - int all_dead_threads = 1; - int child_threads_active = 0; -+ int bucket = i % num_buckets; - - if (i >= retained->max_daemons_limit && - totally_free_length == retained->idle_spawn_rate[child_bucket]) { -@@ -1420,7 +1420,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - if (status <= SERVER_READY && - !ps->quiescing && - ps->generation == retained->mpm->my_generation && -- ps->bucket == child_bucket) { -+ bucket == child_bucket) { - ++idle_thread_count; - } - if (status >= SERVER_READY && status < SERVER_GRACEFUL) { -@@ -1430,6 +1430,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets) - } - active_thread_count += child_threads_active; - if (any_dead_threads -+ && bucket == child_bucket - && totally_free_length < retained->idle_spawn_rate[child_bucket] - && free_length < MAX_SPAWN_RATE / num_buckets - && (!ps->pid /* no process in the slot */ -@@ -1615,14 +1616,15 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets) - ps->quiescing = 0; - if (processed_status == APEXIT_CHILDSICK) { - /* resource shortage, minimize the fork rate */ -- retained->idle_spawn_rate[ps->bucket] = 1; -+ retained->idle_spawn_rate[child_slot % num_buckets] = 1; - } - else if (remaining_children_to_start - && child_slot < ap_daemons_limit) { - /* we're still doing a 1-for-1 replacement of dead - * children with new children - */ -- make_child(ap_server_conf, child_slot, ps->bucket); -+ make_child(ap_server_conf, child_slot, -+ child_slot % num_buckets); - --remaining_children_to_start; - } - } diff --git a/debian/patches/CVE-2019-0215.patch b/debian/patches/CVE-2019-0215.patch deleted file mode 100644 index 6c0461e..0000000 --- a/debian/patches/CVE-2019-0215.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 84edf5f49db23ced03259812bbf9426685f7d82a Mon Sep 17 00:00:00 2001 -From: Joe Orton <jorton@apache.org> -Date: Wed, 20 Mar 2019 15:45:16 +0000 -Subject: [PATCH] Merge r1855849 from trunk: - -* modules/ssl/ssl_engine_kernel.c (ssl_hook_Access_modern): Correctly - restore SSL verify state after PHA failure in TLSv1.3. - -Submitted by: Michael Kaufmann <mail michael-kaufmann.ch> -Reviewed by: jorton, covener, jim - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855917 13f79535-47bb-0310-9956-ffa450edef68 ---- - CHANGES | 3 +++ - modules/ssl/ssl_engine_kernel.c | 2 ++ - 2 files changed, 5 insertions(+) - -#diff --git a/CHANGES b/CHANGES -#index 6b03eadfa07..6f20d688ece 100644 -#--- a/CHANGES -#+++ b/CHANGES -#@@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- -# Changes with Apache 2.4.39 -# -#+ *) mod_ssl: Correctly restore SSL verify state after TLSv1.3 PHA failure. -#+ [Michael Kaufmann <mail michael-kaufmann.ch>] -#+ -# *) mod_log_config: Support %{c}h for conn-hostname, %h for useragent_host -# PR 55348 -# -Index: apache2-2.4.38/modules/ssl/ssl_engine_kernel.c -=================================================================== ---- apache2-2.4.38.orig/modules/ssl/ssl_engine_kernel.c 2019-04-03 14:31:14.279214679 -0400 -+++ apache2-2.4.38/modules/ssl/ssl_engine_kernel.c 2019-04-03 14:31:14.279214679 -0400 -@@ -1154,6 +1154,7 @@ static int ssl_hook_Access_modern(reques - ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server); - apr_table_setn(r->notes, "error-notes", - "Reason: Cannot perform Post-Handshake Authentication.<br />"); -+ SSL_set_verify(ssl, vmode_inplace, NULL); - return HTTP_FORBIDDEN; - } - -@@ -1175,6 +1176,7 @@ static int ssl_hook_Access_modern(reques - * Finally check for acceptable renegotiation results - */ - if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) { -+ SSL_set_verify(ssl, vmode_inplace, NULL); - return rc; - } - } diff --git a/debian/patches/CVE-2019-0217.patch b/debian/patches/CVE-2019-0217.patch deleted file mode 100644 index e8f1090..0000000 --- a/debian/patches/CVE-2019-0217.patch +++ /dev/null @@ -1,147 +0,0 @@ -From 44b3ddc560c490c60600998fa2bf59b142d08e05 Mon Sep 17 00:00:00 2001 -From: Joe Orton <jorton@apache.org> -Date: Tue, 12 Mar 2019 09:24:26 +0000 -Subject: [PATCH] Merge r1853190 from trunk: - -Fix a race condition. Authentication with valid credentials could be -refused in case of concurrent accesses from different users. - -PR: 63124 -Submitted by: Simon Kappel <simon.kappel axis.com> -Reviewed by: jailletc36, icing, jorton - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855298 13f79535-47bb-0310-9956-ffa450edef68 ---- - CHANGES | 4 ++++ - modules/aaa/mod_auth_digest.c | 26 ++++++++++++-------------- - 2 files changed, 16 insertions(+), 14 deletions(-) - -#diff --git a/CHANGES b/CHANGES -#index 08fc740db30..e79251389d5 100644 -#--- a/CHANGES -#+++ b/CHANGES -#@@ -1,6 +1,10 @@ -# -*- coding: utf-8 -*- -# Changes with Apache 2.4.39 -# -#+ *) mod_auth_digest: Fix a race condition. Authentication with valid -#+ credentials could be refused in case of concurrent accesses from -#+ different users. PR 63124. [Simon Kappel <simon.kappel axis.com>] -#+ -# *) mod_proxy_wstunnel: Fix websocket proxy over UDS. -# PR 62932 <pavel dcmsys.com> -# -diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c -index a67f06986f2..b76094114dd 100644 ---- a/modules/aaa/mod_auth_digest.c -+++ b/modules/aaa/mod_auth_digest.c -@@ -92,7 +92,6 @@ typedef struct digest_config_struct { - int check_nc; - const char *algorithm; - char *uri_list; -- const char *ha1; - } digest_config_rec; - - -@@ -153,6 +152,7 @@ typedef struct digest_header_struct { - apr_time_t nonce_time; - enum hdr_sts auth_hdr_sts; - int needed_auth; -+ const char *ha1; - client_entry *client; - } digest_header_rec; - -@@ -1304,7 +1304,7 @@ static int hook_note_digest_auth_failure(request_rec *r, const char *auth_type) - */ - - static authn_status get_hash(request_rec *r, const char *user, -- digest_config_rec *conf) -+ digest_config_rec *conf, const char **rethash) - { - authn_status auth_result; - char *password; -@@ -1356,7 +1356,7 @@ static authn_status get_hash(request_rec *r, const char *user, - } while (current_provider); - - if (auth_result == AUTH_USER_FOUND) { -- conf->ha1 = password; -+ *rethash = password; - } - - return auth_result; -@@ -1483,25 +1483,24 @@ static int check_nonce(request_rec *r, digest_header_rec *resp, - - /* RFC-2069 */ - static const char *old_digest(const request_rec *r, -- const digest_header_rec *resp, const char *ha1) -+ const digest_header_rec *resp) - { - const char *ha2; - - ha2 = ap_md5(r->pool, (unsigned char *)apr_pstrcat(r->pool, resp->method, ":", - resp->uri, NULL)); - return ap_md5(r->pool, -- (unsigned char *)apr_pstrcat(r->pool, ha1, ":", resp->nonce, -- ":", ha2, NULL)); -+ (unsigned char *)apr_pstrcat(r->pool, resp->ha1, ":", -+ resp->nonce, ":", ha2, NULL)); - } - - /* RFC-2617 */ - static const char *new_digest(const request_rec *r, -- digest_header_rec *resp, -- const digest_config_rec *conf) -+ digest_header_rec *resp) - { - const char *ha1, *ha2, *a2; - -- ha1 = conf->ha1; -+ ha1 = resp->ha1; - - a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, NULL); - ha2 = ap_md5(r->pool, (const unsigned char *)a2); -@@ -1514,7 +1513,6 @@ static const char *new_digest(const request_rec *r, - NULL)); - } - -- - static void copy_uri_components(apr_uri_t *dst, - apr_uri_t *src, request_rec *r) { - if (src->scheme && src->scheme[0] != '\0') { -@@ -1759,7 +1757,7 @@ static int authenticate_digest_user(request_rec *r) - return HTTP_UNAUTHORIZED; - } - -- return_code = get_hash(r, r->user, conf); -+ return_code = get_hash(r, r->user, conf, &resp->ha1); - - if (return_code == AUTH_USER_NOT_FOUND) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01790) -@@ -1789,7 +1787,7 @@ static int authenticate_digest_user(request_rec *r) - - if (resp->message_qop == NULL) { - /* old (rfc-2069) style digest */ -- if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) { -+ if (strcmp(resp->digest, old_digest(r, resp))) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792) - "user %s: password mismatch: %s", r->user, - r->uri); -@@ -1819,7 +1817,7 @@ static int authenticate_digest_user(request_rec *r) - return HTTP_UNAUTHORIZED; - } - -- exp_digest = new_digest(r, resp, conf); -+ exp_digest = new_digest(r, resp); - if (!exp_digest) { - /* we failed to allocate a client struct */ - return HTTP_INTERNAL_SERVER_ERROR; -@@ -1903,7 +1901,7 @@ static int add_auth_info(request_rec *r) - - /* calculate rspauth attribute - */ -- ha1 = conf->ha1; -+ ha1 = resp->ha1; - - a2 = apr_pstrcat(r->pool, ":", resp->uri, NULL); - ha2 = ap_md5(r->pool, (const unsigned char *)a2); diff --git a/debian/patches/CVE-2019-0220-1.patch b/debian/patches/CVE-2019-0220-1.patch deleted file mode 100644 index 021c369..0000000 --- a/debian/patches/CVE-2019-0220-1.patch +++ /dev/null @@ -1,278 +0,0 @@ -From 9bc1917a27a2323e535aadb081e38172ae0e3fc2 Mon Sep 17 00:00:00 2001 -From: Stefan Eissing <icing@apache.org> -Date: Mon, 18 Mar 2019 08:49:59 +0000 -Subject: [PATCH] Merge of r1855705 from trunk: - -core: merge consecutive slashes in the path - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855737 13f79535-47bb-0310-9956-ffa450edef68 ---- - CHANGES | 4 ++++ - docs/manual/mod/core.xml | 26 ++++++++++++++++++++++++++ - include/ap_mmn.h | 4 +++- - include/http_core.h | 2 +- - include/httpd.h | 14 ++++++++++++-- - server/core.c | 13 +++++++++++++ - server/request.c | 25 +++++++++---------------- - server/util.c | 10 +++++++--- - 8 files changed, 75 insertions(+), 23 deletions(-) - -#diff --git a/CHANGES b/CHANGES -#index e3e8a98db24..9dd7045c232 100644 -#--- a/CHANGES -#+++ b/CHANGES -#@@ -1,6 +1,10 @@ -# -*- coding: utf-8 -*- -# Changes with Apache 2.4.39 -# -#+ *) core: new configuration option 'MergeSlashes on|off' that controls handling of -#+ multiple, consecutive slash ('/') characters in the path component of the request URL. -#+ [Eric Covener] -#+ -# *) mod_http2: when SSL renegotiation is inhibited and a 403 ErrorDocument is -# in play, the proper HTTP/2 stream reset did not trigger with H2_ERR_HTTP_1_1_REQUIRED. -# Fixed. [Michael Kaufmann] -#diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml -#index fc664116727..460b4367621 100644 -#--- a/docs/manual/mod/core.xml -#+++ b/docs/manual/mod/core.xml -#@@ -5138,4 +5138,30 @@ recognized methods to modules.</p> -# <seealso><directive module="mod_allowmethods">AllowMethods</directive></seealso> -# </directivesynopsis> -# -#+<directivesynopsis> -#+<name>MergeSlashes</name> -#+<description>Controls whether the server merges consecutive slashes in URLs. -#+</description> -#+<syntax>MergeSlashes ON|OFF</syntax> -#+<default>MergeSlashes ON</default> -#+<contextlist><context>server config</context><context>virtual host</context> -#+</contextlist> -#+<compatibility>Added in 2.5.1</compatibility> -#+ -#+<usage> -#+ <p>By default, the server merges (or collapses) multiple consecutive slash -#+ ('/') characters in the path component of the request URL.</p> -#+ -#+ <p>When mapping URL's to the filesystem, these multiple slashes are not -#+ significant. However, URL's handled other ways, such as by CGI or proxy, -#+ might prefer to retain the significance of multiple consecutive slashes. -#+ In these cases <directive>MergeSlashes</directive> can be set to -#+ <em>OFF</em> to retain the multiple consecutive slashes. In these -#+ configurations, regular expressions used in the configuration file that match -#+ the path component of the URL (<directive>LocationMatch</directive>, -#+ <directive>RewriteRule</directive>, ...) need to take into account multiple -#+ consecutive slashes.</p> -#+</usage> -#+</directivesynopsis> -#+ -# </modulesynopsis> -diff --git a/include/ap_mmn.h b/include/ap_mmn.h -index 2167baa0325..4739f7f64d3 100644 ---- a/include/ap_mmn.h -+++ b/include/ap_mmn.h -@@ -523,6 +523,8 @@ - * 20120211.82 (2.4.35-dev) Add optional function declaration for - * ap_proxy_balancer_get_best_worker to mod_proxy.h. - * 20120211.83 (2.4.35-dev) Add client64 field to worker_score struct -+ * 20120211.84 (2.4.35-dev) Add ap_no2slash_ex() and merge_slashes to -+ * core_server_conf. - * - */ - -@@ -531,7 +533,7 @@ - #ifndef MODULE_MAGIC_NUMBER_MAJOR - #define MODULE_MAGIC_NUMBER_MAJOR 20120211 - #endif --#define MODULE_MAGIC_NUMBER_MINOR 83 /* 0...n */ -+#define MODULE_MAGIC_NUMBER_MINOR 84 /* 0...n */ - - /** - * Determine if the server's current MODULE_MAGIC_NUMBER is at least a -diff --git a/include/http_core.h b/include/http_core.h -index 35df5dc9601..8e109882244 100644 ---- a/include/http_core.h -+++ b/include/http_core.h -@@ -740,7 +740,7 @@ typedef struct { - #define AP_HTTP_METHODS_LENIENT 1 - #define AP_HTTP_METHODS_REGISTERED 2 - char http_methods; -- -+ unsigned int merge_slashes; - } core_server_config; - - /* for AddOutputFiltersByType in core.c */ -diff --git a/include/httpd.h b/include/httpd.h -index 65392f83546..99f7f041aea 100644 ---- a/include/httpd.h -+++ b/include/httpd.h -@@ -1697,11 +1697,21 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes); - AP_DECLARE(int) ap_unescape_urlencoded(char *query); - - /** -- * Convert all double slashes to single slashes -- * @param name The string to convert -+ * Convert all double slashes to single slashes, except where significant -+ * to the filesystem on the current platform. -+ * @param name The string to convert, assumed to be a filesystem path - */ - AP_DECLARE(void) ap_no2slash(char *name); - -+/** -+ * Convert all double slashes to single slashes, except where significant -+ * to the filesystem on the current platform. -+ * @param name The string to convert -+ * @param is_fs_path if set to 0, the significance of any double-slashes is -+ * ignored. -+ */ -+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path); -+ - /** - * Remove all ./ and xx/../ substrings from a file name. Also remove - * any leading ../ or /../ substrings. -diff --git a/server/core.c b/server/core.c -index e2a91c7a0c6..eacb54fecec 100644 ---- a/server/core.c -+++ b/server/core.c -@@ -490,6 +490,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s) - - conf->protocols = apr_array_make(a, 5, sizeof(const char *)); - conf->protocols_honor_order = -1; -+ conf->merge_slashes = AP_CORE_CONFIG_UNSET; - - return (void *)conf; - } -@@ -555,6 +556,7 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv) - conf->protocols_honor_order = ((virt->protocols_honor_order < 0)? - base->protocols_honor_order : - virt->protocols_honor_order); -+ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt); - - return conf; - } -@@ -1863,6 +1865,13 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag) - return NULL; - } - -+static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag) -+{ -+ core_server_config *conf = -+ ap_get_core_module_config(cmd->server->module_config); -+ return ap_set_flag_slot(cmd, conf, flag); -+} -+ - static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) - { - core_dir_config *d = d_; -@@ -4562,6 +4571,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON - "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"), - AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF, - "Registers non-standard HTTP methods"), -+AP_INIT_FLAG("MergeSlashes", set_core_server_flag, -+ (void *)APR_OFFSETOF(core_server_config, merge_slashes), -+ RSRC_CONF, -+ "Controls whether consecutive slashes in the URI path are merged"), - { NULL } - }; - -diff --git a/server/request.c b/server/request.c -index dbe3e07f150..1ce8908824b 100644 ---- a/server/request.c -+++ b/server/request.c -@@ -167,6 +167,8 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r) - int file_req = (r->main && r->filename); - int access_status; - core_dir_config *d; -+ core_server_config *sconf = -+ ap_get_core_module_config(r->server->module_config); - - /* Ignore embedded %2F's in path for proxy requests */ - if (!r->proxyreq && r->parsed_uri.path) { -@@ -191,6 +193,10 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r) - } - - ap_getparents(r->uri); /* OK --- shrinking transformations... */ -+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) { -+ ap_no2slash(r->uri); -+ ap_no2slash(r->parsed_uri.path); -+ } - - /* All file subrequests are a huge pain... they cannot bubble through the - * next several steps. Only file subrequests are allowed an empty uri, -@@ -1411,20 +1417,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) - - cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r); - cached = (cache->cached != NULL); -- -- /* Location and LocationMatch differ on their behaviour w.r.t. multiple -- * slashes. Location matches multiple slashes with a single slash, -- * LocationMatch doesn't. An exception, for backwards brokenness is -- * absoluteURIs... in which case neither match multiple slashes. -- */ -- if (r->uri[0] != '/') { -- entry_uri = r->uri; -- } -- else { -- char *uri = apr_pstrdup(r->pool, r->uri); -- ap_no2slash(uri); -- entry_uri = uri; -- } -+ entry_uri = r->uri; - - /* If we have an cache->cached location that matches r->uri, - * and the vhost's list of locations hasn't changed, we can skip -@@ -1491,7 +1484,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) - pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t)); - } - -- if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) { -+ if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) { - continue; - } - -@@ -1501,7 +1494,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) - apr_table_setn(r->subprocess_env, - ((const char **)entry_core->refs->elts)[i], - apr_pstrndup(r->pool, -- r->uri + pmatch[i].rm_so, -+ entry_uri + pmatch[i].rm_so, - pmatch[i].rm_eo - pmatch[i].rm_so)); - } - } -diff --git a/server/util.c b/server/util.c -index fd7a0a14763..607c4850d86 100644 ---- a/server/util.c -+++ b/server/util.c -@@ -561,16 +561,16 @@ AP_DECLARE(void) ap_getparents(char *name) - name[l] = '\0'; - } - } -- --AP_DECLARE(void) ap_no2slash(char *name) -+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path) - { -+ - char *d, *s; - - s = d = name; - - #ifdef HAVE_UNC_PATHS - /* Check for UNC names. Leave leading two slashes. */ -- if (s[0] == '/' && s[1] == '/') -+ if (is_fs_path && s[0] == '/' && s[1] == '/') - *d++ = *s++; - #endif - -@@ -587,6 +587,10 @@ AP_DECLARE(void) ap_no2slash(char *name) - *d = '\0'; - } - -+AP_DECLARE(void) ap_no2slash(char *name) -+{ -+ ap_no2slash_ex(name, 1); -+} - - /* - * copy at most n leading directories of s into d diff --git a/debian/patches/CVE-2019-0220-2.patch b/debian/patches/CVE-2019-0220-2.patch deleted file mode 100644 index 0204259..0000000 --- a/debian/patches/CVE-2019-0220-2.patch +++ /dev/null @@ -1,50 +0,0 @@ -From c4ef468b25718a26f2b92cbea3ca093729b79331 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Mon, 18 Mar 2019 12:10:15 +0000 -Subject: [PATCH] merge 1855743,1855744 ^/httpd/httpd/trunk . - -r->parsed_uri.path safety in recent backport - -*) core: fix SEGFAULT in CONNECT with recent change - 2.4.x: svn merge -c 1855743,1855744 ^/httpd/httpd/trunk . - +1: rpluem, icing, covener - - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855751 13f79535-47bb-0310-9956-ffa450edef68 ---- - server/request.c | 4 +++- - server/util.c | 4 ++++ - 2 files changed, 7 insertions(+), 1 deletion(-) - -diff --git a/server/request.c b/server/request.c -index 1ce8908824b..d5c558afa30 100644 ---- a/server/request.c -+++ b/server/request.c -@@ -195,7 +195,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r) - ap_getparents(r->uri); /* OK --- shrinking transformations... */ - if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) { - ap_no2slash(r->uri); -- ap_no2slash(r->parsed_uri.path); -+ if (r->parsed_uri.path) { -+ ap_no2slash(r->parsed_uri.path); -+ } - } - - /* All file subrequests are a huge pain... they cannot bubble through the -diff --git a/server/util.c b/server/util.c -index 607c4850d86..f3b17f1581e 100644 ---- a/server/util.c -+++ b/server/util.c -@@ -566,6 +566,10 @@ AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path) - - char *d, *s; - -+ if (!name || !*name) { -+ return; -+ } -+ - s = d = name; - - #ifdef HAVE_UNC_PATHS diff --git a/debian/patches/CVE-2019-0220-3.patch b/debian/patches/CVE-2019-0220-3.patch deleted file mode 100644 index 7b3ff6f..0000000 --- a/debian/patches/CVE-2019-0220-3.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 3451fc2bf8708b0dc8cd6a7d0ac0fe5b6401befc Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Tue, 19 Mar 2019 18:01:21 +0000 -Subject: [PATCH] *) maintainer mode fix for util.c no2slash_ex trunk - patch: http://svn.apache.org/r1855755 2.4.x patch svn merge -c 1855755 - ^/httpd/httpd/trunk . +1: covener, rpluem, jim, ylavic - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855853 13f79535-47bb-0310-9956-ffa450edef68 ---- - STATUS | 6 ------ - server/util.c | 2 +- - 2 files changed, 1 insertion(+), 7 deletions(-) - -#diff --git a/STATUS b/STATUS -#index ffe5d22550c..1f8cb2f7884 100644 -#--- a/STATUS -#+++ b/STATUS -#@@ -126,12 +126,6 @@ RELEASE SHOWSTOPPERS: -# PATCHES ACCEPTED TO BACKPORT FROM TRUNK: -# [ start all new proposals below, under PATCHES PROPOSED. ] -# -#- *) maintainer mode fix for util.c no2slash_ex -#- trunk patch: http://svn.apache.org/r1855755 -#- 2.4.x patch svn merge -c 1855755 ^/httpd/httpd/trunk . -#- +1: covener, rpluem, jim, ylavic -#- -#- -# PATCHES PROPOSED TO BACKPORT FROM TRUNK: -# [ New proposals should be added at the end of the list ] -# -diff --git a/server/util.c b/server/util.c -index f3b17f1581e..e0c558cee2d 100644 ---- a/server/util.c -+++ b/server/util.c -@@ -566,7 +566,7 @@ AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path) - - char *d, *s; - -- if (!name || !*name) { -+ if (!*name) { - return; - } - diff --git a/debian/patches/CVE-2019-10092.patch b/debian/patches/CVE-2019-10092.patch deleted file mode 100644 index eb3352c..0000000 --- a/debian/patches/CVE-2019-10092.patch +++ /dev/null @@ -1,193 +0,0 @@ -Description: Fix for CVE-2019-10092 -Author: Stefan Eissing -Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1864191 -Bug: https://security-tracker.debian.org/tracker/CVE-2019-10092 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2019-10-11 -[Salvatore Bonaccorso: Add additional change from https://svn.apache.org/r1864699 -to add missing APLOGNO's in mod_proxy.c and mod_proxy_ftp.c] ---- a/modules/http/http_protocol.c -+++ b/modules/http/http_protocol.c -@@ -1132,13 +1132,10 @@ - "\">here</a>.</p>\n", - NULL)); - case HTTP_USE_PROXY: -- return(apr_pstrcat(p, -- "<p>This resource is only accessible " -- "through the proxy\n", -- ap_escape_html(r->pool, location), -- "<br />\nYou will need to configure " -- "your client to use that proxy.</p>\n", -- NULL)); -+ return("<p>This resource is only accessible " -+ "through the proxy\n" -+ "<br />\nYou will need to configure " -+ "your client to use that proxy.</p>\n"); - case HTTP_PROXY_AUTHENTICATION_REQUIRED: - case HTTP_UNAUTHORIZED: - return("<p>This server could not verify that you\n" -@@ -1154,34 +1151,20 @@ - "error-notes", - "</p>\n")); - case HTTP_FORBIDDEN: -- s1 = apr_pstrcat(p, -- "<p>You don't have permission to access ", -- ap_escape_html(r->pool, r->uri), -- "\non this server.<br />\n", -- NULL); -- return(add_optional_notes(r, s1, "error-notes", "</p>\n")); -+ return(add_optional_notes(r, "<p>You don't have permission to access this resource.", "error-notes", "</p>\n")); - case HTTP_NOT_FOUND: -- return(apr_pstrcat(p, -- "<p>The requested URL ", -- ap_escape_html(r->pool, r->uri), -- " was not found on this server.</p>\n", -- NULL)); -+ return("<p>The requested URL was not found on this server.</p>\n"); - case HTTP_METHOD_NOT_ALLOWED: - return(apr_pstrcat(p, - "<p>The requested method ", - ap_escape_html(r->pool, r->method), -- " is not allowed for the URL ", -- ap_escape_html(r->pool, r->uri), -- ".</p>\n", -+ " is not allowed for this URL.</p>\n", - NULL)); - case HTTP_NOT_ACCEPTABLE: -- s1 = apr_pstrcat(p, -- "<p>An appropriate representation of the " -- "requested resource ", -- ap_escape_html(r->pool, r->uri), -- " could not be found on this server.</p>\n", -- NULL); -- return(add_optional_notes(r, s1, "variant-list", "")); -+ return(add_optional_notes(r, -+ "<p>An appropriate representation of the requested resource " -+ "could not be found on this server.</p>\n", -+ "variant-list", "")); - case HTTP_MULTIPLE_CHOICES: - return(add_optional_notes(r, "", "variant-list", "")); - case HTTP_LENGTH_REQUIRED: -@@ -1192,18 +1175,13 @@ - NULL); - return(add_optional_notes(r, s1, "error-notes", "</p>\n")); - case HTTP_PRECONDITION_FAILED: -- return(apr_pstrcat(p, -- "<p>The precondition on the request " -- "for the URL ", -- ap_escape_html(r->pool, r->uri), -- " evaluated to false.</p>\n", -- NULL)); -+ return("<p>The precondition on the request " -+ "for this URL evaluated to false.</p>\n"); - case HTTP_NOT_IMPLEMENTED: - s1 = apr_pstrcat(p, - "<p>", -- ap_escape_html(r->pool, r->method), " to ", -- ap_escape_html(r->pool, r->uri), -- " not supported.<br />\n", -+ ap_escape_html(r->pool, r->method), " ", -+ " not supported for current URL.<br />\n", - NULL); - return(add_optional_notes(r, s1, "error-notes", "</p>\n")); - case HTTP_BAD_GATEWAY: -@@ -1211,29 +1189,19 @@ - "response from an upstream server.<br />" CRLF; - return(add_optional_notes(r, s1, "error-notes", "</p>\n")); - case HTTP_VARIANT_ALSO_VARIES: -- return(apr_pstrcat(p, -- "<p>A variant for the requested " -- "resource\n<pre>\n", -- ap_escape_html(r->pool, r->uri), -- "\n</pre>\nis itself a negotiable resource. " -- "This indicates a configuration error.</p>\n", -- NULL)); -+ return("<p>A variant for the requested " -+ "resource\n<pre>\n" -+ "\n</pre>\nis itself a negotiable resource. " -+ "This indicates a configuration error.</p>\n"); - case HTTP_REQUEST_TIME_OUT: - return("<p>Server timeout waiting for the HTTP request from the client.</p>\n"); - case HTTP_GONE: -- return(apr_pstrcat(p, -- "<p>The requested resource<br />", -- ap_escape_html(r->pool, r->uri), -- "<br />\nis no longer available on this server " -- "and there is no forwarding address.\n" -- "Please remove all references to this " -- "resource.</p>\n", -- NULL)); -+ return("<p>The requested resource is no longer available on this server" -+ " and there is no forwarding address.\n" -+ "Please remove all references to this resource.</p>\n"); - case HTTP_REQUEST_ENTITY_TOO_LARGE: - return(apr_pstrcat(p, -- "The requested resource<br />", -- ap_escape_html(r->pool, r->uri), "<br />\n", -- "does not allow request data with ", -+ "The requested resource does not allow request data with ", - ap_escape_html(r->pool, r->method), - " requests, or the amount of data provided in\n" - "the request exceeds the capacity limit.\n", -@@ -1317,11 +1285,9 @@ - "the Server Name Indication (SNI) in use for this\n" - "connection.</p>\n"); - case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: -- s1 = apr_pstrcat(p, -- "<p>Access to ", ap_escape_html(r->pool, r->uri), -- "\nhas been denied for legal reasons.<br />\n", -- NULL); -- return(add_optional_notes(r, s1, "error-notes", "</p>\n")); -+ return(add_optional_notes(r, -+ "<p>Access to this URL has been denied for legal reasons.<br />\n", -+ "error-notes", "</p>\n")); - default: /* HTTP_INTERNAL_SERVER_ERROR */ - /* - * This comparison to expose error-notes could be modified to ---- a/modules/proxy/mod_proxy.c -+++ b/modules/proxy/mod_proxy.c -@@ -1049,9 +1049,10 @@ - char *end; - maxfwd = apr_strtoi64(str, &end, 10); - if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) { -- return ap_proxyerror(r, HTTP_BAD_REQUEST, -- apr_psprintf(r->pool, -- "Max-Forwards value '%s' could not be parsed", str)); -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10188) -+ "Max-Forwards value '%s' could not be parsed", str); -+ return ap_proxyerror(r, HTTP_BAD_REQUEST, -+ "Max-Forwards request header could not be parsed"); - } - else if (maxfwd == 0) { - switch (r->method_number) { ---- a/modules/proxy/mod_proxy_ftp.c -+++ b/modules/proxy/mod_proxy_ftp.c -@@ -1024,8 +1024,9 @@ - /* We break the URL into host, port, path-search */ - if (r->parsed_uri.hostname == NULL) { - if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) { -- return ap_proxyerror(r, HTTP_BAD_REQUEST, -- apr_psprintf(p, "URI cannot be parsed: %s", url)); -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10189) -+ "URI cannot be parsed: %s", url); -+ return ap_proxyerror(r, HTTP_BAD_REQUEST, "URI cannot be parsed"); - } - connectname = uri.hostname; - connectport = uri.port; ---- a/modules/proxy/proxy_util.c -+++ b/modules/proxy/proxy_util.c -@@ -368,12 +368,9 @@ - - PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) - { -- const char *uri = ap_escape_html(r->pool, r->uri); - apr_table_setn(r->notes, "error-notes", - apr_pstrcat(r->pool, -- "The proxy server could not handle the request <em><a href=\"", -- uri, "\">", ap_escape_html(r->pool, r->method), " ", uri, -- "</a></em>.<p>\n" -+ "The proxy server could not handle the request<p>" - "Reason: <strong>", ap_escape_html(r->pool, message), - "</strong></p>", - NULL)); diff --git a/debian/patches/CVE-2019-10097.patch b/debian/patches/CVE-2019-10097.patch deleted file mode 100644 index 0be05f5..0000000 --- a/debian/patches/CVE-2019-10097.patch +++ /dev/null @@ -1,72 +0,0 @@ -Description: Fix for CVE-2019-10097 -Author: jorton -Origin: upstream, https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1864613 -Bug: https://security-tracker.debian.org/tracker/CVE-2019-10097 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2019-08-17 - ---- a/modules/metadata/mod_remoteip.c -+++ b/modules/metadata/mod_remoteip.c -@@ -987,15 +987,13 @@ - return HDR_ERROR; - #endif - default: -- /* unsupported protocol, keep local connection address */ -- return HDR_DONE; -+ /* unsupported protocol */ -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10183) -+ "RemoteIPProxyProtocol: unsupported protocol %.2hx", -+ (unsigned short)hdr->v2.fam); -+ return HDR_ERROR; - } - break; /* we got a sockaddr now */ -- -- case 0x00: /* LOCAL command */ -- /* keep local connection address for LOCAL */ -- return HDR_DONE; -- - default: - /* not a supported command */ - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03507) -@@ -1087,11 +1085,24 @@ - /* try to read a header's worth of data */ - while (!ctx->done) { - if (APR_BRIGADE_EMPTY(ctx->bb)) { -- ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, -- ctx->need - ctx->rcvd); -+ apr_off_t got, want = ctx->need - ctx->rcvd; -+ -+ ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, want); - if (ret != APR_SUCCESS) { -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10184) -+ "failed reading input"); - return ret; - } -+ -+ ret = apr_brigade_length(ctx->bb, 1, &got); -+ if (ret || got > want) { -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10185) -+ "RemoteIPProxyProtocol header too long, " -+ "got %" APR_OFF_T_FMT " expected %" APR_OFF_T_FMT, -+ got, want); -+ f->c->aborted = 1; -+ return APR_ECONNABORTED; -+ } - } - if (APR_BRIGADE_EMPTY(ctx->bb)) { - return block == APR_NONBLOCK_READ ? APR_SUCCESS : APR_EOF; -@@ -1139,6 +1150,13 @@ - if (ctx->rcvd >= MIN_V2_HDR_LEN) { - ctx->need = MIN_V2_HDR_LEN + - remoteip_get_v2_len((proxy_header *) ctx->header); -+ if (ctx->need > sizeof(proxy_v2)) { -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(10186) -+ "RemoteIPProxyProtocol protocol header length too long"); -+ f->c->aborted = 1; -+ apr_brigade_destroy(ctx->bb); -+ return APR_ECONNABORTED; -+ } - } - if (ctx->rcvd >= ctx->need) { - psts = remoteip_process_v2_header(f->c, conn_conf, diff --git a/debian/patches/CVE-2019-10098.patch b/debian/patches/CVE-2019-10098.patch deleted file mode 100644 index b2c66b2..0000000 --- a/debian/patches/CVE-2019-10098.patch +++ /dev/null @@ -1,20 +0,0 @@ -Description: patch to set PCRE_DOTALL by default -Author: ylavic -Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1864192 -Bug: https://security-tracker.debian.org/tracker/CVE-2019-10098 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2019-08-18 - ---- a/server/util_pcre.c -+++ b/server/util_pcre.c -@@ -120,7 +120,8 @@ - * Compile a regular expression * - *************************************************/ - --static int default_cflags = AP_REG_DOLLAR_ENDONLY; -+static int default_cflags = AP_REG_DOTALL | -+ AP_REG_DOLLAR_ENDONLY; - - AP_DECLARE(int) ap_regcomp_get_default_cflags(void) - { diff --git a/debian/patches/CVE-2020-11984.patch b/debian/patches/CVE-2020-11984.patch deleted file mode 100644 index 409f958..0000000 --- a/debian/patches/CVE-2020-11984.patch +++ /dev/null @@ -1,45 +0,0 @@ -Description: fix error out on HTTP header larger than 16K - The uwsgi protocol does not let us serialize more than 16K of HTTP header, - so fail early with 500 if it happens. -Author: ylavic -Origin: upstream, https://github.com/apache/httpd/commit/0c543e3f -Bug: https://security-tracker.debian.org/tracker/CVE-2020-11984 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2020-08-25 - ---- a/modules/proxy/mod_proxy_uwsgi.c -+++ b/modules/proxy/mod_proxy_uwsgi.c -@@ -136,7 +136,7 @@ - int j; - - apr_size_t headerlen = 4; -- apr_uint16_t pktsize, keylen, vallen; -+ apr_size_t pktsize, keylen, vallen; - const char *script_name; - const char *path_info; - const char *auth; -@@ -177,6 +177,14 @@ - for (j = 0; j < env_table->nelts; ++j) { - headerlen += 2 + strlen(env[j].key) + 2 + strlen(env[j].val); - } -+ pktsize = headerlen - 4; -+ if (pktsize > APR_UINT16_MAX) { -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259) -+ "can't send headers to %s:%u: packet size too " -+ "large (%" APR_SIZE_T_FMT ")", -+ conn->hostname, conn->port, pktsize); -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } - - ptr = buf = apr_palloc(r->pool, headerlen); - -@@ -196,8 +204,6 @@ - ptr += vallen; - } - -- pktsize = headerlen - 4; -- - buf[0] = 0; - buf[1] = (apr_byte_t) (pktsize & 0xff); - buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff); diff --git a/debian/patches/CVE-2020-1927.patch b/debian/patches/CVE-2020-1927.patch deleted file mode 100644 index cbdd84f..0000000 --- a/debian/patches/CVE-2020-1927.patch +++ /dev/null @@ -1,92 +0,0 @@ -Description: fix for CVE-2020-1927 -Author: covener -Origin: upstream, https://svn.apache.org/r1873905 - https://svn.apache.org/r1874191 -Bug: https://security-tracker.debian.org/tracker/CVE-2020-1927 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2020-08-25 - ---- a/include/ap_regex.h -+++ b/include/ap_regex.h -@@ -84,7 +84,11 @@ - - #define AP_REG_DOLLAR_ENDONLY 0x200 /* '$' matches at end of subject string only */ - --#define AP_REG_MATCH "MATCH_" /** suggested prefix for ap_regname */ -+#define AP_REG_NO_DEFAULT 0x400 /**< Don't implicitely add AP_REG_DEFAULT options */ -+ -+#define AP_REG_MATCH "MATCH_" /**< suggested prefix for ap_regname */ -+ -+#define AP_REG_DEFAULT (AP_REG_DOTALL|AP_REG_DOLLAR_ENDONLY) - - /* Error values: */ - enum { ---- a/modules/filters/mod_substitute.c -+++ b/modules/filters/mod_substitute.c -@@ -667,8 +667,10 @@ - - /* 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"; - } ---- a/server/core.c -+++ b/server/core.c -@@ -4937,7 +4937,7 @@ - apr_pool_cleanup_register(pconf, NULL, reset_config_defines, - apr_pool_cleanup_null); - -- ap_regcomp_set_default_cflags(AP_REG_DOLLAR_ENDONLY); -+ ap_regcomp_set_default_cflags(AP_REG_DEFAULT); - - mpm_common_pre_config(pconf); - ---- a/server/util_pcre.c -+++ b/server/util_pcre.c -@@ -120,8 +120,7 @@ - * Compile a regular expression * - *************************************************/ - --static int default_cflags = AP_REG_DOTALL | -- AP_REG_DOLLAR_ENDONLY; -+static int default_cflags = AP_REG_DEFAULT; - - AP_DECLARE(int) ap_regcomp_get_default_cflags(void) - { -@@ -169,7 +168,9 @@ - int errcode = 0; - int options = PCRE_DUPNAMES; - -- cflags |= default_cflags; -+ if ((cflags & AP_REG_NO_DEFAULT) == 0) -+ cflags |= default_cflags; -+ - if ((cflags & AP_REG_ICASE) != 0) - options |= PCRE_CASELESS; - if ((cflags & AP_REG_NEWLINE) != 0) ---- a/server/util_regex.c -+++ b/server/util_regex.c -@@ -94,6 +94,7 @@ - } - - /* anything after the current delimiter is flags */ -+ ret->flags = ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY; - while (*++endp) { - switch (*endp) { - case 'i': ret->flags |= AP_REG_ICASE; break; -@@ -106,7 +107,7 @@ - default: break; /* we should probably be stricter here */ - } - } -- if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) { -+ if (ap_regcomp(&ret->rx, rxstr, AP_REG_NO_DEFAULT | ret->flags) == 0) { - apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup, - apr_pool_cleanup_null); - } diff --git a/debian/patches/CVE-2020-1934.patch b/debian/patches/CVE-2020-1934.patch deleted file mode 100644 index 295ab45..0000000 --- a/debian/patches/CVE-2020-1934.patch +++ /dev/null @@ -1,75 +0,0 @@ -Description: fix uninitialized memory when proxying to a malicious FTP server -Author: covener -Origin: upstream, https://svn.apache.org/viewvc?view=revision&revision=1873745 -Bug: https://security-tracker.debian.org/tracker/CVE-2020-1934 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2020-08-25 - ---- a/modules/proxy/mod_proxy_ftp.c -+++ b/modules/proxy/mod_proxy_ftp.c -@@ -218,7 +218,7 @@ - * (EBCDIC) machines either. - */ - static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, -- char *buff, apr_size_t bufflen, int *eos) -+ char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen) - { - apr_bucket *e; - apr_status_t rv; -@@ -230,6 +230,7 @@ - /* start with an empty string */ - buff[0] = 0; - *eos = 0; -+ *outlen = 0; - - /* loop through each brigade */ - while (!found) { -@@ -273,6 +274,7 @@ - if (len > 0) { - memcpy(pos, response, len); - pos += len; -+ *outlen += len; - } - } - apr_bucket_delete(e); -@@ -385,28 +387,35 @@ - char buff[5]; - char *mb = msgbuf, *me = &msgbuf[msglen]; - apr_status_t rv; -+ apr_size_t nread; -+ - int eos; - -- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { -+ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { - return -1; - } - /* - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233) - "<%s", response); - */ -+ if (nread < 4) { -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response); -+ *mb = '\0'; -+ return -1; -+ } - if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || -- !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) -+ !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) - status = 0; - else - status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; - - mb = apr_cpystrn(mb, response + 4, me - mb); - -- if (response[3] == '-') { -+ if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */ - memcpy(buff, response, 3); - buff[3] = ' '; - do { -- if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { -+ if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { - return -1; - } - mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); diff --git a/debian/patches/CVE-2020-35452.patch b/debian/patches/CVE-2020-35452.patch deleted file mode 100644 index 5204210..0000000 --- a/debian/patches/CVE-2020-35452.patch +++ /dev/null @@ -1,27 +0,0 @@ -Description: <short summary of the patch> -Author: Apache authors -Origin: upstream, https://github.com/apache/httpd/commit/3b6431e -Bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2020-35452 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-06-10 - ---- a/modules/aaa/mod_auth_digest.c -+++ b/modules/aaa/mod_auth_digest.c -@@ -1422,9 +1422,14 @@ - time_rec nonce_time; - char tmp, hash[NONCE_HASH_LEN+1]; - -- if (strlen(resp->nonce) != NONCE_LEN) { -+ /* Since the time part of the nonce is a base64 encoding of an -+ * apr_time_t (8 bytes), it should end with a '=', fail early otherwise. -+ */ -+ if (strlen(resp->nonce) != NONCE_LEN -+ || resp->nonce[NONCE_TIME_LEN - 1] != '=') { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01775) -- "invalid nonce %s received - length is not %d", -+ "invalid nonce '%s' received - length is not %d " -+ "or time encoding is incorrect", - resp->nonce, NONCE_LEN); - note_digest_auth_failure(r, conf, resp, 1); - return HTTP_UNAUTHORIZED; diff --git a/debian/patches/CVE-2021-26690.patch b/debian/patches/CVE-2021-26690.patch deleted file mode 100644 index 72c7457..0000000 --- a/debian/patches/CVE-2021-26690.patch +++ /dev/null @@ -1,20 +0,0 @@ -Description: <short summary of the patch> -Author: Apache authors -Origin: upstream, https://github.com/apache/httpd/commit/67bd9bfe -Bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2021-26690 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-06-10 - ---- a/modules/session/mod_session.c -+++ b/modules/session/mod_session.c -@@ -392,8 +392,8 @@ - char *plast = NULL; - const char *psep = "="; - char *key = apr_strtok(pair, psep, &plast); -- char *val = apr_strtok(NULL, psep, &plast); - if (key && *key) { -+ char *val = apr_strtok(NULL, sep, &plast); - if (!val || !*val) { - apr_table_unset(z->entries, key); - } diff --git a/debian/patches/CVE-2021-26691.patch b/debian/patches/CVE-2021-26691.patch deleted file mode 100644 index 7b96fad..0000000 --- a/debian/patches/CVE-2021-26691.patch +++ /dev/null @@ -1,18 +0,0 @@ -Description: mod_session: account for the '&' in identity_concat(). -Author: Apache authors -Origin: upstream, https://github.com/apache/httpd/commit/7e09dd71 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-06-10 - ---- a/modules/session/mod_session.c -+++ b/modules/session/mod_session.c -@@ -305,7 +305,7 @@ - static int identity_count(void *v, const char *key, const char *val) - { - int *count = v; -- *count += strlen(key) * 3 + strlen(val) * 3 + 1; -+ *count += strlen(key) * 3 + strlen(val) * 3 + 2; - return 1; - } - diff --git a/debian/patches/CVE-2021-30641.patch b/debian/patches/CVE-2021-30641.patch deleted file mode 100644 index 7486e1b..0000000 --- a/debian/patches/CVE-2021-30641.patch +++ /dev/null @@ -1,50 +0,0 @@ -Description: legacy default slash-matching behavior w/ 'MergeSlashes OFF' -Author: Apache authors -Origin: upstream, https://github.com/apache/httpd/commit/eb986059 -Bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2021-30641 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-06-10 - ---- a/server/request.c -+++ b/server/request.c -@@ -1419,7 +1419,20 @@ - - cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r); - cached = (cache->cached != NULL); -- entry_uri = r->uri; -+ -+ /* -+ * When merge_slashes is set to AP_CORE_CONFIG_OFF the slashes in r->uri -+ * have not been merged. But for Location walks we always go with merged -+ * slashes no matter what merge_slashes is set to. -+ */ -+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) { -+ entry_uri = r->uri; -+ } -+ else { -+ char *uri = apr_pstrdup(r->pool, r->uri); -+ ap_no2slash(uri); -+ entry_uri = uri; -+ } - - /* If we have an cache->cached location that matches r->uri, - * and the vhost's list of locations hasn't changed, we can skip -@@ -1486,7 +1499,7 @@ - pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t)); - } - -- if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) { -+ if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) { - continue; - } - -@@ -1496,7 +1509,7 @@ - apr_table_setn(r->subprocess_env, - ((const char **)entry_core->refs->elts)[i], - apr_pstrndup(r->pool, -- entry_uri + pmatch[i].rm_so, -+ r->uri + pmatch[i].rm_so, - pmatch[i].rm_eo - pmatch[i].rm_so)); - } - } diff --git a/debian/patches/CVE-2021-31618.patch b/debian/patches/CVE-2021-31618.patch deleted file mode 100644 index 12d59c8..0000000 --- a/debian/patches/CVE-2021-31618.patch +++ /dev/null @@ -1,20 +0,0 @@ -Description: fix NULL pointer dereference on specially crafted HTTP/2 request -Author: Upstream -Origin: upstream, http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c?r1=1889759&r2=1889758&pathrev=1889759 -Bug: https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2021-31618 -Bug-Debian: https://bugs.debian.org/989562 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-06-10 - ---- a/modules/http2/h2_stream.c -+++ b/modules/http2/h2_stream.c -@@ -638,7 +638,7 @@ - - static void set_error_response(h2_stream *stream, int http_status) - { -- if (!h2_stream_is_ready(stream)) { -+ if (!h2_stream_is_ready(stream) && stream->rtmp) { - conn_rec *c = stream->session->c; - apr_bucket *b; - h2_headers *response; diff --git a/debian/patches/CVE-2021-33193.patch b/debian/patches/CVE-2021-33193.patch deleted file mode 100644 index d2737b8..0000000 --- a/debian/patches/CVE-2021-33193.patch +++ /dev/null @@ -1,702 +0,0 @@ -Description: Fix for CVE-2021-33193: mod_proxy HTTP/2 validation bypass - A crafted method sent through HTTP/2 will bypass validation and be forwarded by - mod_proxy, which can lead to request splitting or cache poisoning. -Origin: other, https://git.centos.org/rpms/httpd/blob/c496dea5e0b6e82a9f503e973fc5d5ea93a94180/f/SOURCES/httpd-2.4.37-CVE-2021-33193.patch -Forwarded: not-needed -Applied-Upstream: 2.4.49, https://github.com/apache/httpd/commit/ecebcc035ccd8d0e2984fe41420d9e944f456b3c -Last-Update: 2023-02-24 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/include/http_core.h -+++ b/include/http_core.h -@@ -741,6 +741,7 @@ - #define AP_HTTP_METHODS_REGISTERED 2 - char http_methods; - unsigned int merge_slashes; -+ unsigned int strict_host_check; - } core_server_config; - - /* for AddOutputFiltersByType in core.c */ -@@ -769,6 +770,11 @@ - typedef struct core_output_filter_ctx core_output_filter_ctx_t; - typedef struct core_filter_ctx core_ctx_t; - -+struct core_filter_ctx { -+ apr_bucket_brigade *b; -+ apr_bucket_brigade *tmpbb; -+}; -+ - typedef struct core_net_rec { - /** Connection to the client */ - apr_socket_t *client_socket; ---- a/include/http_protocol.h -+++ b/include/http_protocol.h -@@ -54,6 +54,13 @@ - */ - - /** -+ * Read an empty request and set reasonable defaults. -+ * @param c The current connection -+ * @return The new request_rec -+ */ -+AP_DECLARE(request_rec *) ap_create_request(conn_rec *c); -+ -+/** - * Read a request and fill in the fields. - * @param c The current connection - * @return The new request_rec -@@ -61,6 +68,20 @@ - request_rec *ap_read_request(conn_rec *c); - - /** -+ * Parse and validate the request line. -+ * @param r The current request -+ * @return 1 on success, 0 on failure -+ */ -+AP_DECLARE(int) ap_parse_request_line(request_rec *r); -+ -+/** -+ * Validate the request header and select vhost. -+ * @param r The current request -+ * @return 1 on success, 0 on failure -+ */ -+AP_DECLARE(int) ap_check_request_header(request_rec *r); -+ -+/** - * Read the mime-encoded headers. - * @param r The current request - */ ---- a/include/http_vhost.h -+++ b/include/http_vhost.h -@@ -100,6 +100,19 @@ - AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r); - - /** -+ * Updates r->server with the best name-based virtual host match, within -+ * the chain of matching virtual hosts selected by ap_update_vhost_given_ip. -+ * @param r The current request -+ * @param require_match 1 to return an HTTP error if the requested hostname is -+ * not explicitly matched to a VirtualHost. -+ * @return return HTTP_OK unless require_match was specified and the requested -+ * hostname did not match any ServerName, ServerAlias, or VirtualHost -+ * address-spec. -+ */ -+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match); -+ -+ -+/** - * Match the host in the header with the hostname of the server for this - * request. - * @param r The current request ---- a/server/core.c -+++ b/server/core.c -@@ -494,6 +494,8 @@ - conf->protocols_honor_order = -1; - conf->merge_slashes = AP_CORE_CONFIG_UNSET; - -+ conf->strict_host_check= AP_CORE_CONFIG_UNSET; -+ - return (void *)conf; - } - -@@ -559,7 +561,13 @@ - base->protocols_honor_order : - virt->protocols_honor_order); - AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt); -- -+ -+ conf->strict_host_check = (virt->strict_host_check != AP_CORE_CONFIG_UNSET) -+ ? virt->strict_host_check -+ : base->strict_host_check; -+ -+ AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt); -+ - return conf; - } - -@@ -4518,7 +4526,10 @@ - AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO, - "Controls whether HTTP authorization headers, normally hidden, will " - "be passed to scripts"), -- -+AP_INIT_FLAG("StrictHostCheck", set_core_server_flag, -+ (void *)APR_OFFSETOF(core_server_config, strict_host_check), -+ RSRC_CONF, -+ "Controls whether a hostname match is required"), - AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower, - (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO, - "a mime type that overrides other configured type"), -@@ -5492,4 +5503,3 @@ - core_cmds, /* command apr_table_t */ - register_hooks /* register hooks */ - }; -- ---- a/server/core_filters.c -+++ b/server/core_filters.c -@@ -84,11 +84,6 @@ - apr_size_t bytes_written; - }; - --struct core_filter_ctx { -- apr_bucket_brigade *b; -- apr_bucket_brigade *tmpbb; --}; -- - - apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, - ap_input_mode_t mode, apr_read_type_e block, ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -611,8 +611,15 @@ - } - - r->args = r->parsed_uri.query; -- r->uri = r->parsed_uri.path ? r->parsed_uri.path -- : apr_pstrdup(r->pool, "/"); -+ if (r->parsed_uri.path) { -+ r->uri = r->parsed_uri.path; -+ } -+ else if (r->method_number == M_OPTIONS) { -+ r->uri = apr_pstrdup(r->pool, "*"); -+ } -+ else { -+ r->uri = apr_pstrdup(r->pool, "/"); -+ } - - #if defined(OS2) || defined(WIN32) - /* Handle path translations for OS/2 and plug security hole. -@@ -649,13 +656,6 @@ - - static int read_request_line(request_rec *r, apr_bucket_brigade *bb) - { -- enum { -- rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, -- rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext, -- rrl_badmethod09, rrl_reject09 -- } deferred_error = rrl_none; -- char *ll; -- char *uri; - apr_size_t len; - int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; - core_server_config *conf = ap_get_core_module_config(r->server->module_config); -@@ -720,6 +720,20 @@ - } - - r->request_time = apr_time_now(); -+ return 1; -+} -+ -+AP_DECLARE(int) ap_parse_request_line(request_rec *r) -+{ -+ core_server_config *conf = ap_get_core_module_config(r->server->module_config); -+ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); -+ enum { -+ rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, -+ rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext, -+ rrl_badmethod09, rrl_reject09 -+ } deferred_error = rrl_none; -+ apr_size_t len = 0; -+ char *uri, *ll; - - r->method = r->the_request; - -@@ -751,7 +765,6 @@ - if (deferred_error == rrl_none) - deferred_error = rrl_missinguri; - r->protocol = uri = ""; -- len = 0; - goto rrl_done; - } - else if (strict && ll[0] && apr_isspace(ll[1]) -@@ -782,7 +795,6 @@ - /* Verify URI terminated with a single SP, or mark as specific error */ - if (!ll) { - r->protocol = ""; -- len = 0; - goto rrl_done; - } - else if (strict && ll[0] && apr_isspace(ll[1]) -@@ -875,6 +887,14 @@ - r->header_only = 1; - - ap_parse_uri(r, uri); -+ if (r->status == HTTP_OK -+ && (r->parsed_uri.path != NULL) -+ && (r->parsed_uri.path[0] != '/') -+ && (r->method_number != M_OPTIONS -+ || strcmp(r->parsed_uri.path, "*") != 0)) { -+ /* Invalid request-target per RFC 7230 section 5.3 */ -+ r->status = HTTP_BAD_REQUEST; -+ } - - /* With the request understood, we can consider HTTP/0.9 specific errors */ - if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) { -@@ -982,6 +1002,79 @@ - return 0; - } - -+AP_DECLARE(int) ap_check_request_header(request_rec *r) -+{ -+ core_server_config *conf; -+ int strict_host_check; -+ const char *expect; -+ int access_status; -+ -+ conf = ap_get_core_module_config(r->server->module_config); -+ -+ /* update what we think the virtual host is based on the headers we've -+ * now read. may update status. -+ */ -+ strict_host_check = (conf->strict_host_check == AP_CORE_CONFIG_ON); -+ access_status = ap_update_vhost_from_headers_ex(r, strict_host_check); -+ if (strict_host_check && access_status != HTTP_OK) { -+ if (r->server == ap_server_conf) { -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10156) -+ "Requested hostname '%s' did not match any ServerName/ServerAlias " -+ "in the global server configuration ", r->hostname); -+ } -+ else { -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10157) -+ "Requested hostname '%s' did not match any ServerName/ServerAlias " -+ "in the matching virtual host (default vhost for " -+ "current connection is %s:%u)", -+ r->hostname, r->server->defn_name, r->server->defn_line_number); -+ } -+ r->status = access_status; -+ } -+ if (r->status != HTTP_OK) { -+ return 0; -+ } -+ -+ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1))) -+ || ((r->proto_num == HTTP_VERSION(1, 1)) -+ && !apr_table_get(r->headers_in, "Host"))) { -+ /* -+ * Client sent us an HTTP/1.1 or later request without telling us the -+ * hostname, either with a full URL or a Host: header. We therefore -+ * need to (as per the 1.1 spec) send an error. As a special case, -+ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain -+ * a Host: header, and the server MUST respond with 400 if it doesn't. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569) -+ "client sent HTTP/1.1 request without hostname " -+ "(see RFC2616 section 14.23): %s", r->uri); -+ r->status = HTTP_BAD_REQUEST; -+ return 0; -+ } -+ -+ if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) -+ && (expect[0] != '\0')) { -+ /* -+ * The Expect header field was added to HTTP/1.1 after RFC 2068 -+ * as a means to signal when a 100 response is desired and, -+ * unfortunately, to signal a poor man's mandatory extension that -+ * the server must understand or return 417 Expectation Failed. -+ */ -+ if (ap_cstr_casecmp(expect, "100-continue") == 0) { -+ r->expecting_100 = 1; -+ } -+ else { -+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570) -+ "client sent an unrecognized expectation value " -+ "of Expect: %s", expect); -+ r->status = HTTP_EXPECTATION_FAILED; -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - static int table_do_fn_check_lengths(void *r_, const char *key, - const char *value) - { -@@ -1265,16 +1358,10 @@ - apr_brigade_destroy(tmp_bb); - } - --request_rec *ap_read_request(conn_rec *conn) -+AP_DECLARE(request_rec *) ap_create_request(conn_rec *conn) - { - request_rec *r; - apr_pool_t *p; -- const char *expect; -- int access_status; -- apr_bucket_brigade *tmp_bb; -- apr_socket_t *csd; -- apr_interval_time_t cur_timeout; -- - - apr_pool_create(&p, conn->pool); - apr_pool_tag(p, "request"); -@@ -1313,6 +1400,7 @@ - r->read_body = REQUEST_NO_BODY; - - r->status = HTTP_OK; /* Until further notice */ -+ r->header_only = 0; - r->the_request = NULL; - - /* Begin by presuming any module can make its own path_info assumptions, -@@ -1323,12 +1411,33 @@ - r->useragent_addr = conn->client_addr; - r->useragent_ip = conn->client_ip; - -+ return r; -+} -+ -+/* Apply the server's timeout/config to the connection/request. */ -+static void apply_server_config(request_rec *r) -+{ -+ apr_socket_t *csd; -+ -+ csd = ap_get_conn_socket(r->connection); -+ apr_socket_timeout_set(csd, r->server->timeout); -+ -+ r->per_dir_config = r->server->lookup_defaults; -+} -+ -+request_rec *ap_read_request(conn_rec *conn) -+{ -+ int access_status; -+ apr_bucket_brigade *tmp_bb; -+ -+ request_rec *r = ap_create_request(conn); - tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); - - ap_run_pre_read_request(r, conn); - - /* Get the request... */ -- if (!read_request_line(r, tmp_bb)) { -+ if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) { -+ apr_brigade_cleanup(tmp_bb); - switch (r->status) { - case HTTP_REQUEST_URI_TOO_LARGE: - case HTTP_BAD_REQUEST: -@@ -1344,49 +1453,38 @@ - "request failed: malformed request line"); - } - access_status = r->status; -- r->status = HTTP_OK; -- ap_die(access_status, r); -- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); -- ap_run_log_transaction(r); -- r = NULL; -- apr_brigade_destroy(tmp_bb); -- goto traceout; -+ goto die_unusable_input; -+ - case HTTP_REQUEST_TIME_OUT: -+ /* Just log, no further action on this connection. */ - ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL); - if (!r->connection->keepalives) - ap_run_log_transaction(r); -- apr_brigade_destroy(tmp_bb); -- goto traceout; -- default: -- apr_brigade_destroy(tmp_bb); -- r = NULL; -- goto traceout; -+ break; - } -+ /* Not worth dying with. */ -+ conn->keepalive = AP_CONN_CLOSE; -+ apr_pool_destroy(r->pool); -+ goto ignore; - } -+ apr_brigade_cleanup(tmp_bb); - - /* We may have been in keep_alive_timeout mode, so toggle back - * to the normal timeout mode as we fetch the header lines, - * as necessary. - */ -- csd = ap_get_conn_socket(conn); -- apr_socket_timeout_get(csd, &cur_timeout); -- if (cur_timeout != conn->base_server->timeout) { -- apr_socket_timeout_set(csd, conn->base_server->timeout); -- cur_timeout = conn->base_server->timeout; -- } -+ apply_server_config(r); - - if (!r->assbackwards) { - const char *tenc; - - ap_get_mime_headers_core(r, tmp_bb); -+ apr_brigade_cleanup(tmp_bb); - if (r->status != HTTP_OK) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567) - "request failed: error reading the headers"); -- ap_send_error_response(r, 0); -- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); -- ap_run_log_transaction(r); -- apr_brigade_destroy(tmp_bb); -- goto traceout; -+ access_status = r->status; -+ goto die_unusable_input; - } - - tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); -@@ -1402,13 +1500,8 @@ - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539) - "client sent unknown Transfer-Encoding " - "(%s): %s", tenc, r->uri); -- r->status = HTTP_BAD_REQUEST; -- conn->keepalive = AP_CONN_CLOSE; -- ap_send_error_response(r, 0); -- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); -- ap_run_log_transaction(r); -- apr_brigade_destroy(tmp_bb); -- goto traceout; -+ access_status = HTTP_BAD_REQUEST; -+ goto die_unusable_input; - } - - /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23 -@@ -1421,88 +1514,81 @@ - } - } - -- apr_brigade_destroy(tmp_bb); -- -- /* update what we think the virtual host is based on the headers we've -- * now read. may update status. -- */ -- ap_update_vhost_from_headers(r); -- access_status = r->status; -- -- /* Toggle to the Host:-based vhost's timeout mode to fetch the -- * request body and send the response body, if needed. -- */ -- if (cur_timeout != r->server->timeout) { -- apr_socket_timeout_set(csd, r->server->timeout); -- cur_timeout = r->server->timeout; -- } -- -- /* we may have switched to another server */ -- r->per_dir_config = r->server->lookup_defaults; -- -- if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1))) -- || ((r->proto_num == HTTP_VERSION(1, 1)) -- && !apr_table_get(r->headers_in, "Host"))) { -- /* -- * Client sent us an HTTP/1.1 or later request without telling us the -- * hostname, either with a full URL or a Host: header. We therefore -- * need to (as per the 1.1 spec) send an error. As a special case, -- * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain -- * a Host: header, and the server MUST respond with 400 if it doesn't. -- */ -- access_status = HTTP_BAD_REQUEST; -- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569) -- "client sent HTTP/1.1 request without hostname " -- "(see RFC2616 section 14.23): %s", r->uri); -- } -- - /* - * Add the HTTP_IN filter here to ensure that ap_discard_request_body - * called by ap_die and by ap_send_error_response works correctly on - * status codes that do not cause the connection to be dropped and - * in situations where the connection should be kept alive. - */ -- - ap_add_input_filter_handle(ap_http_input_filter_handle, - NULL, r, r->connection); - -- if (access_status != HTTP_OK -- || (access_status = ap_post_read_request(r))) { -- ap_die(access_status, r); -- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); -- ap_run_log_transaction(r); -- r = NULL; -- goto traceout; -+ /* Validate Host/Expect headers and select vhost. */ -+ if (!ap_check_request_header(r)) { -+ /* we may have switched to another server still */ -+ apply_server_config(r); -+ access_status = r->status; -+ goto die_before_hooks; - } - -- if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) -- && (expect[0] != '\0')) { -- /* -- * The Expect header field was added to HTTP/1.1 after RFC 2068 -- * as a means to signal when a 100 response is desired and, -- * unfortunately, to signal a poor man's mandatory extension that -- * the server must understand or return 417 Expectation Failed. -- */ -- if (strcasecmp(expect, "100-continue") == 0) { -- r->expecting_100 = 1; -- } -- else { -- r->status = HTTP_EXPECTATION_FAILED; -- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570) -- "client sent an unrecognized expectation value of " -- "Expect: %s", expect); -- ap_send_error_response(r, 0); -- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); -- ap_run_log_transaction(r); -- goto traceout; -- } -+ /* we may have switched to another server */ -+ apply_server_config(r); -+ -+ if ((access_status = ap_run_post_read_request(r))) { -+ goto die; - } - -- AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status); -+ AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, -+ (char *)r->uri, (char *)r->server->defn_name, -+ r->status); -+ - return r; -- traceout: -+ -+ /* Everything falls through on failure */ -+ -+die_unusable_input: -+ /* Input filters are in an undeterminate state, cleanup (including -+ * CORE_IN's socket) such that any further attempt to read is EOF. -+ */ -+ { -+ ap_filter_t *f = conn->input_filters; -+ while (f) { -+ if (f->frec == ap_core_input_filter_handle) { -+ core_net_rec *net = f->ctx; -+ apr_brigade_cleanup(net->in_ctx->b); -+ break; -+ } -+ ap_remove_input_filter(f); -+ f = f->next; -+ } -+ conn->input_filters = r->input_filters = f; -+ conn->keepalive = AP_CONN_CLOSE; -+ } -+ -+die_before_hooks: -+ /* First call to ap_die() (non recursive) */ -+ r->status = HTTP_OK; -+ -+die: -+ ap_die(access_status, r); -+ -+ /* ap_die() sent the response through the output filters, we must now -+ * end the request with an EOR bucket for stream/pipeline accounting. -+ */ -+ { -+ apr_bucket_brigade *eor_bb; -+ eor_bb = apr_brigade_create(conn->pool, conn->bucket_alloc); -+ APR_BRIGADE_INSERT_TAIL(eor_bb, -+ ap_bucket_eor_create(conn->bucket_alloc, r)); -+ ap_pass_brigade(conn->output_filters, eor_bb); -+ apr_brigade_cleanup(eor_bb); -+ } -+ -+ignore: -+ r = NULL; -+ - AP_READ_REQUEST_FAILURE((uintptr_t)r); -- return r; -+ return NULL; - } - - AP_DECLARE(int) ap_post_read_request(request_rec *r) ---- a/server/vhost.c -+++ b/server/vhost.c -@@ -34,6 +34,7 @@ - #include "http_vhost.h" - #include "http_protocol.h" - #include "http_core.h" -+#include "http_main.h" - - #if APR_HAVE_ARPA_INET_H - #include <arpa/inet.h> -@@ -973,7 +974,13 @@ - } - - --static void check_hostalias(request_rec *r) -+/* -+ * Updates r->server from ServerName/ServerAlias. Per the interaction -+ * of ip and name-based vhosts, it only looks in the best match from the -+ * connection-level ip-based matching. -+ * Returns HTTP_BAD_REQUEST if there was no match. -+ */ -+static int update_server_from_aliases(request_rec *r) - { - /* - * Even if the request has a Host: header containing a port we ignore -@@ -1050,11 +1057,18 @@ - goto found; - } - -- return; -+ if (!r->connection->vhost_lookup_data) { -+ if (matches_aliases(r->server, host)) { -+ s = r->server; -+ goto found; -+ } -+ } -+ return HTTP_BAD_REQUEST; - - found: - /* s is the first matching server, we're done */ - r->server = s; -+ return HTTP_OK; - } - - -@@ -1071,7 +1085,7 @@ - * This is in conjunction with the ServerPath code in http_core, so we - * get the right host attached to a non- Host-sending request. - * -- * See the comment in check_hostalias about how each vhost can be -+ * See the comment in update_server_from_aliases about how each vhost can be - * listed multiple times. - */ - -@@ -1135,10 +1149,16 @@ - - AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r) - { -+ ap_update_vhost_from_headers_ex(r, 0); -+} -+ -+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match) -+{ - core_server_config *conf = ap_get_core_module_config(r->server->module_config); - const char *host_header = apr_table_get(r->headers_in, "Host"); - int is_v6literal = 0; - int have_hostname_from_url = 0; -+ int rc = HTTP_OK; - - if (r->hostname) { - /* -@@ -1151,8 +1171,8 @@ - else if (host_header != NULL) { - is_v6literal = fix_hostname(r, host_header, conf->http_conformance); - } -- if (r->status != HTTP_OK) -- return; -+ if (!require_match && r->status != HTTP_OK) -+ return HTTP_OK; - - if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) { - /* -@@ -1173,10 +1193,16 @@ - /* check if we tucked away a name_chain */ - if (r->connection->vhost_lookup_data) { - if (r->hostname) -- check_hostalias(r); -+ rc = update_server_from_aliases(r); - else - check_serverpath(r); - } -+ else if (require_match && r->hostname) { -+ /* check the base server config */ -+ rc = update_server_from_aliases(r); -+ } -+ -+ return rc; - } - - /** diff --git a/debian/patches/CVE-2021-34798.patch b/debian/patches/CVE-2021-34798.patch deleted file mode 100644 index bd6261a..0000000 --- a/debian/patches/CVE-2021-34798.patch +++ /dev/null @@ -1,40 +0,0 @@ -Description: Initialize the request fields on read failure to avoid NULLs -Origin: upstream, https://github.com/apache/httpd/commit/74c097f0, - https://github.com/apache/httpd/commit/6945bb2 -Bug: https://security-tracker.debian.org/tracker/CVE-2021-34798 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-09-21 - ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -643,6 +643,8 @@ - return end - field; - } - -+static const char m_invalid_str[] = "-"; -+ - static int read_request_line(request_rec *r, apr_bucket_brigade *bb) - { - enum { -@@ -685,6 +687,11 @@ - if (rv != APR_SUCCESS) { - r->request_time = apr_time_now(); - -+ /* Fall through with an invalid (non NULL) request */ -+ r->method = m_invalid_str; -+ r->method_number = M_INVALID; -+ r->uri = r->unparsed_uri = apr_pstrdup(r->pool, "-"); -+ - /* ap_rgetline returns APR_ENOSPC if it fills up the - * buffer before finding the end-of-line. This is only going to - * happen if it exceeds the configured limit for a request-line. -@@ -1330,7 +1337,7 @@ - "request failed: client's request-line exceeds LimitRequestLine (longer than %d)", - r->server->limit_req_line); - } -- else if (r->method == NULL) { -+ else if (r->method == m_invalid_str) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566) - "request failed: malformed request line"); - } diff --git a/debian/patches/CVE-2021-36160-2.patch b/debian/patches/CVE-2021-36160-2.patch deleted file mode 100644 index cad5774..0000000 --- a/debian/patches/CVE-2021-36160-2.patch +++ /dev/null @@ -1,32 +0,0 @@ -Description: mod_proxy_uwsgi: Remove duplicate slashes at the beginning of PATH_INFO. - Relaxes the behaviour introduced by the CVE-2021-36160 fix -Author: Stefan Eissing <icing@apache.org> -Origin: upstream, https://github.com/apache/httpd/commit/8966e290a -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-12-21 - ---- a/modules/proxy/mod_proxy_uwsgi.c -+++ b/modules/proxy/mod_proxy_uwsgi.c -@@ -467,11 +467,20 @@ - - /* ADD PATH_INFO (unescaped) */ - u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/'); -- if (!u_path_info || ap_unescape_url(u_path_info) != OK) { -+ if (!u_path_info) { -+ u_path_info = apr_pstrdup(r->pool, "/"); -+ } -+ else if (ap_unescape_url(u_path_info) != OK) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100) - "unable to decode uwsgi uri: %s", url); - return HTTP_INTERNAL_SERVER_ERROR; - } -+ else { -+ /* Remove duplicate slashes at the beginning of PATH_INFO */ -+ while (u_path_info[1] == '/') { -+ u_path_info++; -+ } -+ } - apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info); - - diff --git a/debian/patches/CVE-2021-36160.patch b/debian/patches/CVE-2021-36160.patch deleted file mode 100644 index fcd8087..0000000 --- a/debian/patches/CVE-2021-36160.patch +++ /dev/null @@ -1,51 +0,0 @@ -Description: mod_proxy_uwsgi: Fix PATH_INFO setting for generic worker -Author: Yann Ylavic <ylavic@apache.org> -Origin: upstream, https://github.com/apache/httpd/commit/b364cad7 -Bug: https://security-tracker.debian.org/tracker/CVE-2021-36160 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-09-21 - ---- a/modules/proxy/mod_proxy_uwsgi.c -+++ b/modules/proxy/mod_proxy_uwsgi.c -@@ -452,11 +452,8 @@ - const char *proxyname, apr_port_t proxyport) - { - int status; -- int delta = 0; -- int decode_status; - proxy_conn_rec *backend = NULL; - apr_pool_t *p = r->pool; -- size_t w_len; - char server_portstr[32]; - char *u_path_info; - apr_uri_t *uri; -@@ -468,23 +465,14 @@ - - uri = apr_palloc(r->pool, sizeof(*uri)); - -- /* ADD PATH_INFO */ --#if AP_MODULE_MAGIC_AT_LEAST(20111130,0) -- w_len = strlen(worker->s->name); --#else -- w_len = strlen(worker->name); --#endif -- u_path_info = r->filename + 6 + w_len; -- if (u_path_info[0] != '/') { -- delta = 1; -- } -- decode_status = ap_unescape_url(url + w_len - delta); -- if (decode_status) { -+ /* ADD PATH_INFO (unescaped) */ -+ u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/'); -+ if (!u_path_info || ap_unescape_url(u_path_info) != OK) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100) -- "unable to decode uri: %s", url + w_len - delta); -+ "unable to decode uwsgi uri: %s", url); - return HTTP_INTERNAL_SERVER_ERROR; - } -- apr_table_add(r->subprocess_env, "PATH_INFO", url + w_len - delta); -+ apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info); - - - /* Create space for state information */ diff --git a/debian/patches/CVE-2021-39275.patch b/debian/patches/CVE-2021-39275.patch deleted file mode 100644 index d489891..0000000 --- a/debian/patches/CVE-2021-39275.patch +++ /dev/null @@ -1,35 +0,0 @@ -Description: Backport of - From c69d4cc90c0e27703030b3ff09f91bf4dcbcfd51 Mon Sep 17 00:00:00 2001 - From: Stefan Eissing <icing@apache.org> - Date: Tue, 10 Aug 2021 08:55:54 +0000 - Subject: [PATCH] Merged r1892012 from trunk: - . - and - From ac62c7e7436560cf4f7725ee586364ce95c07804 Mon Sep 17 00:00:00 2001 - From: Graham Leggett <minfrin@apache.org> - Date: Sat, 21 Aug 2021 21:35:04 +0000 - Subject: [PATCH] Backport: -Author: Moritz Muehlenhoff <jmm@inutil.org> -Origin: upstream -Forwarded: not-needed -Last-Update: 2021-09-30 - ---- a/server/util.c -+++ b/server/util.c -@@ -2460,13 +2460,12 @@ - * in front of every " that doesn't already have one. - */ - while (*inchr != '\0') { -- if ((*inchr == '\\') && (inchr[1] != '\0')) { -- *outchr++ = *inchr++; -- *outchr++ = *inchr++; -- } - if (*inchr == '"') { - *outchr++ = '\\'; - } -+ if ((*inchr == '\\') && (inchr[1] != '\0')) { -+ *outchr++ = *inchr++; -+ } - if (*inchr != '\0') { - *outchr++ = *inchr++; - } diff --git a/debian/patches/CVE-2021-40438.patch b/debian/patches/CVE-2021-40438.patch deleted file mode 100644 index 8cf60a7..0000000 --- a/debian/patches/CVE-2021-40438.patch +++ /dev/null @@ -1,124 +0,0 @@ -Description: Backport of the following patches: -Origin: upstream, - https://github.com/apache/httpd/commit/496c863776c68bd08cdbeb7d8fa5935ba63b76c2 - https://github.com/apache/httpd/commit/d4901cb32133bc0e59ad193a29d1665597080d67 - https://github.com/apache/httpd/commit/81a8b0133b46c4cf7dfc4b5476ad46eb34aa0a5c - https://github.com/apache/httpd/commit/6e768a811c59ca6a0769b72681aaef381823339f -Forwarded: not-needed -Reviewed-By: Moritz Muehlenhoff <jmm@inutil.org> -Last-Update: 2021-09-30 - ---- a/modules/mappers/mod_rewrite.c -+++ b/modules/mappers/mod_rewrite.c -@@ -620,6 +620,13 @@ - return 6; - } - break; -+ -+ case 'u': -+ case 'U': -+ if (!ap_cstr_casecmpn(uri, "nix:", 4)) { /* unix: */ -+ *sqs = 1; -+ return (uri[4] == '/' && uri[5] == '/') ? 7 : 5; -+ } - } - - return 0; ---- a/modules/proxy/mod_proxy.c -+++ b/modules/proxy/mod_proxy.c -@@ -1690,7 +1690,7 @@ - * the UDS path... ignore it - */ - if (!strncasecmp(url, "unix:", 5) && -- ((ptr = ap_strchr_c(url, '|')) != NULL)) { -+ ((ptr = ap_strchr_c(url + 5, '|')) != NULL)) { - /* move past the 'unix:...|' UDS path info */ - const char *ret, *c; - ---- a/modules/proxy/proxy_util.c -+++ b/modules/proxy/proxy_util.c -@@ -2077,33 +2077,43 @@ - * were passed a UDS url (eg: from mod_proxy) and adjust uds_path - * as required. - */ --static void fix_uds_filename(request_rec *r, char **url) -+static int fix_uds_filename(request_rec *r, char **url) - { -- char *ptr, *ptr2; -- if (!r || !r->filename) return; -+ char *uds_url = r->filename + 6, *origin_url; - - if (!strncmp(r->filename, "proxy:", 6) && -- (ptr2 = ap_strcasestr(r->filename, "unix:")) && -- (ptr = ap_strchr(ptr2, '|'))) { -+ !ap_cstr_casecmpn(uds_url, "unix:", 5) && -+ (origin_url = ap_strchr(uds_url + 5, '|'))) { -+ char *uds_path = NULL; -+ apr_size_t url_len; - apr_uri_t urisock; - apr_status_t rv; -- *ptr = '\0'; -- rv = apr_uri_parse(r->pool, ptr2, &urisock); -- if (rv == APR_SUCCESS) { -- char *rurl = ptr+1; -- char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path); -- apr_table_setn(r->notes, "uds_path", sockpath); -- *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */ -- /* r->filename starts w/ "proxy:", so add after that */ -- memmove(r->filename+6, rurl, strlen(rurl)+1); -- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, -- "*: rewrite of url due to UDS(%s): %s (%s)", -- sockpath, *url, r->filename); -+ -+ *origin_url = '\0'; -+ rv = apr_uri_parse(r->pool, uds_url, &urisock); -+ *origin_url++ = '|'; -+ -+ if (rv == APR_SUCCESS && urisock.path && (!urisock.hostname -+ || !urisock.hostname[0])) { -+ uds_path = ap_runtime_dir_relative(r->pool, urisock.path); - } -- else { -- *ptr = '|'; -+ if (!uds_path) { -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292) -+ "Invalid proxy UDS filename (%s)", r->filename); -+ return 0; - } -+ apr_table_setn(r->notes, "uds_path", uds_path); -+ -+ /* Remove the UDS path from *url and r->filename */ -+ url_len = strlen(origin_url); -+ *url = apr_pstrmemdup(r->pool, origin_url, url_len); -+ memcpy(uds_url, *url, url_len + 1); -+ -+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, -+ "*: rewrite of url due to UDS(%s): %s (%s)", -+ uds_path, *url, r->filename); - } -+ return 1; - } - - PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, -@@ -2121,7 +2131,9 @@ - "%s: found worker %s for %s", - (*worker)->s->scheme, (*worker)->s->name, *url); - *balancer = NULL; -- fix_uds_filename(r, url); -+ if (!fix_uds_filename(r, url)) { -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } - access_status = OK; - } - else if (r->proxyreq == PROXYREQ_PROXY) { -@@ -2152,7 +2164,9 @@ - * regarding the Connection header in the request. - */ - apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); -- fix_uds_filename(r, url); -+ if (!fix_uds_filename(r, url)) { -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } - } - } - } diff --git a/debian/patches/CVE-2021-44224-1.patch b/debian/patches/CVE-2021-44224-1.patch deleted file mode 100644 index 0f540c8..0000000 --- a/debian/patches/CVE-2021-44224-1.patch +++ /dev/null @@ -1,206 +0,0 @@ -Description: CVE-2021-44224 -Author: Yann Ylavic <ylavic@apache.org> -Origin: upstream, https://github.com/apache/httpd/commit/a962ba73 -Bug: https://security-tracker.debian.org/tracker/CVE-2021-44224 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-12-21 - ---- a/include/http_protocol.h -+++ b/include/http_protocol.h -@@ -75,6 +75,13 @@ - AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, - apr_bucket_brigade *bb); - -+/** -+ * Run post_read_request hook and validate. -+ * @param r The current request -+ * @return OK or HTTP_... -+ */ -+AP_DECLARE(int) ap_post_read_request(request_rec *r); -+ - /* Finish up stuff after a request */ - - /** ---- a/modules/http/http_request.c -+++ b/modules/http/http_request.c -@@ -681,7 +681,7 @@ - * to do their thing on internal redirects as well. Perhaps this is a - * misnamed function. - */ -- if ((access_status = ap_run_post_read_request(new))) { -+ if ((access_status = ap_post_read_request(new))) { - ap_die(access_status, new); - return NULL; - } ---- a/modules/http2/h2_request.c -+++ b/modules/http2/h2_request.c -@@ -337,7 +337,7 @@ - NULL, r, r->connection); - - if (access_status != HTTP_OK -- || (access_status = ap_run_post_read_request(r))) { -+ || (access_status = ap_post_read_request(r))) { - /* Request check post hooks failed. An example of this would be a - * request for a vhost where h2 is disabled --> 421. - */ ---- a/modules/proxy/mod_proxy.c -+++ b/modules/proxy/mod_proxy.c -@@ -576,13 +576,13 @@ - - /* Ick... msvc (perhaps others) promotes ternary short results to int */ - -- if (conf->req && r->parsed_uri.scheme) { -+ if (conf->req && r->parsed_uri.scheme && r->parsed_uri.hostname) { - /* but it might be something vhosted */ -- if (!(r->parsed_uri.hostname -- && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r)) -- && ap_matches_request_vhost(r, r->parsed_uri.hostname, -- (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port -- : ap_default_port(r))))) { -+ if (strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0 -+ || !ap_matches_request_vhost(r, r->parsed_uri.hostname, -+ (apr_port_t)(r->parsed_uri.port_str -+ ? r->parsed_uri.port -+ : ap_default_port(r)))) { - r->proxyreq = PROXYREQ_PROXY; - r->uri = r->unparsed_uri; - r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); -@@ -1722,6 +1722,7 @@ - struct proxy_alias *new; - char *f = cmd->path; - char *r = NULL; -+ const char *real; - char *word; - apr_table_t *params = apr_table_make(cmd->pool, 5); - const apr_array_header_t *arr; -@@ -1787,6 +1788,10 @@ - if (r == NULL) { - return "ProxyPass|ProxyPassMatch needs a path when not defined in a location"; - } -+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, r))) { -+ return "ProxyPass|ProxyPassMatch uses an invalid \"unix:\" URL"; -+ } -+ - - /* if per directory, save away the single alias */ - if (cmd->path) { -@@ -1803,7 +1808,7 @@ - } - - new->fake = apr_pstrdup(cmd->pool, f); -- new->real = apr_pstrdup(cmd->pool, ap_proxy_de_socketfy(cmd->pool, r)); -+ new->real = apr_pstrdup(cmd->pool, real); - new->flags = flags; - if (use_regex) { - new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); -@@ -2280,6 +2285,7 @@ - proxy_worker *worker; - char *path = cmd->path; - char *name = NULL; -+ const char *real; - char *word; - apr_table_t *params = apr_table_make(cmd->pool, 5); - const apr_array_header_t *arr; -@@ -2320,6 +2326,9 @@ - return "BalancerMember must define balancer name when outside <Proxy > section"; - if (!name) - return "BalancerMember must define remote proxy server"; -+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { -+ return "BalancerMember uses an invalid \"unix:\" URL"; -+ } - - ap_str_tolower(path); /* lowercase scheme://hostname */ - -@@ -2332,7 +2341,7 @@ - } - - /* Try to find existing worker */ -- worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, ap_proxy_de_socketfy(cmd->temp_pool, name)); -+ worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, real); - if (!worker) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147) - "Defining worker '%s' for balancer '%s'", -@@ -2421,7 +2430,14 @@ - } - } - else { -- worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, ap_proxy_de_socketfy(cmd->temp_pool, name)); -+ const char *real; -+ -+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { -+ return "ProxySet uses an invalid \"unix:\" URL"; -+ } -+ -+ worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, -+ real); - if (!worker) { - if (in_proxy_section) { - err = ap_proxy_define_worker(cmd->pool, &worker, NULL, -@@ -2563,8 +2579,14 @@ - } - } - else { -+ const char *real; -+ -+ if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, conf->p))) { -+ return "<Proxy/ProxyMatch > uses an invalid \"unix:\" URL"; -+ } -+ - worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf, -- ap_proxy_de_socketfy(cmd->temp_pool, (char*)conf->p)); -+ real); - if (!worker) { - err = ap_proxy_define_worker(cmd->pool, &worker, NULL, - sconf, conf->p, 0); ---- a/modules/proxy/proxy_util.c -+++ b/modules/proxy/proxy_util.c -@@ -1662,6 +1662,9 @@ - } - - url = ap_proxy_de_socketfy(p, url); -+ if (!url) { -+ return NULL; -+ } - - c = ap_strchr_c(url, ':'); - if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -1465,7 +1465,7 @@ - NULL, r, r->connection); - - if (access_status != HTTP_OK -- || (access_status = ap_run_post_read_request(r))) { -+ || (access_status = ap_post_read_request(r))) { - ap_die(access_status, r); - ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); - ap_run_log_transaction(r); -@@ -1503,6 +1503,27 @@ - return r; - } - -+AP_DECLARE(int) ap_post_read_request(request_rec *r) -+{ -+ int status; -+ -+ if ((status = ap_run_post_read_request(r))) { -+ return status; -+ } -+ -+ /* Enforce http(s) only scheme for non-forward-proxy requests */ -+ if (!r->proxyreq -+ && r->parsed_uri.scheme -+ && (ap_cstr_casecmpn(r->parsed_uri.scheme, "http", 4) != 0 -+ || (r->parsed_uri.scheme[4] != '\0' -+ && (apr_tolower(r->parsed_uri.scheme[4]) != 's' -+ || r->parsed_uri.scheme[5] != '\0')))) { -+ return HTTP_BAD_REQUEST; -+ } -+ -+ return OK; -+} -+ - /* if a request with a body creates a subrequest, remove original request's - * input headers which pertain to the body which has already been read. - * out-of-line helper function for ap_set_sub_req_protocol. diff --git a/debian/patches/CVE-2021-44224-2.patch b/debian/patches/CVE-2021-44224-2.patch deleted file mode 100644 index 6b841dd..0000000 --- a/debian/patches/CVE-2021-44224-2.patch +++ /dev/null @@ -1,93 +0,0 @@ -Description: mod_proxy: Don't prevent forwarding URIs w/ no hostname. - (fix for r1895955 already in 2.4.x) - . - Part not applied: - #--- a/modules/proxy/mod_proxy.h - #+++ b/modules/proxy/mod_proxy.h - #@@ -323,6 +323,8 @@ - # #define PROXY_WORKER_HC_FAIL_FLAG 'C' - # #define PROXY_WORKER_HOT_SPARE_FLAG 'R' - # - #+#define AP_PROXY_WORKER_NO_UDS (1u << 3) - #+ - # #define PROXY_WORKER_NOT_USABLE_BITMAP ( PROXY_WORKER_IN_SHUTDOWN | \ - # PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR | \ - # PROXY_WORKER_HC_FAIL ) - #--- a/modules/proxy/proxy_util.c - #+++ b/modules/proxy/proxy_util.c - #@@ -1661,9 +1661,11 @@ - # return NULL; - # } - # - #- url = ap_proxy_de_socketfy(p, url); - #- if (!url) { - #- return NULL; - #+ if (!(mask & AP_PROXY_WORKER_NO_UDS)) { - #+ url = ap_proxy_de_socketfy(p, url); - #+ if (!url) { - #+ return NULL; - #+ } - # } - # - # c = ap_strchr_c(url, ':'); -Author: Stefan Eissing <icing@apache.org> -Origin: upstream, https://github.com/apache/httpd/commit/a0521d289 -Bug: https://security-tracker.debian.org/tracker/CVE-2021-44224 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-12-21 - ---- a/modules/proxy/mod_proxy.c -+++ b/modules/proxy/mod_proxy.c -@@ -576,9 +576,10 @@ - - /* Ick... msvc (perhaps others) promotes ternary short results to int */ - -- if (conf->req && r->parsed_uri.scheme && r->parsed_uri.hostname) { -+ if (conf->req && r->parsed_uri.scheme) { - /* but it might be something vhosted */ -- if (strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0 -+ if (!r->parsed_uri.hostname -+ || strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0 - || !ap_matches_request_vhost(r, r->parsed_uri.hostname, - (apr_port_t)(r->parsed_uri.port_str - ? r->parsed_uri.port ---- a/modules/proxy/proxy_util.c -+++ b/modules/proxy/proxy_util.c -@@ -2128,22 +2128,21 @@ - - access_status = proxy_run_pre_request(worker, balancer, r, conf, url); - if (access_status == DECLINED && *balancer == NULL) { -+ const int forward = (r->proxyreq == PROXYREQ_PROXY); - *worker = ap_proxy_get_worker(r->pool, NULL, conf, *url); - if (*worker) { - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, - "%s: found worker %s for %s", - (*worker)->s->scheme, (*worker)->s->name, *url); -- *balancer = NULL; -- if (!fix_uds_filename(r, url)) { -+ if (!forward && !fix_uds_filename(r, url)) { - return HTTP_INTERNAL_SERVER_ERROR; - } - access_status = OK; - } -- else if (r->proxyreq == PROXYREQ_PROXY) { -+ else if (forward) { - if (conf->forward) { - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, - "*: found forward proxy worker for %s", *url); -- *balancer = NULL; - *worker = conf->forward; - access_status = OK; - /* -@@ -2157,8 +2156,8 @@ - else if (r->proxyreq == PROXYREQ_REVERSE) { - if (conf->reverse) { - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, -- "*: using default reverse proxy worker for %s (no keepalive)", *url); -- *balancer = NULL; -+ "*: using default reverse proxy worker for %s " -+ "(no keepalive)", *url); - *worker = conf->reverse; - access_status = OK; - /* diff --git a/debian/patches/CVE-2021-44790.patch b/debian/patches/CVE-2021-44790.patch deleted file mode 100644 index dbba745..0000000 --- a/debian/patches/CVE-2021-44790.patch +++ /dev/null @@ -1,18 +0,0 @@ -Description: Improve error handling -Author: Stefan Eissing <icing@apache.org> -Origin: upstream, https://github.com/apache/httpd/commit/07b9768c -Bug: https://security-tracker.debian.org/tracker/CVE-2021-44790 -Forwarded: not-needed -Reviewed-By: Yadd <yadd@debian.org> -Last-Update: 2021-12-21 - ---- a/modules/lua/lua_request.c -+++ b/modules/lua/lua_request.c -@@ -376,6 +376,7 @@ - if (end == NULL) break; - key = (char *) apr_pcalloc(r->pool, 256); - filename = (char *) apr_pcalloc(r->pool, 256); -+ if (end - crlf <= 8) break; - vlen = end - crlf - 8; - buffer = (char *) apr_pcalloc(r->pool, vlen+1); - memcpy(buffer, crlf + 4, vlen); diff --git a/debian/patches/CVE-2022-22719.patch b/debian/patches/CVE-2022-22719.patch deleted file mode 100644 index c52ceef..0000000 --- a/debian/patches/CVE-2022-22719.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 1b96582269d9ec7c82ee0fea1f67934e4b8176ad Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Mon, 7 Mar 2022 14:51:19 +0000 -Subject: [PATCH] mod_lua: Error out if lua_read_body() or lua_write_body() - fail. - -Otherwise r:requestbody() or r:parsebody() failures might go unnoticed for -the user. - - -Merge r1898689 from trunk. -Submitted by: rpluem -Reviewed by: rpluem, covener, ylavic - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898694 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/lua/lua_request.c | 33 ++++++++++++++++++++------------- - 1 file changed, 20 insertions(+), 13 deletions(-) - -diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c -index 493b2bb431..1eab7b6a47 100644 ---- a/modules/lua/lua_request.c -+++ b/modules/lua/lua_request.c -@@ -235,14 +235,16 @@ static int lua_read_body(request_rec *r, const char **rbuf, apr_off_t *size, - { - int rc = OK; - -+ *rbuf = NULL; -+ *size = 0; -+ - if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { - return (rc); - } - if (ap_should_client_block(r)) { - - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -- char argsbuffer[HUGE_STRING_LEN]; -- apr_off_t rsize, len_read, rpos = 0; -+ apr_off_t len_read, rpos = 0; - apr_off_t length = r->remaining; - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - -@@ -250,18 +252,18 @@ static int lua_read_body(request_rec *r, const char **rbuf, apr_off_t *size, - return APR_EINCOMPLETE; /* Only room for incomplete data chunk :( */ - } - *rbuf = (const char *) apr_pcalloc(r->pool, (apr_size_t) (length + 1)); -- *size = length; -- while ((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) { -- if ((rpos + len_read) > length) { -- rsize = length - rpos; -- } -- else { -- rsize = len_read; -- } -- -- memcpy((char *) *rbuf + rpos, argsbuffer, (size_t) rsize); -- rpos += rsize; -+ while ((rpos < length) -+ && (len_read = ap_get_client_block(r, (char *) *rbuf + rpos, -+ length - rpos)) > 0) { -+ rpos += len_read; -+ } -+ if (len_read < 0) { -+ return APR_EINCOMPLETE; - } -+ *size = rpos; -+ } -+ else { -+ rc = DONE; - } - - return (rc); -@@ -278,6 +280,8 @@ static apr_status_t lua_write_body(request_rec *r, apr_file_t *file, apr_off_t * - { - apr_status_t rc = OK; - -+ *size = 0; -+ - if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) - return rc; - if (ap_should_client_block(r)) { -@@ -303,6 +307,9 @@ static apr_status_t lua_write_body(request_rec *r, apr_file_t *file, apr_off_t * - rpos += rsize; - } - } -+ else { -+ rc = DONE; -+ } - - return rc; - } --- -2.30.2 - diff --git a/debian/patches/CVE-2022-22720.patch b/debian/patches/CVE-2022-22720.patch deleted file mode 100644 index a296824..0000000 --- a/debian/patches/CVE-2022-22720.patch +++ /dev/null @@ -1,190 +0,0 @@ -From 19aa2d83b379719420f3a178413325156d7a62f3 Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Mon, 7 Mar 2022 14:46:08 +0000 -Subject: [PATCH] core: Simpler connection close logic if discarding the - request body fails. - -If ap_discard_request_body() sets AP_CONN_CLOSE by itself it simplifies and -allows to consolidate end_output_stream() and error_output_stream(). - - -Merge r1898683 from trunk. -Submitted by: ylavic, rpluem -Reviewed by: ylavic, rpluem, covener - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898692 13f79535-47bb-0310-9956-ffa450edef68 ---- - changes-entries/discard_body.diff | 2 + - modules/http/http_filters.c | 69 ++++++++++++++++--------------- - server/protocol.c | 14 +++++-- - 3 files changed, 48 insertions(+), 37 deletions(-) - create mode 100644 changes-entries/discard_body.diff - -diff --git a/changes-entries/discard_body.diff b/changes-entries/discard_body.diff -new file mode 100644 -index 0000000000..6b467ac5ee ---- /dev/null -+++ b/changes-entries/discard_body.diff -@@ -0,0 +1,2 @@ -+ *) core: Simpler connection close logic if discarding the request body fails. -+ [Yann Ylavic, Ruediger Pluem] -\ No newline at end of file -diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c -index d9b3621215..43e8c6dd5d 100644 ---- a/modules/http/http_filters.c -+++ b/modules/http/http_filters.c -@@ -1598,9 +1598,9 @@ AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status) - */ - AP_DECLARE(int) ap_discard_request_body(request_rec *r) - { -+ int rc = OK; -+ conn_rec *c = r->connection; - apr_bucket_brigade *bb; -- int seen_eos; -- apr_status_t rv; - - /* Sometimes we'll get in a state where the input handling has - * detected an error where we want to drop the connection, so if -@@ -1609,54 +1609,57 @@ AP_DECLARE(int) ap_discard_request_body(request_rec *r) - * - * This function is also a no-op on a subrequest. - */ -- if (r->main || r->connection->keepalive == AP_CONN_CLOSE || -- ap_status_drops_connection(r->status)) { -+ if (r->main || c->keepalive == AP_CONN_CLOSE) { -+ return OK; -+ } -+ if (ap_status_drops_connection(r->status)) { -+ c->keepalive = AP_CONN_CLOSE; - return OK; - } - - bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); -- seen_eos = 0; -- do { -- apr_bucket *bucket; -+ for (;;) { -+ apr_status_t rv; - - rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, - APR_BLOCK_READ, HUGE_STRING_LEN); -- - if (rv != APR_SUCCESS) { -- apr_brigade_destroy(bb); -- return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); -+ rc = ap_map_http_request_error(rv, HTTP_BAD_REQUEST); -+ goto cleanup; - } - -- for (bucket = APR_BRIGADE_FIRST(bb); -- bucket != APR_BRIGADE_SENTINEL(bb); -- bucket = APR_BUCKET_NEXT(bucket)) -- { -- const char *data; -- apr_size_t len; -+ while (!APR_BRIGADE_EMPTY(bb)) { -+ apr_bucket *b = APR_BRIGADE_FIRST(bb); - -- if (APR_BUCKET_IS_EOS(bucket)) { -- seen_eos = 1; -- break; -- } -- -- /* These are metadata buckets. */ -- if (bucket->length == 0) { -- continue; -+ if (APR_BUCKET_IS_EOS(b)) { -+ goto cleanup; - } - -- /* We MUST read because in case we have an unknown-length -- * bucket or one that morphs, we want to exhaust it. -+ /* There is no need to read empty or metadata buckets or -+ * buckets of known length, but we MUST read buckets of -+ * unknown length in order to exhaust them. - */ -- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); -- if (rv != APR_SUCCESS) { -- apr_brigade_destroy(bb); -- return HTTP_BAD_REQUEST; -+ if (b->length == (apr_size_t)-1) { -+ apr_size_t len; -+ const char *data; -+ -+ rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); -+ if (rv != APR_SUCCESS) { -+ rc = HTTP_BAD_REQUEST; -+ goto cleanup; -+ } - } -+ -+ apr_bucket_delete(b); - } -- apr_brigade_cleanup(bb); -- } while (!seen_eos); -+ } - -- return OK; -+cleanup: -+ apr_brigade_cleanup(bb); -+ if (rc != OK) { -+ c->keepalive = AP_CONN_CLOSE; -+ } -+ return rc; - } - - /* Here we deal with getting the request message body from the client. -diff --git a/server/protocol.c b/server/protocol.c -index 2214f72b5a..298f61e1fb 100644 ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -1687,23 +1687,29 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, - rnew->main = (request_rec *) r; - } - --static void end_output_stream(request_rec *r) -+static void end_output_stream(request_rec *r, int status) - { - conn_rec *c = r->connection; - apr_bucket_brigade *bb; - apr_bucket *b; - - bb = apr_brigade_create(r->pool, c->bucket_alloc); -+ if (status != OK) { -+ b = ap_bucket_error_create(status, NULL, r->pool, c->bucket_alloc); -+ APR_BRIGADE_INSERT_TAIL(bb, b); -+ } - b = apr_bucket_eos_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); -+ - ap_pass_brigade(r->output_filters, bb); -+ apr_brigade_cleanup(bb); - } - - AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub) - { - /* tell the filter chain there is no more content coming */ - if (!sub->eos_sent) { -- end_output_stream(sub); -+ end_output_stream(sub, OK); - } - } - -@@ -1714,11 +1720,11 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub) - */ - AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r) - { -- (void) ap_discard_request_body(r); -+ int status = ap_discard_request_body(r); - - /* tell the filter chain there is no more content coming */ - if (!r->eos_sent) { -- end_output_stream(r); -+ end_output_stream(r, status); - } - } - --- -2.30.2 - diff --git a/debian/patches/CVE-2022-22721.patch b/debian/patches/CVE-2022-22721.patch deleted file mode 100644 index 2f607aa..0000000 --- a/debian/patches/CVE-2022-22721.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 5a72f0fe6f2f8ce35c45242e99a421dc19251ab5 Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Mon, 7 Mar 2022 14:48:54 +0000 -Subject: [PATCH] core: Make sure and check that LimitXMLRequestBody fits in - system memory. - -LimitXMLRequestBody can not exceed the size needed to ap_escape_html2() the -body without failing to allocate memory, so enforce this at load time based -on APR_SIZE_MAX, and make sure that ap_escape_html2() is within the bounds. - -Document the limits for LimitXMLRequestBody in our docs. - - -Merge r1898686 from trunk. -Submitted by: ylavic, rpluem -Reviewed by: ylavic, covener, rpluem - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898693 13f79535-47bb-0310-9956-ffa450edef68 ---- - changes-entries/AP_MAX_LIMIT_XML_BODY.diff | 2 ++ - docs/manual/mod/core.xml | 12 +++++++++--- - server/core.c | 9 +++++++++ - server/util.c | 8 ++++++-- - server/util_xml.c | 2 +- - 5 files changed, 27 insertions(+), 6 deletions(-) - create mode 100644 changes-entries/AP_MAX_LIMIT_XML_BODY.diff - -diff --git a/changes-entries/AP_MAX_LIMIT_XML_BODY.diff b/changes-entries/AP_MAX_LIMIT_XML_BODY.diff -new file mode 100644 -index 0000000000..07fef3c624 ---- /dev/null -+++ b/changes-entries/AP_MAX_LIMIT_XML_BODY.diff -@@ -0,0 +1,2 @@ -+ *) core: Make sure and check that LimitXMLRequestBody fits in system memory. -+ [Ruediger Pluem, Yann Ylavic] -\ No newline at end of file -diff --git a/server/core.c b/server/core.c -index 798212b480..090e397642 100644 ---- a/server/core.c -+++ b/server/core.c -@@ -72,6 +72,8 @@ - /* LimitXMLRequestBody handling */ - #define AP_LIMIT_UNSET ((long) -1) - #define AP_DEFAULT_LIMIT_XML_BODY ((apr_size_t)1000000) -+/* Hard limit for ap_escape_html2() */ -+#define AP_MAX_LIMIT_XML_BODY ((apr_size_t)(APR_SIZE_MAX / 6 - 1)) - - #define AP_MIN_SENDFILE_BYTES (256) - -@@ -3761,6 +3763,11 @@ static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_, - if (conf->limit_xml_body < 0) - return "LimitXMLRequestBody requires a non-negative integer."; - -+ /* zero is AP_MAX_LIMIT_XML_BODY (implicitly) */ -+ if ((apr_size_t)conf->limit_xml_body > AP_MAX_LIMIT_XML_BODY) -+ return apr_psprintf(cmd->pool, "LimitXMLRequestBody must not exceed " -+ "%" APR_SIZE_T_FMT, AP_MAX_LIMIT_XML_BODY); -+ - return NULL; - } - -@@ -3849,6 +3856,8 @@ AP_DECLARE(apr_size_t) ap_get_limit_xml_body(const request_rec *r) - conf = ap_get_core_module_config(r->per_dir_config); - if (conf->limit_xml_body == AP_LIMIT_UNSET) - return AP_DEFAULT_LIMIT_XML_BODY; -+ if (conf->limit_xml_body == 0) -+ return AP_MAX_LIMIT_XML_BODY; - - return (apr_size_t)conf->limit_xml_body; - } -diff --git a/server/util.c b/server/util.c -index 6cfe0035c4..604be1a1ce 100644 ---- a/server/util.c -+++ b/server/util.c -@@ -2142,11 +2142,14 @@ AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer) - - AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc) - { -- int i, j; -+ apr_size_t i, j; - char *x; - - /* first, count the number of extra characters */ -- for (i = 0, j = 0; s[i] != '\0'; i++) -+ for (i = 0, j = 0; s[i] != '\0'; i++) { -+ if (i + j > APR_SIZE_MAX - 6) { -+ abort(); -+ } - if (s[i] == '<' || s[i] == '>') - j += 3; - else if (s[i] == '&') -@@ -2155,6 +2158,7 @@ AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc) - j += 5; - else if (toasc && !apr_isascii(s[i])) - j += 5; -+ } - - if (j == 0) - return apr_pstrmemdup(p, s, i); -diff --git a/server/util_xml.c b/server/util_xml.c -index 4845194656..22806fa8a4 100644 ---- a/server/util_xml.c -+++ b/server/util_xml.c -@@ -85,7 +85,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc) - } - - total_read += len; -- if (limit_xml_body && total_read > limit_xml_body) { -+ if (total_read > limit_xml_body) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00539) - "XML request body is larger than the configured " - "limit of %lu", (unsigned long)limit_xml_body); --- -2.30.2 - diff --git a/debian/patches/CVE-2022-23943-1.patch b/debian/patches/CVE-2022-23943-1.patch deleted file mode 100644 index d82fd1d..0000000 --- a/debian/patches/CVE-2022-23943-1.patch +++ /dev/null @@ -1,360 +0,0 @@ -From 943f57b336f264d77e5b780c82ab73daf3d14deb Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Mon, 7 Mar 2022 14:52:42 +0000 -Subject: [PATCH] mod_sed: use size_t to allow for larger buffer sizes and - unsigned arithmetics. - -Let's switch to apr_size_t buffers and get rid of the ints. - - -Merge r1898690 from trunk. -Submitted by: rpluem -Reviewed by: rpluem, covener, ylavic - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898695 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/filters/libsed.h | 12 +++--- - modules/filters/mod_sed.c | 10 ++--- - modules/filters/sed1.c | 79 +++++++++++++++++++++++---------------- - 3 files changed, 58 insertions(+), 43 deletions(-) - -diff --git a/modules/filters/libsed.h b/modules/filters/libsed.h -index 76cbc0ce8a..0256b1ea83 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_sed.c b/modules/filters/mod_sed.c -index 9b408029a8..7092dd5e7f 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; -@@ -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) { -diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c -index be03506788..67a8d06515 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, ...) -@@ -92,11 +92,11 @@ static void eval_errf(sed_eval_t *eval, const char *fmt, ...) - * grow_buffer - */ - static void grow_buffer(apr_pool_t *pool, char **buffer, -- char **spend, unsigned int *cursize, -- unsigned int newsize) -+ char **spend, apr_size_t *cursize, -+ apr_size_t newsize) - { - char* newbuffer = NULL; -- int spendsize = 0; -+ apr_size_t spendsize = 0; - if (*cursize >= newsize) - return; - /* Avoid number of times realloc is called. It could cause huge memory -@@ -124,7 +124,7 @@ static void grow_buffer(apr_pool_t *pool, char **buffer, - /* - * grow_line_buffer - */ --static void grow_line_buffer(sed_eval_t *eval, int newsize) -+static void grow_line_buffer(sed_eval_t *eval, apr_size_t newsize) - { - grow_buffer(eval->pool, &eval->linebuf, &eval->lspend, - &eval->lsize, newsize); -@@ -133,7 +133,7 @@ static void grow_line_buffer(sed_eval_t *eval, int newsize) - /* - * grow_hold_buffer - */ --static void grow_hold_buffer(sed_eval_t *eval, int newsize) -+static void grow_hold_buffer(sed_eval_t *eval, apr_size_t newsize) - { - grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend, - &eval->hsize, newsize); -@@ -142,7 +142,7 @@ static void grow_hold_buffer(sed_eval_t *eval, int newsize) - /* - * grow_gen_buffer - */ --static void grow_gen_buffer(sed_eval_t *eval, int newsize, -+static void grow_gen_buffer(sed_eval_t *eval, apr_size_t newsize, - char **gspend) - { - if (gspend == NULL) { -@@ -156,9 +156,9 @@ static void grow_gen_buffer(sed_eval_t *eval, int newsize, - /* - * appendmem_to_linebuf - */ --static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len) -+static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t len) - { -- unsigned int reqsize = (eval->lspend - eval->linebuf) + len; -+ apr_size_t reqsize = (eval->lspend - eval->linebuf) + len; - if (eval->lsize < reqsize) { - grow_line_buffer(eval, reqsize); - } -@@ -169,21 +169,36 @@ static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len) - /* - * append_to_linebuf - */ --static void append_to_linebuf(sed_eval_t *eval, const char* sz) -+static void 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; - /* Copy string including null character */ - appendmem_to_linebuf(eval, sz, len + 1); - --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; -+ } -+ } - } - - /* - * copy_to_linebuf - */ --static void copy_to_linebuf(sed_eval_t *eval, const char* sz) -+static void copy_to_linebuf(sed_eval_t *eval, const char* sz, -+ step_vars_storage *step_vars) - { - eval->lspend = eval->linebuf; -- append_to_linebuf(eval, sz); -+ append_to_linebuf(eval, sz, step_vars); - } - - /* -@@ -191,8 +206,8 @@ static void copy_to_linebuf(sed_eval_t *eval, const char* sz) - */ - static void 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; - if (eval->hsize <= reqsize) { - grow_hold_buffer(eval, reqsize); - } -@@ -215,8 +230,8 @@ static void copy_to_holdbuf(sed_eval_t *eval, const char* sz) - */ - static void 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; - if (eval->gsize < reqsize) { - grow_gen_buffer(eval, reqsize, gspend); - } -@@ -230,8 +245,8 @@ static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend) - */ - static void 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; - if (eval->gsize < reqsize) { - grow_gen_buffer(eval, reqsize, NULL); - } -@@ -353,7 +368,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; - -@@ -383,7 +398,7 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void - - while (bufsz) { - char *n; -- int llen; -+ apr_size_t llen; - - n = memchr(buf, '\n', bufsz); - if (n == NULL) -@@ -442,7 +457,7 @@ 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, ""); -+ append_to_linebuf(eval, "", NULL); - } - - *eval->lspend = '\0'; -@@ -666,7 +681,7 @@ static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n, - lp = step_vars->loc2; - step_vars->loc2 = sp - eval->genbuf + eval->linebuf; - append_to_genbuf(eval, lp, &sp); -- copy_to_linebuf(eval, eval->genbuf); -+ copy_to_linebuf(eval, eval->genbuf, step_vars); - return rv; - } - -@@ -676,8 +691,8 @@ 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); -@@ -735,7 +750,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, - } - - p1++; -- copy_to_linebuf(eval, p1); -+ copy_to_linebuf(eval, p1, step_vars); - eval->jflag++; - break; - -@@ -745,12 +760,12 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, - break; - - case GCOM: -- copy_to_linebuf(eval, eval->holdbuf); -+ copy_to_linebuf(eval, eval->holdbuf, step_vars); - break; - - case CGCOM: -- append_to_linebuf(eval, "\n"); -- append_to_linebuf(eval, eval->holdbuf); -+ append_to_linebuf(eval, "\n", step_vars); -+ append_to_linebuf(eval, eval->holdbuf, step_vars); - break; - - case HCOM: -@@ -881,7 +896,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, - if (rv != APR_SUCCESS) - return rv; - } -- append_to_linebuf(eval, "\n"); -+ append_to_linebuf(eval, "\n", step_vars); - eval->pending = ipc->next; - break; - -@@ -956,7 +971,7 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, - - case XCOM: - copy_to_genbuf(eval, eval->linebuf); -- copy_to_linebuf(eval, eval->holdbuf); -+ copy_to_linebuf(eval, eval->holdbuf, step_vars); - copy_to_holdbuf(eval, eval->genbuf); - break; - -@@ -1013,7 +1028,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); --- -2.30.2 - diff --git a/debian/patches/CVE-2022-23943-2.patch b/debian/patches/CVE-2022-23943-2.patch deleted file mode 100644 index bcf883c..0000000 --- a/debian/patches/CVE-2022-23943-2.patch +++ /dev/null @@ -1,63 +0,0 @@ -From e266bd09c313a668d7cca17a8b096d189148be49 Mon Sep 17 00:00:00 2001 -From: Ruediger Pluem <rpluem@apache.org> -Date: Wed, 9 Mar 2022 07:41:40 +0000 -Subject: [PATCH] Merge r1898735 from trunk: - -* Improve the logic flow - -Reviewed by: rpluem, covener, ylavic - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1898772 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/filters/mod_sed.c | 30 +++++++++++++++++++----------- - 1 file changed, 19 insertions(+), 11 deletions(-) - -diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c -index 7092dd5e7f..4bdb4ce33a 100644 ---- a/modules/filters/mod_sed.c -+++ b/modules/filters/mod_sed.c -@@ -168,21 +168,29 @@ static apr_status_t sed_write_output(void *dummy, char *buf, apr_size_t 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 { --- -2.30.2 - diff --git a/debian/patches/CVE-2022-26377.patch b/debian/patches/CVE-2022-26377.patch deleted file mode 100644 index af59776..0000000 --- a/debian/patches/CVE-2022-26377.patch +++ /dev/null @@ -1,39 +0,0 @@ -From f7f15f3d8bfe3032926c8c39eb8434529f680bd4 Mon Sep 17 00:00:00 2001 -From: Yann Ylavic <ylavic@apache.org> -Date: Wed, 1 Jun 2022 13:48:21 +0000 -Subject: [PATCH] mod_proxy_ajp: T-E has precedence over C-L. - -Merge r1901521 from trunk. -Submitted by: rpluem - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901522 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/f7f15f3d8bfe3032926c8c39eb8434529f680bd4 ---- - modules/proxy/mod_proxy_ajp.c | 15 ++++++++++++--- - 1 file changed, 12 insertions(+), 3 deletions(-) - ---- a/modules/proxy/mod_proxy_ajp.c -+++ b/modules/proxy/mod_proxy_ajp.c -@@ -245,9 +245,18 @@ - /* read the first bloc of data */ - input_brigade = apr_brigade_create(p, r->connection->bucket_alloc); - tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); -- if (tenc && (strcasecmp(tenc, "chunked") == 0)) { -- /* The AJP protocol does not want body data yet */ -- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00870) "request is chunked"); -+ if (tenc) { -+ if (strcasecmp(tenc, "chunked") == 0) { -+ /* The AJP protocol does not want body data yet */ -+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00870) -+ "request is chunked"); -+ } -+ else { -+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396) -+ "%s Transfer-Encoding is not supported", -+ tenc); -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } - } else { - /* Get client provided Content-Length header */ - content_length = get_content_length(r); diff --git a/debian/patches/CVE-2022-28614.patch b/debian/patches/CVE-2022-28614.patch deleted file mode 100644 index fdd8f6b..0000000 --- a/debian/patches/CVE-2022-28614.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 8c14927162cf3b4f810683e1c5505e9ef9e1f123 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Wed, 1 Jun 2022 12:34:16 +0000 -Subject: [PATCH] Merge r1901500 from trunk: - -handle large writes in ap_rputs - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901501 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/8c14927162cf3b4f810683e1c5505e9ef9e1f123 ---- - include/http_protocol.h | 22 +++++++++++++++++++++- - server/protocol.c | 3 +++ - 2 files changed, 24 insertions(+), 1 deletion(-) - -diff --git a/include/http_protocol.h b/include/http_protocol.h -index 20bd202226..94c481e5f4 100644 ---- a/include/http_protocol.h -+++ b/include/http_protocol.h -@@ -475,7 +475,27 @@ AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r); - */ - static APR_INLINE int ap_rputs(const char *str, request_rec *r) - { -- return ap_rwrite(str, (int)strlen(str), r); -+ apr_size_t len; -+ -+ len = strlen(str); -+ -+ for (;;) { -+ if (len <= INT_MAX) { -+ return ap_rwrite(str, (int)len, r); -+ } -+ else { -+ int rc; -+ -+ rc = ap_rwrite(str, INT_MAX, r); -+ if (rc < 0) { -+ return rc; -+ } -+ else { -+ str += INT_MAX; -+ len -= INT_MAX; -+ } -+ } -+ } - } - - /** -diff --git a/server/protocol.c b/server/protocol.c -index 298f61e1fb..7adc7f75c1 100644 ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -2128,6 +2128,9 @@ AP_DECLARE(int) ap_rputc(int c, request_rec *r) - - AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r) - { -+ if (nbyte < 0) -+ return -1; -+ - if (r->connection->aborted) - return -1; - --- -2.30.2 - diff --git a/debian/patches/CVE-2022-28615.patch b/debian/patches/CVE-2022-28615.patch deleted file mode 100644 index 2c15157..0000000 --- a/debian/patches/CVE-2022-28615.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 6503d09ab51047554c384a6d03646ce1a8848120 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Wed, 1 Jun 2022 12:21:45 +0000 -Subject: [PATCH] Merge r1901494 from trunk: - -fix types - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901495 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/6503d09ab51047554c384a6d03646ce1a8848120 ---- - server/util.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/server/util.c -+++ b/server/util.c -@@ -186,7 +186,7 @@ - */ - AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) - { -- int x, y; -+ apr_size_t x, y; - - for (x = 0, y = 0; expected[y]; ++y, ++x) { - if ((!str[x]) && (expected[y] != '*')) -@@ -210,7 +210,7 @@ - - AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected) - { -- int x, y; -+ apr_size_t x, y; - - for (x = 0, y = 0; expected[y]; ++y, ++x) { - if (!str[x] && expected[y] != '*') diff --git a/debian/patches/CVE-2022-29404.patch b/debian/patches/CVE-2022-29404.patch deleted file mode 100644 index 259e920..0000000 --- a/debian/patches/CVE-2022-29404.patch +++ /dev/null @@ -1,82 +0,0 @@ -From ce259c4061905bf834f9af51c92456cfe8335ddc Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Wed, 1 Jun 2022 12:31:48 +0000 -Subject: [PATCH] Merge r1901497 from trunk: - -use a liberal default limit for LimitRequestBody of 1GB - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901499 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/ce259c4061905bf834f9af51c92456cfe8335ddc ---- - modules/http/http_filters.c | 6 ++++++ - modules/proxy/mod_proxy_http.c | 14 -------------- - server/core.c | 2 +- - 3 files changed, 7 insertions(+), 15 deletions(-) - ---- a/modules/http/http_filters.c -+++ b/modules/http/http_filters.c -@@ -1657,6 +1657,7 @@ - { - const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); - const char *lenp = apr_table_get(r->headers_in, "Content-Length"); -+ apr_off_t limit_req_body = ap_get_limit_req_body(r); - - r->read_body = read_policy; - r->read_chunked = 0; -@@ -1695,6 +1696,11 @@ - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } - -+ if (limit_req_body > 0 && (r->remaining > limit_req_body)) { -+ /* will be logged when the body is discarded */ -+ return HTTP_REQUEST_ENTITY_TOO_LARGE; -+ } -+ - #ifdef AP_DEBUG - { - /* Make sure ap_getline() didn't leave any droppings. */ ---- a/server/core.c -+++ b/server/core.c -@@ -61,7 +61,7 @@ - - /* LimitRequestBody handling */ - #define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1) --#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0) -+#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 1<<30) /* 1GB */ - - /* LimitXMLRequestBody handling */ - #define AP_LIMIT_UNSET ((long) -1) ---- a/modules/proxy/mod_proxy_http.c -+++ b/modules/proxy/mod_proxy_http.c -@@ -512,12 +512,9 @@ - apr_bucket *e; - apr_off_t bytes, bytes_spooled = 0, fsize = 0; - apr_file_t *tmpfile = NULL; -- apr_off_t limit; - - body_brigade = apr_brigade_create(p, bucket_alloc); - -- limit = ap_get_limit_req_body(r); -- - while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) - { - /* If this brigade contains EOS, either stop or remove it. */ -@@ -532,17 +529,6 @@ - apr_brigade_length(input_brigade, 1, &bytes); - - if (bytes_spooled + bytes > MAX_MEM_SPOOL) { -- /* -- * LimitRequestBody does not affect Proxy requests (Should it?). -- * Let it take effect if we decide to store the body in a -- * temporary file on disk. -- */ -- if (limit && (bytes_spooled + bytes > limit)) { -- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088) -- "Request body is larger than the configured " -- "limit of %" APR_OFF_T_FMT, limit); -- return HTTP_REQUEST_ENTITY_TOO_LARGE; -- } - /* can't spool any more in memory; write latest brigade to disk */ - if (tmpfile == NULL) { - const char *temp_dir; diff --git a/debian/patches/CVE-2022-30522.patch b/debian/patches/CVE-2022-30522.patch deleted file mode 100644 index 5ad124e..0000000 --- a/debian/patches/CVE-2022-30522.patch +++ /dev/null @@ -1,561 +0,0 @@ -From db47781128e42bd49f55076665b3f6ca4e2bc5e2 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Wed, 1 Jun 2022 12:50:40 +0000 -Subject: [PATCH] Merge r1901506 from trunk: - -limit mod_sed memory use - -Resync mod_sed.c with trunk due to merge conflicts. - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901509 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/db47781128e42bd49f55076665b3f6ca4e2bc5e2 ---- - modules/filters/mod_sed.c | 75 ++++++++---------- - modules/filters/sed1.c | 158 +++++++++++++++++++++++++++----------- - 2 files changed, 147 insertions(+), 86 deletions(-) - -diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c -index 4bdb4ce33a..12cb04a20f 100644 ---- a/modules/filters/mod_sed.c -+++ b/modules/filters/mod_sed.c -@@ -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) - { -@@ -277,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; -@@ -302,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. -@@ -326,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); -@@ -433,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 -@@ -475,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/sed1.c b/modules/filters/sed1.c -index 67a8d06515..047f49ba13 100644 ---- a/modules/filters/sed1.c -+++ b/modules/filters/sed1.c -@@ -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, -+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; - apr_size_t spendsize = 0; -- if (*cursize >= newsize) -- return; -+ 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,63 +124,77 @@ 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, apr_size_t 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, apr_size_t 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, apr_size_t 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, apr_size_t len) -+static apr_status_t appendmem_to_linebuf(sed_eval_t *eval, const char* sz, apr_size_t 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) - { - 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) { -@@ -189,68 +208,84 @@ static void append_to_linebuf(sed_eval_t *eval, const char* sz, - 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, step_vars); -+ 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) - { - 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) - { - 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) - { - 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; - } - - /* -@@ -397,6 +432,7 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz - } - - while (bufsz) { -+ apr_status_t rc = 0; - char *n; - apr_size_t llen; - -@@ -411,7 +447,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz - 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'; -@@ -426,7 +465,10 @@ apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, apr_size_t bufsz - - /* 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; -@@ -448,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; -@@ -457,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, "", NULL); -+ rc = append_to_linebuf(eval, "", NULL); -+ if (rc != APR_SUCCESS) { -+ return rc; -+ } - } - - *eval->lspend = '\0'; -@@ -655,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++; -@@ -675,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, step_vars); -+ rv = append_to_genbuf(eval, lp, &sp); -+ if (rv != APR_SUCCESS) { -+ return rv; -+ } -+ rv = copy_to_linebuf(eval, eval->genbuf, step_vars); - return rv; - } - -@@ -695,7 +751,10 @@ static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2) - 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; -@@ -750,7 +809,8 @@ static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc, - } - - p1++; -- copy_to_linebuf(eval, p1, step_vars); -+ rv = copy_to_linebuf(eval, p1, step_vars); -+ if (rv != APR_SUCCESS) return rv; - eval->jflag++; - break; - -@@ -760,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, step_vars); -+ rv = copy_to_linebuf(eval, eval->holdbuf, step_vars); -+ if (rv != APR_SUCCESS) return rv; - break; - - case CGCOM: -- append_to_linebuf(eval, "\n", step_vars); -- append_to_linebuf(eval, eval->holdbuf, step_vars); -+ 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: -@@ -896,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", step_vars); -+ rv = append_to_linebuf(eval, "\n", step_vars); -+ if (rv != APR_SUCCESS) return rv; - eval->pending = ipc->next; - break; - -@@ -970,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, step_vars); -- 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: --- -2.30.2 - diff --git a/debian/patches/CVE-2022-30556.patch b/debian/patches/CVE-2022-30556.patch deleted file mode 100644 index f9b541d..0000000 --- a/debian/patches/CVE-2022-30556.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 3a561759fcb37af179585adb8478922dc9bc6a85 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Wed, 1 Jun 2022 12:36:39 +0000 -Subject: [PATCH] Merge r1901502 from trunk: - -use filters consistently - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901503 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/3a561759fcb37af179585adb8478922dc9bc6a85 ---- - modules/lua/lua_request.c | 144 ++++++++++++++------------------------ - 1 file changed, 53 insertions(+), 91 deletions(-) - -diff --git a/modules/lua/lua_request.c b/modules/lua/lua_request.c -index a3e3b613bc..2ec453e86b 100644 ---- a/modules/lua/lua_request.c -+++ b/modules/lua/lua_request.c -@@ -2227,23 +2227,20 @@ static int lua_websocket_greet(lua_State *L) - return 0; - } - --static apr_status_t lua_websocket_readbytes(conn_rec* c, char* buffer, -- apr_off_t len) -+static apr_status_t lua_websocket_readbytes(conn_rec* c, -+ apr_bucket_brigade *brigade, -+ char* buffer, apr_off_t len) - { -- apr_bucket_brigade *brigade = apr_brigade_create(c->pool, c->bucket_alloc); -+ apr_size_t delivered; - apr_status_t rv; -+ - rv = ap_get_brigade(c->input_filters, brigade, AP_MODE_READBYTES, - APR_BLOCK_READ, len); - if (rv == APR_SUCCESS) { -- if (!APR_BRIGADE_EMPTY(brigade)) { -- apr_bucket* bucket = APR_BRIGADE_FIRST(brigade); -- const char* data = NULL; -- apr_size_t data_length = 0; -- rv = apr_bucket_read(bucket, &data, &data_length, APR_BLOCK_READ); -- if (rv == APR_SUCCESS) { -- memcpy(buffer, data, len); -- } -- apr_bucket_delete(bucket); -+ delivered = len; -+ rv = apr_brigade_flatten(brigade, buffer, &delivered); -+ if ((rv == APR_SUCCESS) && (delivered < len)) { -+ rv = APR_INCOMPLETE; - } - } - apr_brigade_cleanup(brigade); -@@ -2273,35 +2270,28 @@ static int lua_websocket_peek(lua_State *L) - - static int lua_websocket_read(lua_State *L) - { -- apr_socket_t *sock; - apr_status_t rv; - int do_read = 1; - int n = 0; -- apr_size_t len = 1; - apr_size_t plen = 0; - unsigned short payload_short = 0; - apr_uint64_t payload_long = 0; - unsigned char *mask_bytes; - char byte; -- int plaintext; -- -- -+ apr_bucket_brigade *brigade; -+ conn_rec* c; -+ - request_rec *r = ap_lua_check_request_rec(L, 1); -- plaintext = ap_lua_ssl_is_https(r->connection) ? 0 : 1; -+ c = r->connection; - -- - mask_bytes = apr_pcalloc(r->pool, 4); -- sock = ap_get_conn_socket(r->connection); -+ -+ brigade = apr_brigade_create(r->pool, c->bucket_alloc); - - while (do_read) { - do_read = 0; - /* Get opcode and FIN bit */ -- if (plaintext) { -- rv = apr_socket_recv(sock, &byte, &len); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, &byte, 1); -- } -+ rv = lua_websocket_readbytes(c, brigade, &byte, 1); - if (rv == APR_SUCCESS) { - unsigned char ubyte, fin, opcode, mask, payload; - ubyte = (unsigned char)byte; -@@ -2311,12 +2301,7 @@ static int lua_websocket_read(lua_State *L) - opcode = ubyte & 0xf; - - /* Get the payload length and mask bit */ -- if (plaintext) { -- rv = apr_socket_recv(sock, &byte, &len); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, &byte, 1); -- } -+ rv = lua_websocket_readbytes(c, brigade, &byte, 1); - if (rv == APR_SUCCESS) { - ubyte = (unsigned char)byte; - /* Mask is the first bit */ -@@ -2327,40 +2312,25 @@ static int lua_websocket_read(lua_State *L) - - /* Extended payload? */ - if (payload == 126) { -- len = 2; -- if (plaintext) { -- /* XXX: apr_socket_recv does not receive len bits, only up to len bits! */ -- rv = apr_socket_recv(sock, (char*) &payload_short, &len); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, -- (char*) &payload_short, 2); -- } -- payload_short = ntohs(payload_short); -+ rv = lua_websocket_readbytes(c, brigade, -+ (char*) &payload_short, 2); - -- if (rv == APR_SUCCESS) { -- plen = payload_short; -- } -- else { -+ if (rv != APR_SUCCESS) { - return 0; - } -+ -+ plen = ntohs(payload_short); - } - /* Super duper extended payload? */ - if (payload == 127) { -- len = 8; -- if (plaintext) { -- rv = apr_socket_recv(sock, (char*) &payload_long, &len); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, -- (char*) &payload_long, 8); -- } -- if (rv == APR_SUCCESS) { -- plen = ap_ntoh64(&payload_long); -- } -- else { -+ rv = lua_websocket_readbytes(c, brigade, -+ (char*) &payload_long, 8); -+ -+ if (rv != APR_SUCCESS) { - return 0; - } -+ -+ plen = ap_ntoh64(&payload_long); - } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03210) - "Websocket: Reading %" APR_SIZE_T_FMT " (%s) bytes, masking is %s. %s", -@@ -2369,46 +2339,27 @@ static int lua_websocket_read(lua_State *L) - mask ? "on" : "off", - fin ? "This is a final frame" : "more to follow"); - if (mask) { -- len = 4; -- if (plaintext) { -- rv = apr_socket_recv(sock, (char*) mask_bytes, &len); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, -- (char*) mask_bytes, 4); -- } -+ rv = lua_websocket_readbytes(c, brigade, -+ (char*) mask_bytes, 4); -+ - if (rv != APR_SUCCESS) { - return 0; - } - } - if (plen < (HUGE_STRING_LEN*1024) && plen > 0) { - apr_size_t remaining = plen; -- apr_size_t received; -- apr_off_t at = 0; - char *buffer = apr_palloc(r->pool, plen+1); - buffer[plen] = 0; - -- if (plaintext) { -- while (remaining > 0) { -- received = remaining; -- rv = apr_socket_recv(sock, buffer+at, &received); -- if (received > 0 ) { -- remaining -= received; -- at += received; -- } -- } -- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, -- "Websocket: Frame contained %" APR_OFF_T_FMT " bytes, pushed to Lua stack", -- at); -- } -- else { -- rv = lua_websocket_readbytes(r->connection, buffer, -- remaining); -- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, -- "Websocket: SSL Frame contained %" APR_SIZE_T_FMT " bytes, "\ -- "pushed to Lua stack", -- remaining); -+ rv = lua_websocket_readbytes(c, brigade, buffer, remaining); -+ -+ if (rv != APR_SUCCESS) { -+ return 0; - } -+ -+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, -+ "Websocket: Frame contained %" APR_SIZE_T_FMT \ -+ " bytes, pushed to Lua stack", remaining); - if (mask) { - for (n = 0; n < plen; n++) { - buffer[n] ^= mask_bytes[n%4]; -@@ -2420,14 +2371,25 @@ static int lua_websocket_read(lua_State *L) - return 2; - } - -- - /* Decide if we need to react to the opcode or not */ - if (opcode == 0x09) { /* ping */ - char frame[2]; -- plen = 2; -+ apr_bucket *b; -+ - frame[0] = 0x8A; - frame[1] = 0; -- apr_socket_send(sock, frame, &plen); /* Pong! */ -+ -+ /* Pong! */ -+ b = apr_bucket_transient_create(frame, 2, c->bucket_alloc); -+ APR_BRIGADE_INSERT_TAIL(brigade, b); -+ -+ rv = ap_pass_brigade(c->output_filters, brigade); -+ apr_brigade_cleanup(brigade); -+ -+ if (rv != APR_SUCCESS) { -+ return 0; -+ } -+ - do_read = 1; - } - } --- -2.30.2 - diff --git a/debian/patches/CVE-2022-31813.patch b/debian/patches/CVE-2022-31813.patch deleted file mode 100644 index d2bd341..0000000 --- a/debian/patches/CVE-2022-31813.patch +++ /dev/null @@ -1,242 +0,0 @@ -From 956f708b094698ac9ad570d640d4f30eb0df7305 Mon Sep 17 00:00:00 2001 -From: Stefan Eissing <icing@apache.org> -Date: Wed, 1 Jun 2022 07:51:04 +0000 -Subject: [PATCH] Merge r1901461 from trunk via #320: - - *) mod_proxy: ap_proxy_create_hdrbrgd() to clear hop-by-hop first and fixup last. - - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1901480 13f79535-47bb-0310-9956-ffa450edef68 -Origin: https://github.com/apache/httpd/commit/956f708b094698ac9ad570d640d4f30eb0df7305 ---- - modules/proxy/proxy_util.c | 153 ++++++++++++++++++++++----------------------- - 1 file changed, 77 insertions(+), 76 deletions(-) - ---- a/modules/proxy/proxy_util.c -+++ b/modules/proxy/proxy_util.c -@@ -3396,12 +3396,14 @@ - char **old_cl_val, - char **old_te_val) - { -+ int rc = OK; - conn_rec *c = r->connection; - int counter; - char *buf; -+ apr_table_t *saved_headers_in = r->headers_in; -+ const char *saved_host = apr_table_get(saved_headers_in, "Host"); - const apr_array_header_t *headers_in_array; - const apr_table_entry_t *headers_in; -- apr_table_t *saved_headers_in; - apr_bucket *e; - int do_100_continue; - conn_rec *origin = p_conn->connection; -@@ -3437,6 +3439,52 @@ - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); -+ -+ /* -+ * Make a copy on r->headers_in for the request we make to the backend, -+ * modify the copy in place according to our configuration and connection -+ * handling, use it to fill in the forwarded headers' brigade, and finally -+ * restore the saved/original ones in r->headers_in. -+ * -+ * Note: We need to take r->pool for apr_table_copy as the key / value -+ * pairs in r->headers_in have been created out of r->pool and -+ * p might be (and actually is) a longer living pool. -+ * This would trigger the bad pool ancestry abort in apr_table_copy if -+ * apr is compiled with APR_POOL_DEBUG. -+ * -+ * icing: if p indeed lives longer than r->pool, we should allocate -+ * all new header values from r->pool as well and avoid leakage. -+ */ -+ r->headers_in = apr_table_copy(r->pool, saved_headers_in); -+ -+ /* Return the original Transfer-Encoding and/or Content-Length values -+ * then drop the headers, they must be set by the proxy handler based -+ * on the actual body being forwarded. -+ */ -+ if ((*old_te_val = (char *)apr_table_get(r->headers_in, -+ "Transfer-Encoding"))) { -+ apr_table_unset(r->headers_in, "Transfer-Encoding"); -+ } -+ if ((*old_cl_val = (char *)apr_table_get(r->headers_in, -+ "Content-Length"))) { -+ apr_table_unset(r->headers_in, "Content-Length"); -+ } -+ -+ /* Clear out hop-by-hop request headers not to forward */ -+ if (ap_proxy_clear_connection(r, r->headers_in) < 0) { -+ rc = HTTP_BAD_REQUEST; -+ goto cleanup; -+ } -+ -+ /* RFC2616 13.5.1 says we should strip these */ -+ apr_table_unset(r->headers_in, "Keep-Alive"); -+ apr_table_unset(r->headers_in, "Upgrade"); -+ apr_table_unset(r->headers_in, "Trailer"); -+ apr_table_unset(r->headers_in, "TE"); -+ -+ /* We used to send `Host: ` always first, so let's keep it that -+ * way. No telling which legacy backend is relying no this. -+ */ - if (dconf->preserve_host == 0) { - if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ - if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { -@@ -3458,7 +3506,7 @@ - /* don't want to use r->hostname, as the incoming header might have a - * port attached - */ -- const char* hostname = apr_table_get(r->headers_in,"Host"); -+ const char* hostname = saved_host; - if (!hostname) { - hostname = r->server->server_hostname; - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) -@@ -3472,21 +3520,7 @@ - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); -- -- /* -- * Save the original headers in here and restore them when leaving, since -- * we will apply proxy purpose only modifications (eg. clearing hop-by-hop -- * headers, add Via or X-Forwarded-* or Expect...), whereas the originals -- * will be needed later to prepare the correct response and logging. -- * -- * Note: We need to take r->pool for apr_table_copy as the key / value -- * pairs in r->headers_in have been created out of r->pool and -- * p might be (and actually is) a longer living pool. -- * This would trigger the bad pool ancestry abort in apr_table_copy if -- * apr is compiled with APR_POOL_DEBUG. -- */ -- saved_headers_in = r->headers_in; -- r->headers_in = apr_table_copy(r->pool, saved_headers_in); -+ apr_table_unset(r->headers_in, "Host"); - - /* handle Via */ - if (conf->viaopt == via_block) { -@@ -3561,8 +3595,6 @@ - */ - if (dconf->add_forwarded_headers) { - if (PROXYREQ_REVERSE == r->proxyreq) { -- const char *buf; -- - /* Add X-Forwarded-For: so that the upstream has a chance to - * determine, where the original request came from. - */ -@@ -3572,8 +3604,9 @@ - /* Add X-Forwarded-Host: so that upstream knows what the - * original request hostname was. - */ -- if ((buf = apr_table_get(r->headers_in, "Host"))) { -- apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); -+ if (saved_host) { -+ apr_table_mergen(r->headers_in, "X-Forwarded-Host", -+ saved_host); - } - - /* Add X-Forwarded-Server: so that upstream knows what the -@@ -3585,67 +3618,37 @@ - } - } - -- proxy_run_fixups(r); -- if (ap_proxy_clear_connection(r, r->headers_in) < 0) { -- return HTTP_BAD_REQUEST; -+ /* Do we want to strip Proxy-Authorization ? -+ * If we haven't used it, then NO -+ * If we have used it then MAYBE: RFC2616 says we MAY propagate it. -+ * So let's make it configurable by env. -+ */ -+ if (r->user != NULL /* we've authenticated */ -+ && !apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { -+ apr_table_unset(r->headers_in, "Proxy-Authorization"); - } - -+ /* for sub-requests, ignore freshness/expiry headers */ -+ if (r->main) { -+ apr_table_unset(r->headers_in, "If-Match"); -+ apr_table_unset(r->headers_in, "If-Modified-Since"); -+ apr_table_unset(r->headers_in, "If-Range"); -+ apr_table_unset(r->headers_in, "If-Unmodified-Since"); -+ apr_table_unset(r->headers_in, "If-None-Match"); -+ } -+ -+ /* run hook to fixup the request we are about to send */ -+ proxy_run_fixups(r); -+ - /* send request headers */ - headers_in_array = apr_table_elts(r->headers_in); - headers_in = (const apr_table_entry_t *) headers_in_array->elts; - for (counter = 0; counter < headers_in_array->nelts; counter++) { - if (headers_in[counter].key == NULL -- || headers_in[counter].val == NULL -- -- /* Already sent */ -- || !strcasecmp(headers_in[counter].key, "Host") -- -- /* Clear out hop-by-hop request headers not to send -- * RFC2616 13.5.1 says we should strip these headers -- */ -- || !strcasecmp(headers_in[counter].key, "Keep-Alive") -- || !strcasecmp(headers_in[counter].key, "TE") -- || !strcasecmp(headers_in[counter].key, "Trailer") -- || !strcasecmp(headers_in[counter].key, "Upgrade") -- -- ) { -- continue; -- } -- /* Do we want to strip Proxy-Authorization ? -- * If we haven't used it, then NO -- * If we have used it then MAYBE: RFC2616 says we MAY propagate it. -- * So let's make it configurable by env. -- */ -- if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) { -- if (r->user != NULL) { /* we've authenticated */ -- if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { -- continue; -- } -- } -- } -- -- /* Skip Transfer-Encoding and Content-Length for now. -- */ -- if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { -- *old_te_val = headers_in[counter].val; -- continue; -- } -- if (!strcasecmp(headers_in[counter].key, "Content-Length")) { -- *old_cl_val = headers_in[counter].val; -+ || headers_in[counter].val == NULL) { - continue; - } - -- /* for sub-requests, ignore freshness/expiry headers */ -- if (r->main) { -- if ( !strcasecmp(headers_in[counter].key, "If-Match") -- || !strcasecmp(headers_in[counter].key, "If-Modified-Since") -- || !strcasecmp(headers_in[counter].key, "If-Range") -- || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") -- || !strcasecmp(headers_in[counter].key, "If-None-Match")) { -- continue; -- } -- } -- - buf = apr_pstrcat(p, headers_in[counter].key, ": ", - headers_in[counter].val, CRLF, - NULL); -@@ -3654,11 +3657,9 @@ - APR_BRIGADE_INSERT_TAIL(header_brigade, e); - } - -- /* Restore the original headers in (see comment above), -- * we won't modify them anymore. -- */ -+cleanup: - r->headers_in = saved_headers_in; -- return OK; -+ return rc; - } - - PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, diff --git a/debian/patches/CVE-2022-36760.patch b/debian/patches/CVE-2022-36760.patch deleted file mode 100644 index ebeefa3..0000000 --- a/debian/patches/CVE-2022-36760.patch +++ /dev/null @@ -1,27 +0,0 @@ -From d93e61e3e9622bacff746772cb9c97fdcaed8baf Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Tue, 10 Jan 2023 13:20:55 +0000 -Subject: [PATCH] Merge r1906540 from trunk: - -cleanup on error - - -Reviewed By: rpluem, gbechis, covener - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906542 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/proxy/mod_proxy_ajp.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/modules/proxy/mod_proxy_ajp.c -+++ b/modules/proxy/mod_proxy_ajp.c -@@ -255,6 +255,8 @@ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396) - "%s Transfer-Encoding is not supported", - tenc); -+ /* We had a failure: Close connection to backend */ -+ conn->close = 1; - return HTTP_INTERNAL_SERVER_ERROR; - } - } else { diff --git a/debian/patches/CVE-2022-37436.patch b/debian/patches/CVE-2022-37436.patch deleted file mode 100644 index a123959..0000000 --- a/debian/patches/CVE-2022-37436.patch +++ /dev/null @@ -1,125 +0,0 @@ -From 8b6d55f6a047acf62675e32606b037f5eea8ccc7 Mon Sep 17 00:00:00 2001 -From: Eric Covener <covener@apache.org> -Date: Tue, 10 Jan 2023 13:20:09 +0000 -Subject: [PATCH] Merge r1906539 from trunk: - -fail on bad header - -Submitted By: covener -Reviewed By: covener, rpluem, gbechis - - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906541 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/proxy/mod_proxy_http.c | 46 ++++++++++++++++++++-------------- - server/protocol.c | 2 ++ - 2 files changed, 29 insertions(+), 19 deletions(-) - ---- a/modules/proxy/mod_proxy_http.c -+++ b/modules/proxy/mod_proxy_http.c -@@ -1011,7 +1011,7 @@ - * any sense at all, since we depend on buffer still containing - * what was read by ap_getline() upon return. - */ --static void ap_proxy_read_headers(request_rec *r, request_rec *rr, -+static apr_status_t ap_proxy_read_headers(request_rec *r, request_rec *rr, - char *buffer, int size, - conn_rec *c, int *pread_len) - { -@@ -1043,19 +1043,26 @@ - rc = ap_proxygetline(tmp_bb, buffer, size, rr, - AP_GETLINE_FOLD | AP_GETLINE_NOSPC_EOL, &len); - -- if (len <= 0) -- break; - -- if (APR_STATUS_IS_ENOSPC(rc)) { -- /* The header could not fit in the provided buffer, warn. -- * XXX: falls through with the truncated header, 5xx instead? -- */ -- int trunc = (len > 128 ? 128 : len) / 2; -- ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) -- "header size is over the limit allowed by " -- "ResponseFieldSize (%d bytes). " -- "Bad response header: '%.*s[...]%s'", -- size, trunc, buffer, buffer + len - trunc); -+ if (rc != APR_SUCCESS) { -+ if (APR_STATUS_IS_ENOSPC(rc)) { -+ int trunc = (len > 128 ? 128 : len) / 2; -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) -+ "header size is over the limit allowed by " -+ "ResponseFieldSize (%d bytes). " -+ "Bad response header: '%.*s[...]%s'", -+ size, trunc, buffer, buffer + len - trunc); -+ } -+ else { -+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10404) -+ "Error reading headers from backend"); -+ } -+ r->headers_out = NULL; -+ return rc; -+ } -+ -+ if (len <= 0) { -+ break; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); -@@ -1078,7 +1085,7 @@ - if (psc->badopt == bad_error) { - /* Nope, it wasn't even an extra HTTP header. Give up. */ - r->headers_out = NULL; -- return; -+ return APR_EINVAL; - } - else if (psc->badopt == bad_body) { - /* if we've already started loading headers_out, then -@@ -1092,13 +1099,13 @@ - "in headers returned by %s (%s)", - r->uri, r->method); - *pread_len = len; -- return; -+ return APR_SUCCESS; - } - else { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01099) - "No HTTP headers returned by %s (%s)", - r->uri, r->method); -- return; -+ return APR_SUCCESS; - } - } - } -@@ -1128,6 +1135,7 @@ - process_proxy_header(r, dconf, buffer, value); - saw_headers = 1; - } -+ return APR_SUCCESS; - } - - -@@ -1398,10 +1406,10 @@ - "Set-Cookie", NULL); - - /* shove the headers direct into r->headers_out */ -- ap_proxy_read_headers(r, backend->r, buffer, response_field_size, origin, -- &pread_len); -+ rc = ap_proxy_read_headers(r, backend->r, buffer, response_field_size, -+ origin, &pread_len); - -- if (r->headers_out == NULL) { -+ if (rc != APR_SUCCESS || r->headers_out == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01106) - "bad HTTP/%d.%d header returned by %s (%s)", - major, minor, r->uri, r->method); ---- a/server/protocol.c -+++ b/server/protocol.c -@@ -508,6 +508,8 @@ - /* PR#43039: We shouldn't accept NULL bytes within the line */ - bytes_handled = strlen(*s); - if (bytes_handled < *read) { -+ ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf, -+ "NULL bytes in header", *s, *read, 0); - *read = bytes_handled; - if (rv == APR_SUCCESS) { - rv = APR_EINVAL; diff --git a/debian/patches/build_suexec-custom.patch b/debian/patches/build_suexec-custom.patch index e03d54b..a509cd5 100644 --- a/debian/patches/build_suexec-custom.patch +++ b/debian/patches/build_suexec-custom.patch @@ -4,7 +4,7 @@ Author: Stefan Fritsch <sf@debian.org> Last-Update: 2012-02-25 --- a/Makefile.in +++ b/Makefile.in -@@ -272,23 +272,26 @@ +@@ -293,23 +293,26 @@ install-suexec: install-suexec-$(INSTALL_SUEXEC) install-suexec-binary: diff --git a/debian/patches/customize_apxs.patch b/debian/patches/customize_apxs.patch index 9c75ff1..281b910 100644 --- a/debian/patches/customize_apxs.patch +++ b/debian/patches/customize_apxs.patch @@ -8,7 +8,7 @@ Last-Update: 2012-03-17 --- a/support/apxs.in +++ b/support/apxs.in -@@ -38,7 +38,7 @@ +@@ -48,7 +48,7 @@ my $CFG_TARGET = get_vars("progname"); my $CFG_SYSCONFDIR = get_vars("sysconfdir"); my $CFG_CFLAGS = join ' ', map { get_vars($_) } @@ -16,8 +16,8 @@ Last-Update: 2012-03-17 + qw(SHLTCFLAGS CFLAGS CPPFLAGS NOTEST_CPPFLAGS EXTRA_CPPFLAGS EXTRA_CFLAGS); my $CFG_LDFLAGS = join ' ', map { get_vars($_) } qw(LDFLAGS NOTEST_LDFLAGS SH_LDFLAGS); - my $includedir = get_vars("includedir"); -@@ -49,7 +49,7 @@ + my $includedir = $destdir . get_vars("includedir"); +@@ -59,7 +59,7 @@ my $sbindir = get_vars("sbindir"); my $CFG_SBINDIR = eval qq("$sbindir"); my $ltflags = $ENV{'LTFLAGS'}; @@ -26,7 +26,7 @@ Last-Update: 2012-03-17 my %internal_vars = map {$_ => 1} qw(TARGET CC CFLAGS CFLAGS_SHLIB LD_SHLIB LDFLAGS_SHLIB LIBS_SHLIB -@@ -276,6 +276,7 @@ +@@ -286,6 +286,7 @@ $data =~ s|%TARGET%|$CFG_TARGET|sg; $data =~ s|%PREFIX%|$prefix|sg; $data =~ s|%INSTALLBUILDDIR%|$installbuilddir|sg; @@ -34,7 +34,7 @@ Last-Update: 2012-03-17 my ($mkf, $mods, $src) = ($data =~ m|^(.+)-=#=-\n(.+)-=#=-\n(.+)|s); -@@ -428,7 +429,7 @@ +@@ -438,7 +439,7 @@ $la =~ s|\.c$|.la|; my $o = $s; $o =~ s|\.c$|.o|; @@ -43,7 +43,7 @@ Last-Update: 2012-03-17 unshift(@objs, $lo); } -@@ -469,7 +470,7 @@ +@@ -479,7 +480,7 @@ $opt .= " -rpath $CFG_LIBEXECDIR -module -avoid-version $apr_ldflags"; } @@ -52,16 +52,16 @@ Last-Update: 2012-03-17 # execute the commands &execute_cmds(@cmds); -@@ -503,7 +504,7 @@ +@@ -513,7 +514,7 @@ if ($opt_i) { - push(@cmds, "$installbuilddir/instdso.sh SH_LIBTOOL='" . + push(@cmds, $destdir . "$installbuilddir/instdso.sh SH_LIBTOOL='" . "$libtool' $f $CFG_LIBEXECDIR"); - push(@cmds, "chmod 755 $CFG_LIBEXECDIR/$t"); + push(@cmds, "chmod 644 $CFG_LIBEXECDIR/$t"); } # determine module symbolname and filename -@@ -539,10 +540,11 @@ +@@ -549,10 +550,11 @@ $filename = "mod_${name}.c"; } my $dir = $CFG_LIBEXECDIR; @@ -75,7 +75,7 @@ Last-Update: 2012-03-17 } # execute the commands -@@ -550,108 +552,35 @@ +@@ -560,108 +562,35 @@ # activate module via LoadModule/AddModule directive if ($opt_a or $opt_A) { @@ -207,7 +207,7 @@ Last-Update: 2012-03-17 } } -@@ -671,8 +600,8 @@ +@@ -681,8 +610,8 @@ ## builddir=. diff --git a/debian/patches/fhs_compliance.patch b/debian/patches/fhs_compliance.patch index 00f8f71..986d8bc 100644 --- a/debian/patches/fhs_compliance.patch +++ b/debian/patches/fhs_compliance.patch @@ -1,33 +1,31 @@ Description: Fix up FHS file locations for apache2 droppings. Forwarded: not-needed Author: Adam Conrad <adconrad@0c3.net> -Last-Update: 2012-02-25 +Reviewed-By: Yadd <yadd@debian.org> +Last-Update: 2023-10-19 + --- a/configure +++ b/configure -@@ -39688,17 +39688,17 @@ +@@ -42812,13 +42812,13 @@ + ap_prefix="${ap_cur}" - cat >>confdefs.h <<_ACEOF --#define HTTPD_ROOT "${ap_prefix}" -+#define HTTPD_ROOT "/etc/apache2" - _ACEOF +-printf "%s\n" "#define HTTPD_ROOT \"${ap_prefix}\"" >>confdefs.h ++printf "%s\n" "#define HTTPD_ROOT \"/etc/apache2\"" >>confdefs.h - cat >>confdefs.h <<_ACEOF --#define SERVER_CONFIG_FILE "${rel_sysconfdir}/${progname}.conf" -+#define SERVER_CONFIG_FILE "${progname}.conf" - _ACEOF +-printf "%s\n" "#define SERVER_CONFIG_FILE \"${rel_sysconfdir}/${progname}.conf\"" >>confdefs.h ++printf "%s\n" "#define SERVER_CONFIG_FILE \"${progname}.conf\"" >>confdefs.h - cat >>confdefs.h <<_ACEOF --#define AP_TYPES_CONFIG_FILE "${rel_sysconfdir}/mime.types" -+#define AP_TYPES_CONFIG_FILE "mime.types" - _ACEOF +-printf "%s\n" "#define AP_TYPES_CONFIG_FILE \"${rel_sysconfdir}/mime.types\"" >>confdefs.h ++printf "%s\n" "#define AP_TYPES_CONFIG_FILE \"mime.types\"" >>confdefs.h + perlbin=`$ac_aux_dir/PrintPath perl` --- a/configure.in +++ b/configure.in -@@ -871,11 +871,11 @@ +@@ -928,11 +928,11 @@ echo $MODLIST | $AWK -f $srcdir/build/build-modules-c.awk > modules.c APR_EXPAND_VAR(ap_prefix, $prefix) @@ -53,12 +51,16 @@ Last-Update: 2012-02-25 #endif /* AP_CONFIG_LAYOUT_H */ --- a/include/httpd.h +++ b/include/httpd.h -@@ -109,7 +109,7 @@ - #define DOCUMENT_LOCATION HTTPD_ROOT "/docs" +@@ -107,10 +107,10 @@ + #ifndef DOCUMENT_LOCATION + #ifdef OS2 + /* Set default for OS/2 file system */ +-#define DOCUMENT_LOCATION HTTPD_ROOT "/docs" ++#define DOCUMENT_LOCATION "/var/www/html" #else /* Set default for non OS/2 file system */ -#define DOCUMENT_LOCATION HTTPD_ROOT "/htdocs" -+#define DOCUMENT_LOCATION "/var/www/html" ++#define DOCUMENT_LOCATION "/var/www/html" #endif #endif /* DOCUMENT_LOCATION */ diff --git a/debian/patches/fix-macro.patch b/debian/patches/fix-macro.patch new file mode 100644 index 0000000..ea83a64 --- /dev/null +++ b/debian/patches/fix-macro.patch @@ -0,0 +1,160 @@ +Description: add macro_ignore_empty and macro_ignore_bad_nesting parameters +Author: Upstream authors +Origin: upstream, https://svn.apache.org/viewvc/httpd/httpd/trunk/modules/core/mod_macro.c?r1=1770843&r2=1770842&pathrev=1770843 +Forwarded: not-needed +Reviewed-By: Yadd <yadd@debian.org> +Last-Update: 2021-10-25 + +--- a/modules/core/mod_macro.c ++++ b/modules/core/mod_macro.c +@@ -49,6 +49,10 @@ + + /********************************************************** MACRO MANAGEMENT */ + ++/* Global warning modifiers */ ++int ignore_empty = FALSE; /* no warning about empty argument */ ++int ignore_bad_nesting = FALSE; /* no warning about bad nesting */ ++ + /* + this is a macro: name, arguments, contents, location. + */ +@@ -58,6 +62,8 @@ + apr_array_header_t *arguments; /* of char*, macro parameter names */ + apr_array_header_t *contents; /* of char*, macro body */ + char *location; /* of macro definition, for error messages */ ++ int ignore_empty; /* no warning about empty argument */ ++ int ignore_bad_nesting; /* no warning about bad nesting */ + } ap_macro_t; + + /* configuration tokens. +@@ -67,6 +73,10 @@ + #define USE_MACRO "Use" + #define UNDEF_MACRO "UndefMacro" + ++#define IGNORE_EMPTY_MACRO_FLAG "/IgnoreEmptyArgs" ++#define IGNORE_BAD_NESTING_MACRO_FLAG "/IgnoreBadNesting" ++#define IGNORE_EMPTY_MACRO_DIRECTIVE "MacroIgnoreEmptyArgs" ++#define IGNORE_BAD_NESTING_MACRO_DIRECTIVE "MacroIgnoreBadNesting" + /* + Macros are kept globally... + They are not per-server or per-directory entities. +@@ -135,7 +145,8 @@ + const char *end_token, + const char *begin_token, + const char *where, +- apr_array_header_t ** plines) ++ apr_array_header_t ** plines, ++ int ignore_nesting) + { + apr_array_header_t *lines = apr_array_make(pool, 1, sizeof(char *)); + char line[MAX_STRING_LEN]; /* sorry, but this is expected by getline:-( */ +@@ -153,7 +164,7 @@ + /* detect nesting... */ + if (!strncmp(first, "</", 2)) { + any_nesting--; +- if (any_nesting < 0) { ++ if (!ignore_nesting && (any_nesting < 0)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, + 0, NULL, APLOGNO(02793) + "bad (negative) nesting on line %d of %s", +@@ -180,7 +191,7 @@ + + macro_nesting--; + if (!macro_nesting) { +- if (any_nesting) { ++ if (!ignore_nesting && any_nesting) { + ap_log_error(APLOG_MARK, + APLOG_WARNING, 0, NULL, APLOGNO(02795) + "bad cumulated nesting (%+d) in %s", +@@ -255,6 +266,13 @@ + tab[i], i + 1, ARG_PREFIX); + } + ++ if ((tab[i][0] == '$') && (tab[i][1] == '{')) { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02805) ++ "macro '%s' (%s) " ++ "argument name '%s' (#%d) clashes with 'Define' syntax '${...}', better use '$(...)'.", ++ macro->name, macro->location, tab[i], i + 1); ++ } ++ + for (j = i + 1; j < nelts; j++) { + size_t ltabj = strlen(tab[j]); + +@@ -763,7 +781,25 @@ + where, ARG_PREFIX); + } + +- /* get macro parameters */ ++ /* get/remove macro modifiers from parameters */ ++#define CHECK_MACRO_FLAG(arg_, flag_str, flag_val) if (!strncasecmp(arg_, flag_str, strlen(flag_str))) { flag_val = TRUE; arg_ += strlen(flag_str); if (!*arg) break;} ++ while (*arg == '/') { ++ CHECK_MACRO_FLAG(arg, IGNORE_EMPTY_MACRO_FLAG, macro->ignore_empty); ++ CHECK_MACRO_FLAG(arg, IGNORE_BAD_NESTING_MACRO_FLAG, macro->ignore_bad_nesting); ++ if (*arg != ' ') { ++ char *c = ap_strchr(arg, ' '); ++ if (c) *c = '\0'; ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02804) ++ "%s: unknown flag '%s'", where, arg); ++ if (c) { ++ *c = ' '; ++ arg = c; ++ } ++ } ++ arg++; ++ } ++ ++ /* get macro parameters */ + macro->arguments = get_arguments(pool, arg); + + errmsg = check_macro_arguments(cmd->temp_pool, macro); +@@ -774,7 +810,7 @@ + + errmsg = get_lines_till_end_token(pool, cmd->config_file, + END_MACRO, BEGIN_MACRO, +- where, ¯o->contents); ++ where, ¯o->contents, ignore_bad_nesting || macro->ignore_bad_nesting); + + if (errmsg) { + return apr_psprintf(cmd->temp_pool, +@@ -860,7 +896,8 @@ + cmd->config_file->line_number, + cmd->config_file->name); + +- check_macro_use_arguments(where, replacements); ++ if (!ignore_empty && !macro->ignore_empty) ++ check_macro_use_arguments(where, replacements); + + errmsg = process_content(cmd->temp_pool, macro, replacements, + NULL, &contents); +@@ -911,6 +948,18 @@ + return NULL; + } + ++static const char *macro_ignore_empty(cmd_parms * cmd, void *dummy) ++{ ++ ignore_empty = TRUE; ++ return NULL; ++} ++ ++static const char *macro_ignore_bad_nesting(cmd_parms * cmd, void *dummy) ++{ ++ ignore_bad_nesting = TRUE; ++ return NULL; ++} ++ + /************************************************************* EXPORT MODULE */ + + /* +@@ -924,7 +973,11 @@ + AP_INIT_RAW_ARGS(USE_MACRO, use_macro, NULL, EXEC_ON_READ | OR_ALL, + "Use of a macro."), + AP_INIT_TAKE1(UNDEF_MACRO, undef_macro, NULL, EXEC_ON_READ | OR_ALL, +- "Remove a macro definition."), ++ "Remove a macro definition."), ++ AP_INIT_NO_ARGS(IGNORE_EMPTY_MACRO_DIRECTIVE, macro_ignore_empty, NULL, EXEC_ON_READ | OR_ALL, ++ "Globally ignore warnings about empty arguments."), ++ AP_INIT_NO_ARGS(IGNORE_BAD_NESTING_MACRO_DIRECTIVE, macro_ignore_bad_nesting, NULL, EXEC_ON_READ | OR_ALL, ++ "Globally ignore warnings about bad nesting."), + + {NULL} + }; diff --git a/debian/patches/import-http2-module-from-2.4.46.patch b/debian/patches/import-http2-module-from-2.4.46.patch deleted file mode 100644 index cdca37d..0000000 --- a/debian/patches/import-http2-module-from-2.4.46.patch +++ /dev/null @@ -1,7588 +0,0 @@ -Description: import http2 module from 2.4.41 - There are too many changes in http2 module to distiguish CVE-2019-9517, - CVE-2019-10082 and CVE-2019-10081 changes. -Author: Apache authors -Bug: https://security-tracker.debian.org/tracker/CVE-2019-9517 - https://security-tracker.debian.org/tracker/CVE-2019-10082 - https://security-tracker.debian.org/tracker/CVE-2019-10081 - https://security-tracker.debian.org/tracker/CVE-2020-9490 - https://security-tracker.debian.org/tracker/CVE-2020-11993 -Forwarded: not-needed -Reviewed-By: Xavier Guimard <yadd@debian.org> -Last-Update: 2020-08-25 - ---- a/modules/http2/config2.m4 -+++ b/modules/http2/config2.m4 -@@ -31,7 +31,6 @@ - h2_h2.lo dnl - h2_headers.lo dnl - h2_mplx.lo dnl --h2_ngn_shed.lo dnl - h2_push.lo dnl - h2_request.lo dnl - h2_session.lo dnl ---- a/modules/http2/h2.h -+++ b/modules/http2/h2.h -@@ -48,12 +48,12 @@ - #define H2_HEADER_PATH_LEN 5 - #define H2_CRLF "\r\n" - --/* Max data size to write so it fits inside a TLS record */ --#define H2_DATA_CHUNK_SIZE ((16*1024) - 100 - 9) -- - /* Size of the frame header itself in HTTP/2 */ - #define H2_FRAME_HDR_LEN 9 - -+/* Max data size to write so it fits inside a TLS record */ -+#define H2_DATA_CHUNK_SIZE ((16*1024) - 100 - H2_FRAME_HDR_LEN) -+ - /* Maximum number of padding bytes in a frame, rfc7540 */ - #define H2_MAX_PADLEN 256 - /* Initial default window size, RFC 7540 ch. 6.5.2 */ -@@ -138,7 +138,7 @@ - apr_table_t *headers; - - apr_time_t request_time; -- unsigned int chunked : 1; /* iff requst body needs to be forwarded as chunked */ -+ unsigned int chunked : 1; /* iff request body needs to be forwarded as chunked */ - unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */ - apr_off_t raw_bytes; /* RAW network bytes that generated this request - if known. */ - }; -@@ -162,5 +162,6 @@ - #define H2_FILTER_DEBUG_NOTE "http2-debug" - #define H2_HDR_CONFORMANCE "http2-hdr-conformance" - #define H2_HDR_CONFORMANCE_UNSAFE "unsafe" -+#define H2_PUSH_MODE_NOTE "http2-push-mode" - - #endif /* defined(__mod_h2__h2__) */ ---- a/modules/http2/h2_alt_svc.c -+++ b/modules/http2/h2_alt_svc.c -@@ -75,7 +75,7 @@ - - static int h2_alt_svc_handler(request_rec *r) - { -- const h2_config *cfg; -+ apr_array_header_t *alt_svcs; - int i; - - if (r->connection->keepalives > 0) { -@@ -87,8 +87,8 @@ - return DECLINED; - } - -- cfg = h2_config_sget(r->server); -- if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) { -+ alt_svcs = h2_config_alt_svcs(r); -+ if (r->hostname && alt_svcs && alt_svcs->nelts > 0) { - const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used"); - if (!alt_svc_used) { - /* We have alt-svcs defined and client is not already using -@@ -99,7 +99,7 @@ - const char *alt_svc = ""; - const char *svc_ma = ""; - int secure = h2_h2_is_tls(r->connection); -- int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE); -+ int ma = h2_config_rgeti(r, H2_CONF_ALT_SVC_MAX_AGE); - if (ma >= 0) { - svc_ma = apr_psprintf(r->pool, "; ma=%d", ma); - } -@@ -107,8 +107,8 @@ - "h2_alt_svc: announce %s for %s:%d", - (secure? "secure" : "insecure"), - r->hostname, (int)r->server->port); -- for (i = 0; i < cfg->alt_svcs->nelts; ++i) { -- h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i); -+ for (i = 0; i < alt_svcs->nelts; ++i) { -+ h2_alt_svc *as = h2_alt_svc_IDX(alt_svcs, i); - const char *ahost = as->host; - if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) { - ahost = NULL; ---- a/modules/http2/h2_bucket_beam.c -+++ b/modules/http2/h2_bucket_beam.c -@@ -196,7 +196,7 @@ - * bucket beam that can transport buckets across threads - ******************************************************************************/ - --static void mutex_leave(void *ctx, apr_thread_mutex_t *lock) -+static void mutex_leave(apr_thread_mutex_t *lock) - { - apr_thread_mutex_unlock(lock); - } -@@ -217,7 +217,7 @@ - static void leave_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl) - { - if (pbl->leave) { -- pbl->leave(pbl->leave_ctx, pbl->mutex); -+ pbl->leave(pbl->mutex); - } - } - ---- a/modules/http2/h2_bucket_beam.h -+++ b/modules/http2/h2_bucket_beam.h -@@ -126,12 +126,11 @@ - * buffers until the transmission is complete. Star gates use a similar trick. - */ - --typedef void h2_beam_mutex_leave(void *ctx, struct apr_thread_mutex_t *lock); -+typedef void h2_beam_mutex_leave(struct apr_thread_mutex_t *lock); - - typedef struct { - apr_thread_mutex_t *mutex; - h2_beam_mutex_leave *leave; -- void *leave_ctx; - } h2_beam_lock; - - typedef struct h2_bucket_beam h2_bucket_beam; ---- a/modules/http2/h2_config.c -+++ b/modules/http2/h2_config.c -@@ -42,6 +42,55 @@ - #define H2_CONFIG_GET(a, b, n) \ - (((a)->n == DEF_VAL)? (b) : (a))->n - -+#define H2_CONFIG_SET(a, n, v) \ -+ ((a)->n = v) -+ -+#define CONFIG_CMD_SET(cmd,dir,var,val) \ -+ h2_config_seti(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val) -+ -+#define CONFIG_CMD_SET64(cmd,dir,var,val) \ -+ h2_config_seti64(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val) -+ -+/* Apache httpd module configuration for h2. */ -+typedef struct h2_config { -+ const char *name; -+ int h2_max_streams; /* max concurrent # streams (http2) */ -+ int h2_window_size; /* stream window size (http2) */ -+ int min_workers; /* min # of worker threads/child */ -+ int max_workers; /* max # of worker threads/child */ -+ int max_worker_idle_secs; /* max # of idle seconds for worker */ -+ int stream_max_mem_size; /* max # bytes held in memory/stream */ -+ apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */ -+ int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/ -+ int serialize_headers; /* Use serialized HTTP/1.1 headers for -+ processing, better compatibility */ -+ int h2_direct; /* if mod_h2 is active directly */ -+ int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */ -+ int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ -+ apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */ -+ int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */ -+ int h2_push; /* if HTTP/2 server push is enabled */ -+ struct apr_hash_t *priorities;/* map of content-type to h2_priority records */ -+ -+ int push_diary_size; /* # of entries in push diary */ -+ int copy_files; /* if files shall be copied vs setaside on output */ -+ apr_array_header_t *push_list;/* list of h2_push_res configurations */ -+ int early_hints; /* support status code 103 */ -+ int padding_bits; -+ int padding_always; -+} h2_config; -+ -+typedef struct h2_dir_config { -+ const char *name; -+ apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */ -+ int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/ -+ int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ -+ int h2_push; /* if HTTP/2 server push is enabled */ -+ apr_array_header_t *push_list;/* list of h2_push_res configurations */ -+ int early_hints; /* support status code 103 */ -+} h2_dir_config; -+ -+ - static h2_config defconf = { - "default", - 100, /* max_streams */ -@@ -64,6 +113,18 @@ - 0, /* copy files across threads */ - NULL, /* push list */ - 0, /* early hints, http status 103 */ -+ 0, /* padding bits */ -+ 1, /* padding always */ -+}; -+ -+static h2_dir_config defdconf = { -+ "default", -+ NULL, /* no alt-svcs */ -+ -1, /* alt-svc max age */ -+ -1, /* HTTP/1 Upgrade support */ -+ -1, /* HTTP/2 server push enabled */ -+ NULL, /* push list */ -+ -1, /* early hints, http status 103 */ - }; - - void h2_config_init(apr_pool_t *pool) -@@ -71,12 +132,10 @@ - (void)pool; - } - --static void *h2_config_create(apr_pool_t *pool, -- const char *prefix, const char *x) -+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s) - { - h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config)); -- const char *s = x? x : "unknown"; -- char *name = apr_pstrcat(pool, prefix, "[", s, "]", NULL); -+ char *name = apr_pstrcat(pool, "srv[", s->defn_name, "]", NULL); - - conf->name = name; - conf->h2_max_streams = DEF_VAL; -@@ -98,19 +157,11 @@ - conf->copy_files = DEF_VAL; - conf->push_list = NULL; - conf->early_hints = DEF_VAL; -+ conf->padding_bits = DEF_VAL; -+ conf->padding_always = DEF_VAL; - return conf; - } - --void *h2_config_create_svr(apr_pool_t *pool, server_rec *s) --{ -- return h2_config_create(pool, "srv", s->defn_name); --} -- --void *h2_config_create_dir(apr_pool_t *pool, char *x) --{ -- return h2_config_create(pool, "dir", x); --} -- - static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) - { - h2_config *base = (h2_config *)basev; -@@ -149,25 +200,52 @@ - n->push_list = add->push_list? add->push_list : base->push_list; - } - n->early_hints = H2_CONFIG_GET(add, base, early_hints); -+ n->padding_bits = H2_CONFIG_GET(add, base, padding_bits); -+ n->padding_always = H2_CONFIG_GET(add, base, padding_always); - return n; - } - --void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) -+void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) - { - return h2_config_merge(pool, basev, addv); - } - --void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv) -+void *h2_config_create_dir(apr_pool_t *pool, char *x) - { -- return h2_config_merge(pool, basev, addv); -+ h2_dir_config *conf = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config)); -+ const char *s = x? x : "unknown"; -+ char *name = apr_pstrcat(pool, "dir[", s, "]", NULL); -+ -+ conf->name = name; -+ conf->alt_svc_max_age = DEF_VAL; -+ conf->h2_upgrade = DEF_VAL; -+ conf->h2_push = DEF_VAL; -+ conf->early_hints = DEF_VAL; -+ return conf; - } - --int h2_config_geti(const h2_config *conf, h2_config_var_t var) -+void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv) - { -- return (int)h2_config_geti64(conf, var); -+ h2_dir_config *base = (h2_dir_config *)basev; -+ h2_dir_config *add = (h2_dir_config *)addv; -+ h2_dir_config *n = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config)); -+ -+ n->name = apr_pstrcat(pool, "merged[", add->name, ", ", base->name, "]", NULL); -+ n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs; -+ n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age); -+ n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade); -+ n->h2_push = H2_CONFIG_GET(add, base, h2_push); -+ if (add->push_list && base->push_list) { -+ n->push_list = apr_array_append(pool, base->push_list, add->push_list); -+ } -+ else { -+ n->push_list = add->push_list? add->push_list : base->push_list; -+ } -+ n->early_hints = H2_CONFIG_GET(add, base, early_hints); -+ return n; - } - --apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) -+static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t var) - { - switch(var) { - case H2_CONF_MAX_STREAMS: -@@ -204,12 +282,93 @@ - return H2_CONFIG_GET(conf, &defconf, copy_files); - case H2_CONF_EARLY_HINTS: - return H2_CONFIG_GET(conf, &defconf, early_hints); -+ case H2_CONF_PADDING_BITS: -+ return H2_CONFIG_GET(conf, &defconf, padding_bits); -+ case H2_CONF_PADDING_ALWAYS: -+ return H2_CONFIG_GET(conf, &defconf, padding_always); - default: - return DEF_VAL; - } - } - --const h2_config *h2_config_sget(server_rec *s) -+static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val) -+{ -+ switch(var) { -+ case H2_CONF_MAX_STREAMS: -+ H2_CONFIG_SET(conf, h2_max_streams, val); -+ break; -+ case H2_CONF_WIN_SIZE: -+ H2_CONFIG_SET(conf, h2_window_size, val); -+ break; -+ case H2_CONF_MIN_WORKERS: -+ H2_CONFIG_SET(conf, min_workers, val); -+ break; -+ case H2_CONF_MAX_WORKERS: -+ H2_CONFIG_SET(conf, max_workers, val); -+ break; -+ case H2_CONF_MAX_WORKER_IDLE_SECS: -+ H2_CONFIG_SET(conf, max_worker_idle_secs, val); -+ break; -+ case H2_CONF_STREAM_MAX_MEM: -+ H2_CONFIG_SET(conf, stream_max_mem_size, val); -+ break; -+ case H2_CONF_ALT_SVC_MAX_AGE: -+ H2_CONFIG_SET(conf, alt_svc_max_age, val); -+ break; -+ case H2_CONF_SER_HEADERS: -+ H2_CONFIG_SET(conf, serialize_headers, val); -+ break; -+ case H2_CONF_MODERN_TLS_ONLY: -+ H2_CONFIG_SET(conf, modern_tls_only, val); -+ break; -+ case H2_CONF_UPGRADE: -+ H2_CONFIG_SET(conf, h2_upgrade, val); -+ break; -+ case H2_CONF_DIRECT: -+ H2_CONFIG_SET(conf, h2_direct, val); -+ break; -+ case H2_CONF_TLS_WARMUP_SIZE: -+ H2_CONFIG_SET(conf, tls_warmup_size, val); -+ break; -+ case H2_CONF_TLS_COOLDOWN_SECS: -+ H2_CONFIG_SET(conf, tls_cooldown_secs, val); -+ break; -+ case H2_CONF_PUSH: -+ H2_CONFIG_SET(conf, h2_push, val); -+ break; -+ case H2_CONF_PUSH_DIARY_SIZE: -+ H2_CONFIG_SET(conf, push_diary_size, val); -+ break; -+ case H2_CONF_COPY_FILES: -+ H2_CONFIG_SET(conf, copy_files, val); -+ break; -+ case H2_CONF_EARLY_HINTS: -+ H2_CONFIG_SET(conf, early_hints, val); -+ break; -+ case H2_CONF_PADDING_BITS: -+ H2_CONFIG_SET(conf, padding_bits, val); -+ break; -+ case H2_CONF_PADDING_ALWAYS: -+ H2_CONFIG_SET(conf, padding_always, val); -+ break; -+ default: -+ break; -+ } -+} -+ -+static void h2_srv_config_seti64(h2_config *conf, h2_config_var_t var, apr_int64_t val) -+{ -+ switch(var) { -+ case H2_CONF_TLS_WARMUP_SIZE: -+ H2_CONFIG_SET(conf, tls_warmup_size, val); -+ break; -+ default: -+ h2_srv_config_seti(conf, var, (int)val); -+ break; -+ } -+} -+ -+static h2_config *h2_config_sget(server_rec *s) - { - h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, - &http2_module); -@@ -217,9 +376,162 @@ - return cfg; - } - --const struct h2_priority *h2_config_get_priority(const h2_config *conf, -- const char *content_type) -+static const h2_dir_config *h2_config_rget(request_rec *r) -+{ -+ h2_dir_config *cfg = (h2_dir_config *)ap_get_module_config(r->per_dir_config, -+ &http2_module); -+ ap_assert(cfg); -+ return cfg; -+} -+ -+static apr_int64_t h2_dir_config_geti64(const h2_dir_config *conf, h2_config_var_t var) -+{ -+ switch(var) { -+ case H2_CONF_ALT_SVC_MAX_AGE: -+ return H2_CONFIG_GET(conf, &defdconf, alt_svc_max_age); -+ case H2_CONF_UPGRADE: -+ return H2_CONFIG_GET(conf, &defdconf, h2_upgrade); -+ case H2_CONF_PUSH: -+ return H2_CONFIG_GET(conf, &defdconf, h2_push); -+ case H2_CONF_EARLY_HINTS: -+ return H2_CONFIG_GET(conf, &defdconf, early_hints); -+ -+ default: -+ return DEF_VAL; -+ } -+} -+ -+static void h2_config_seti(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, int val) -+{ -+ int set_srv = !dconf; -+ if (dconf) { -+ switch(var) { -+ case H2_CONF_ALT_SVC_MAX_AGE: -+ H2_CONFIG_SET(dconf, alt_svc_max_age, val); -+ break; -+ case H2_CONF_UPGRADE: -+ H2_CONFIG_SET(dconf, h2_upgrade, val); -+ break; -+ case H2_CONF_PUSH: -+ H2_CONFIG_SET(dconf, h2_push, val); -+ break; -+ case H2_CONF_EARLY_HINTS: -+ H2_CONFIG_SET(dconf, early_hints, val); -+ break; -+ default: -+ /* not handled in dir_conf */ -+ set_srv = 1; -+ break; -+ } -+ } -+ -+ if (set_srv) { -+ h2_srv_config_seti(conf, var, val); -+ } -+} -+ -+static void h2_config_seti64(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, apr_int64_t val) - { -+ int set_srv = !dconf; -+ if (dconf) { -+ switch(var) { -+ default: -+ /* not handled in dir_conf */ -+ set_srv = 1; -+ break; -+ } -+ } -+ -+ if (set_srv) { -+ h2_srv_config_seti64(conf, var, val); -+ } -+} -+ -+static const h2_config *h2_config_get(conn_rec *c) -+{ -+ h2_ctx *ctx = h2_ctx_get(c, 0); -+ -+ if (ctx) { -+ if (ctx->config) { -+ return ctx->config; -+ } -+ else if (ctx->server) { -+ ctx->config = h2_config_sget(ctx->server); -+ return ctx->config; -+ } -+ } -+ -+ return h2_config_sget(c->base_server); -+} -+ -+int h2_config_cgeti(conn_rec *c, h2_config_var_t var) -+{ -+ return (int)h2_srv_config_geti64(h2_config_get(c), var); -+} -+ -+apr_int64_t h2_config_cgeti64(conn_rec *c, h2_config_var_t var) -+{ -+ return h2_srv_config_geti64(h2_config_get(c), var); -+} -+ -+int h2_config_sgeti(server_rec *s, h2_config_var_t var) -+{ -+ return (int)h2_srv_config_geti64(h2_config_sget(s), var); -+} -+ -+apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var) -+{ -+ return h2_srv_config_geti64(h2_config_sget(s), var); -+} -+ -+int h2_config_geti(request_rec *r, server_rec *s, h2_config_var_t var) -+{ -+ return (int)h2_config_geti64(r, s, var); -+} -+ -+apr_int64_t h2_config_geti64(request_rec *r, server_rec *s, h2_config_var_t var) -+{ -+ apr_int64_t mode = r? (int)h2_dir_config_geti64(h2_config_rget(r), var) : DEF_VAL; -+ return (mode != DEF_VAL)? mode : h2_config_sgeti64(s, var); -+} -+ -+int h2_config_rgeti(request_rec *r, h2_config_var_t var) -+{ -+ return h2_config_geti(r, r->server, var); -+} -+ -+apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var) -+{ -+ return h2_config_geti64(r, r->server, var); -+} -+ -+apr_array_header_t *h2_config_push_list(request_rec *r) -+{ -+ const h2_config *sconf; -+ const h2_dir_config *conf = h2_config_rget(r); -+ -+ if (conf && conf->push_list) { -+ return conf->push_list; -+ } -+ sconf = h2_config_sget(r->server); -+ return sconf? sconf->push_list : NULL; -+} -+ -+apr_array_header_t *h2_config_alt_svcs(request_rec *r) -+{ -+ const h2_config *sconf; -+ const h2_dir_config *conf = h2_config_rget(r); -+ -+ if (conf && conf->alt_svcs) { -+ return conf->alt_svcs; -+ } -+ sconf = h2_config_sget(r->server); -+ return sconf? sconf->alt_svcs : NULL; -+} -+ -+const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type) -+{ -+ const h2_config *conf = h2_config_get(c); - if (content_type && conf->priorities) { - size_t len = strcspn(content_type, "; \t"); - h2_priority *prio = apr_hash_get(conf->priorities, content_type, len); -@@ -228,166 +540,156 @@ - return NULL; - } - --static const char *h2_conf_set_max_streams(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_max_streams(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->h2_max_streams = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->h2_max_streams < 1) { -+ apr_int64_t ival = (int)apr_atoi64(value); -+ if (ival < 1) { - return "value must be > 0"; - } -+ CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_MAX_STREAMS, ival); - return NULL; - } - --static const char *h2_conf_set_window_size(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_window_size(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->h2_window_size = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->h2_window_size < 1024) { -+ int val = (int)apr_atoi64(value); -+ if (val < 1024) { - return "value must be >= 1024"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_WIN_SIZE, val); - return NULL; - } - --static const char *h2_conf_set_min_workers(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_min_workers(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->min_workers = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->min_workers < 1) { -+ int val = (int)apr_atoi64(value); -+ if (val < 1) { - return "value must be > 0"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MIN_WORKERS, val); - return NULL; - } - --static const char *h2_conf_set_max_workers(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_max_workers(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->max_workers = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->max_workers < 1) { -+ int val = (int)apr_atoi64(value); -+ if (val < 1) { - return "value must be > 0"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_WORKERS, val); - return NULL; - } - --static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->max_worker_idle_secs = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->max_worker_idle_secs < 1) { -+ int val = (int)apr_atoi64(value); -+ if (val < 1) { - return "value must be > 0"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_WORKER_IDLE_SECS, val); - return NULL; - } - --static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_stream_max_mem_size(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- -- -- cfg->stream_max_mem_size = (int)apr_atoi64(value); -- (void)arg; -- if (cfg->stream_max_mem_size < 1024) { -+ int val = (int)apr_atoi64(value); -+ if (val < 1024) { - return "value must be >= 1024"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_STREAM_MAX_MEM, val); - return NULL; - } - --static const char *h2_add_alt_svc(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_add_alt_svc(cmd_parms *cmd, -+ void *dirconf, const char *value) - { - if (value && *value) { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool); -+ h2_alt_svc *as = h2_alt_svc_parse(value, cmd->pool); - if (!as) { - return "unable to parse alt-svc specifier"; - } -- if (!cfg->alt_svcs) { -- cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*)); -+ -+ if (cmd->path) { -+ h2_dir_config *dcfg = (h2_dir_config *)dirconf; -+ if (!dcfg->alt_svcs) { -+ dcfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*)); -+ } -+ APR_ARRAY_PUSH(dcfg->alt_svcs, h2_alt_svc*) = as; -+ } -+ else { -+ h2_config *cfg = (h2_config *)h2_config_sget(cmd->server); -+ if (!cfg->alt_svcs) { -+ cfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*)); -+ } -+ APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as; - } -- APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as; - } -- (void)arg; - return NULL; - } - --static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_alt_svc_max_age(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->alt_svc_max_age = (int)apr_atoi64(value); -- (void)arg; -+ int val = (int)apr_atoi64(value); -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_ALT_SVC_MAX_AGE, val); - return NULL; - } - --static const char *h2_conf_set_session_extra_files(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_session_extra_files(cmd_parms *cmd, -+ void *dirconf, const char *value) - { - /* deprecated, ignore */ -- (void)arg; -+ (void)dirconf; - (void)value; -- ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, parms->pool, /* NO LOGNO */ -+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, /* NO LOGNO */ - "H2SessionExtraFiles is obsolete and will be ignored"); - return NULL; - } - --static const char *h2_conf_set_serialize_headers(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_serialize_headers(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - if (!strcasecmp(value, "On")) { -- cfg->serialize_headers = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->serialize_headers = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - --static const char *h2_conf_set_direct(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_direct(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - if (!strcasecmp(value, "On")) { -- cfg->h2_direct = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->h2_direct = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - --static const char *h2_conf_set_push(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_push(cmd_parms *cmd, void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - if (!strcasecmp(value, "On")) { -- cfg->h2_push = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->h2_push = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - -@@ -419,7 +721,7 @@ - else if (!strcasecmp("BEFORE", sdependency)) { - dependency = H2_DEPENDANT_BEFORE; - if (sweight) { -- return "dependency 'Before' does not allow a weight"; -+ return "dependecy 'Before' does not allow a weight"; - } - } - else if (!strcasecmp("INTERLEAVED", sdependency)) { -@@ -447,100 +749,88 @@ - return NULL; - } - --static const char *h2_conf_set_modern_tls_only(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_modern_tls_only(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - if (!strcasecmp(value, "On")) { -- cfg->modern_tls_only = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->modern_tls_only = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - --static const char *h2_conf_set_upgrade(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_upgrade(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); - if (!strcasecmp(value, "On")) { -- cfg->h2_upgrade = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->h2_upgrade = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - --static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_tls_warmup_size(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->tls_warmup_size = apr_atoi64(value); -- (void)arg; -+ apr_int64_t val = apr_atoi64(value); -+ CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_WARMUP_SIZE, val); - return NULL; - } - --static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- cfg->tls_cooldown_secs = (int)apr_atoi64(value); -- (void)arg; -+ apr_int64_t val = (int)apr_atoi64(value); -+ CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_COOLDOWN_SECS, val); - return NULL; - } - --static const char *h2_conf_set_push_diary_size(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_push_diary_size(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- (void)arg; -- cfg->push_diary_size = (int)apr_atoi64(value); -- if (cfg->push_diary_size < 0) { -+ int val = (int)apr_atoi64(value); -+ if (val < 0) { - return "value must be >= 0"; - } -- if (cfg->push_diary_size > 0 && (cfg->push_diary_size & (cfg->push_diary_size-1))) { -+ if (val > 0 && (val & (val-1))) { - return "value must a power of 2"; - } -- if (cfg->push_diary_size > (1 << 15)) { -+ if (val > (1 << 15)) { - return "value must <= 65536"; - } -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH_DIARY_SIZE, val); - return NULL; - } - --static const char *h2_conf_set_copy_files(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_copy_files(cmd_parms *cmd, -+ void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)arg; - if (!strcasecmp(value, "On")) { -- cfg->copy_files = 1; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 1); - return NULL; - } - else if (!strcasecmp(value, "Off")) { -- cfg->copy_files = 0; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 0); - return NULL; - } -- -- (void)arg; - return "value must be On or Off"; - } - --static void add_push(apr_pool_t *pool, h2_config *conf, h2_push_res *push) -+static void add_push(apr_array_header_t **plist, apr_pool_t *pool, h2_push_res *push) - { - h2_push_res *new; -- if (!conf->push_list) { -- conf->push_list = apr_array_make(pool, 10, sizeof(*push)); -+ if (!*plist) { -+ *plist = apr_array_make(pool, 10, sizeof(*push)); - } -- new = apr_array_push(conf->push_list); -+ new = apr_array_push(*plist); - new->uri_ref = push->uri_ref; - new->critical = push->critical; - } -@@ -549,8 +839,6 @@ - const char *arg1, const char *arg2, - const char *arg3) - { -- h2_config *dconf = (h2_config*)dirconf ; -- h2_config *sconf = (h2_config*)h2_config_sget(cmd->server); - h2_push_res push; - const char *last = arg3; - -@@ -575,42 +863,54 @@ - } - } - -- /* server command? set both */ -- if (cmd->path == NULL) { -- add_push(cmd->pool, sconf, &push); -- add_push(cmd->pool, dconf, &push); -+ if (cmd->path) { -+ add_push(&(((h2_dir_config*)dirconf)->push_list), cmd->pool, &push); - } - else { -- add_push(cmd->pool, dconf, &push); -+ add_push(&(h2_config_sget(cmd->server)->push_list), cmd->pool, &push); - } -+ return NULL; -+} - -+static const char *h2_conf_set_early_hints(cmd_parms *cmd, -+ void *dirconf, const char *value) -+{ -+ int val; -+ -+ if (!strcasecmp(value, "On")) val = 1; -+ else if (!strcasecmp(value, "Off")) val = 0; -+ else return "value must be On or Off"; -+ -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_EARLY_HINTS, val); -+ if (cmd->path) { -+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, -+ "H2EarlyHints = %d on path %s", val, cmd->path); -+ } - return NULL; - } - --static const char *h2_conf_set_early_hints(cmd_parms *parms, -- void *arg, const char *value) -+static const char *h2_conf_set_padding(cmd_parms *cmd, void *dirconf, const char *value) - { -- h2_config *cfg = (h2_config *)h2_config_sget(parms->server); -- if (!strcasecmp(value, "On")) { -- cfg->early_hints = 1; -- return NULL; -+ int val; -+ -+ val = (int)apr_atoi64(value); -+ if (val < 0) { -+ return "number of bits must be >= 0"; - } -- else if (!strcasecmp(value, "Off")) { -- cfg->early_hints = 0; -- return NULL; -+ if (val > 8) { -+ return "number of bits must be <= 8"; - } -- -- (void)arg; -- return "value must be On or Off"; -+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PADDING_BITS, val); -+ return NULL; - } - -+ - void h2_get_num_workers(server_rec *s, int *minw, int *maxw) - { - int threads_per_child = 0; -- const h2_config *config = h2_config_sget(s); - -- *minw = h2_config_geti(config, H2_CONF_MIN_WORKERS); -- *maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS); -+ *minw = h2_config_sgeti(s, H2_CONF_MIN_WORKERS); -+ *maxw = h2_config_sgeti(s, H2_CONF_MAX_WORKERS); - ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); - - if (*minw <= 0) { -@@ -652,7 +952,7 @@ - AP_INIT_TAKE1("H2ModernTLSOnly", h2_conf_set_modern_tls_only, NULL, - RSRC_CONF, "off to not impose RFC 7540 restrictions on TLS"), - AP_INIT_TAKE1("H2Upgrade", h2_conf_set_upgrade, NULL, -- RSRC_CONF, "on to allow HTTP/1 Upgrades to h2/h2c"), -+ RSRC_CONF|OR_AUTHCFG, "on to allow HTTP/1 Upgrades to h2/h2c"), - AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL, - RSRC_CONF, "on to enable direct HTTP/2 mode"), - AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL, -@@ -662,7 +962,7 @@ - AP_INIT_TAKE1("H2TLSCoolDownSecs", h2_conf_set_tls_cooldown_secs, NULL, - RSRC_CONF, "seconds of idle time on TLS before shrinking writes"), - AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL, -- RSRC_CONF, "off to disable HTTP/2 server push"), -+ RSRC_CONF|OR_AUTHCFG, "off to disable HTTP/2 server push"), - AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL, - RSRC_CONF, "define priority of PUSHed resources per content type"), - AP_INIT_TAKE1("H2PushDiarySize", h2_conf_set_push_diary_size, NULL, -@@ -670,33 +970,12 @@ - AP_INIT_TAKE1("H2CopyFiles", h2_conf_set_copy_files, NULL, - OR_FILEINFO, "on to perform copy of file data"), - AP_INIT_TAKE123("H2PushResource", h2_conf_add_push_res, NULL, -- OR_FILEINFO, "add a resource to be pushed in this location/on this server."), -+ OR_FILEINFO|OR_AUTHCFG, "add a resource to be pushed in this location/on this server."), - AP_INIT_TAKE1("H2EarlyHints", h2_conf_set_early_hints, NULL, - RSRC_CONF, "on to enable interim status 103 responses"), -+ AP_INIT_TAKE1("H2Padding", h2_conf_set_padding, NULL, -+ RSRC_CONF, "set payload padding"), - AP_END_CMD - }; - - --const h2_config *h2_config_rget(request_rec *r) --{ -- h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, -- &http2_module); -- return cfg? cfg : h2_config_sget(r->server); --} -- --const h2_config *h2_config_get(conn_rec *c) --{ -- h2_ctx *ctx = h2_ctx_get(c, 0); -- -- if (ctx) { -- if (ctx->config) { -- return ctx->config; -- } -- else if (ctx->server) { -- ctx->config = h2_config_sget(ctx->server); -- return ctx->config; -- } -- } -- -- return h2_config_sget(c->base_server); --} ---- a/modules/http2/h2_config.h -+++ b/modules/http2/h2_config.h -@@ -42,6 +42,8 @@ - H2_CONF_PUSH_DIARY_SIZE, - H2_CONF_COPY_FILES, - H2_CONF_EARLY_HINTS, -+ H2_CONF_PADDING_BITS, -+ H2_CONF_PADDING_ALWAYS, - } h2_config_var_t; - - struct apr_hash_t; -@@ -53,33 +55,6 @@ - int critical; - } h2_push_res; - --/* Apache httpd module configuration for h2. */ --typedef struct h2_config { -- const char *name; -- int h2_max_streams; /* max concurrent # streams (http2) */ -- int h2_window_size; /* stream window size (http2) */ -- int min_workers; /* min # of worker threads/child */ -- int max_workers; /* max # of worker threads/child */ -- int max_worker_idle_secs; /* max # of idle seconds for worker */ -- int stream_max_mem_size; /* max # bytes held in memory/stream */ -- apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */ -- int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/ -- int serialize_headers; /* Use serialized HTTP/1.1 headers for -- processing, better compatibility */ -- int h2_direct; /* if mod_h2 is active directly */ -- int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */ -- int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ -- apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */ -- int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */ -- int h2_push; /* if HTTP/2 server push is enabled */ -- struct apr_hash_t *priorities;/* map of content-type to h2_priority records */ -- -- int push_diary_size; /* # of entries in push diary */ -- int copy_files; /* if files shall be copied vs setaside on output */ -- apr_array_header_t *push_list;/* list of h2_push_res configurations */ -- int early_hints; /* support status code 103 */ --} h2_config; -- - - void *h2_config_create_dir(apr_pool_t *pool, char *x); - void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv); -@@ -88,19 +63,37 @@ - - extern const command_rec h2_cmds[]; - --const h2_config *h2_config_get(conn_rec *c); --const h2_config *h2_config_sget(server_rec *s); --const h2_config *h2_config_rget(request_rec *r); -+int h2_config_geti(request_rec *r, server_rec *s, h2_config_var_t var); -+apr_int64_t h2_config_geti64(request_rec *r, server_rec *s, h2_config_var_t var); - --int h2_config_geti(const h2_config *conf, h2_config_var_t var); --apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var); -+/** -+ * Get the configured value for variable <var> at the given connection. -+ */ -+int h2_config_cgeti(conn_rec *c, h2_config_var_t var); -+apr_int64_t h2_config_cgeti64(conn_rec *c, h2_config_var_t var); -+ -+/** -+ * Get the configured value for variable <var> at the given server. -+ */ -+int h2_config_sgeti(server_rec *s, h2_config_var_t var); -+apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var); -+ -+/** -+ * Get the configured value for variable <var> at the given request, -+ * if configured for the request location. -+ * Fallback to request server config otherwise. -+ */ -+int h2_config_rgeti(request_rec *r, h2_config_var_t var); -+apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var); - --void h2_get_num_workers(server_rec *s, int *minw, int *maxw); -+apr_array_header_t *h2_config_push_list(request_rec *r); -+apr_array_header_t *h2_config_alt_svcs(request_rec *r); - -+ -+void h2_get_num_workers(server_rec *s, int *minw, int *maxw); - void h2_config_init(apr_pool_t *pool); - --const struct h2_priority *h2_config_get_priority(const h2_config *conf, -- const char *content_type); -+const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type); - - #endif /* __mod_h2__h2_config_h__ */ - ---- a/modules/http2/h2_conn.c -+++ b/modules/http2/h2_conn.c -@@ -18,6 +18,7 @@ - #include <apr_strings.h> - - #include <ap_mpm.h> -+#include <ap_mmn.h> - - #include <httpd.h> - #include <http_core.h> -@@ -79,7 +80,7 @@ - mpm_type = H2_MPM_PREFORK; - mpm_module = m; - /* While http2 can work really well on prefork, it collides -- * today's use case for prefork: runnning single-thread app engines -+ * today's use case for prefork: running single-thread app engines - * like php. If we restrict h2_workers to 1 per process, php will - * work fine, but browser will be limited to 1 active request at a - * time. */ -@@ -109,7 +110,6 @@ - - apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s) - { -- const h2_config *config = h2_config_sget(s); - apr_status_t status = APR_SUCCESS; - int minw, maxw; - int max_threads_per_child = 0; -@@ -129,7 +129,7 @@ - - h2_get_num_workers(s, &minw, &maxw); - -- idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS); -+ idle_secs = h2_config_sgeti(s, H2_CONF_MAX_WORKER_IDLE_SECS); - ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s, - "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d", - minw, maxw, max_threads_per_child, idle_secs); -@@ -138,7 +138,7 @@ - ap_register_input_filter("H2_IN", h2_filter_core_input, - NULL, AP_FTYPE_CONNECTION); - -- status = h2_mplx_child_init(pool, s); -+ status = h2_mplx_m_child_init(pool, s); - - if (status == APR_SUCCESS) { - status = apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM, -@@ -172,9 +172,10 @@ - return mpm_module; - } - --apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r) -+apr_status_t h2_conn_setup(conn_rec *c, request_rec *r, server_rec *s) - { - h2_session *session; -+ h2_ctx *ctx; - apr_status_t status; - - if (!workers) { -@@ -183,24 +184,25 @@ - return APR_EGENERAL; - } - -- if (r) { -- status = h2_session_rcreate(&session, r, ctx, workers); -- } -- else { -- status = h2_session_create(&session, c, ctx, workers); -- } -- -- if (status == APR_SUCCESS) { -+ if (APR_SUCCESS == (status = h2_session_create(&session, c, r, s, workers))) { -+ ctx = h2_ctx_get(c, 1); - h2_ctx_session_set(ctx, session); -+ -+ /* remove the input filter of mod_reqtimeout, now that the connection -+ * is established and we have swtiched to h2. reqtimeout has supervised -+ * possibly configured handshake timeouts and needs to get out of the way -+ * now since the rest of its state handling assumes http/1.x to take place. */ -+ ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout"); - } -+ - return status; - } - --apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c) -+apr_status_t h2_conn_run(conn_rec *c) - { - apr_status_t status; - int mpm_state = 0; -- h2_session *session = h2_ctx_session_get(ctx); -+ h2_session *session = h2_ctx_get_session(c); - - ap_assert(session); - do { -@@ -235,6 +237,13 @@ - case H2_SESSION_ST_BUSY: - case H2_SESSION_ST_WAIT: - c->cs->state = CONN_STATE_WRITE_COMPLETION; -+ if (c->cs && (session->open_streams || !session->remote.emitted_count)) { -+ /* let the MPM know that we are not done and want -+ * the Timeout behaviour instead of a KeepAliveTimeout -+ * See PR 63534. -+ */ -+ c->cs->sense = CONN_SENSE_WANT_READ; -+ } - break; - case H2_SESSION_ST_CLEANUP: - case H2_SESSION_ST_DONE: -@@ -249,7 +258,7 @@ - - apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c) - { -- h2_session *session = h2_ctx_session_get(ctx); -+ h2_session *session = h2_ctx_get_session(c); - if (session) { - apr_status_t status = h2_session_pre_close(session, async_mpm); - return (status == APR_SUCCESS)? DONE : status; -@@ -257,7 +266,7 @@ - return DONE; - } - --conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent) -+conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent) - { - apr_allocator_t *allocator; - apr_status_t status; -@@ -268,11 +277,11 @@ - - ap_assert(master); - ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master, -- "h2_stream(%ld-%d): create slave", master->id, slave_id); -+ "h2_stream(%ld-%d): create secondary", master->id, sec_id); - - /* We create a pool with its own allocator to be used for - * processing a request. This is the only way to have the processing -- * independant of its parent pool in the sense that it can work in -+ * independent of its parent pool in the sense that it can work in - * another thread. Also, the new allocator needs its own mutex to - * synchronize sub-pools. - */ -@@ -281,18 +290,18 @@ - status = apr_pool_create_ex(&pool, parent, NULL, allocator); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, master, -- APLOGNO(10004) "h2_session(%ld-%d): create slave pool", -- master->id, slave_id); -+ APLOGNO(10004) "h2_session(%ld-%d): create secondary pool", -+ master->id, sec_id); - return NULL; - } - apr_allocator_owner_set(allocator, pool); -- apr_pool_tag(pool, "h2_slave_conn"); -+ apr_pool_tag(pool, "h2_secondary_conn"); - - c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec)); - if (c == NULL) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, master, -- APLOGNO(02913) "h2_session(%ld-%d): create slave", -- master->id, slave_id); -+ APLOGNO(02913) "h2_session(%ld-%d): create secondary", -+ master->id, sec_id); - apr_pool_destroy(pool); - return NULL; - } -@@ -310,26 +319,28 @@ - c->filter_conn_ctx = NULL; - #endif - c->bucket_alloc = apr_bucket_alloc_create(pool); -+#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1) - c->data_in_input_filters = 0; - c->data_in_output_filters = 0; -+#endif - /* prevent mpm_event from making wrong assumptions about this connection, - * like e.g. using its socket for an async read check. */ - c->clogging_input_filters = 1; - c->log = NULL; - c->log_id = apr_psprintf(pool, "%ld-%d", -- master->id, slave_id); -+ master->id, sec_id); - c->aborted = 0; -- /* We cannot install the master connection socket on the slaves, as -+ /* We cannot install the master connection socket on the secondary, as - * modules mess with timeouts/blocking of the socket, with - * unwanted side effects to the master connection processing. -- * Fortunately, since we never use the slave socket, we can just install -+ * Fortunately, since we never use the secondary socket, we can just install - * a single, process-wide dummy and everyone is happy. - */ - ap_set_module_config(c->conn_config, &core_module, dummy_socket); - /* TODO: these should be unique to this thread */ - c->sbh = master->sbh; -- /* TODO: not all mpm modules have learned about slave connections yet. -- * copy their config from master to slave. -+ /* TODO: not all mpm modules have learned about secondary connections yet. -+ * copy their config from master to secondary. - */ - if ((mpm = h2_conn_mpm_module()) != NULL) { - cfg = ap_get_module_config(master->conn_config, mpm); -@@ -337,38 +348,38 @@ - } - - ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, -- "h2_slave(%s): created", c->log_id); -+ "h2_secondary(%s): created", c->log_id); - return c; - } - --void h2_slave_destroy(conn_rec *slave) -+void h2_secondary_destroy(conn_rec *secondary) - { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, slave, -- "h2_slave(%s): destroy", slave->log_id); -- slave->sbh = NULL; -- apr_pool_destroy(slave->pool); -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, secondary, -+ "h2_secondary(%s): destroy", secondary->log_id); -+ secondary->sbh = NULL; -+ apr_pool_destroy(secondary->pool); - } - --apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd) -+apr_status_t h2_secondary_run_pre_connection(conn_rec *secondary, apr_socket_t *csd) - { -- if (slave->keepalives == 0) { -+ if (secondary->keepalives == 0) { - /* Simulate that we had already a request on this connection. Some - * hooks trigger special behaviour when keepalives is 0. - * (Not necessarily in pre_connection, but later. Set it here, so it - * is in place.) */ -- slave->keepalives = 1; -+ secondary->keepalives = 1; - /* We signal that this connection will be closed after the request. - * Which is true in that sense that we throw away all traffic data -- * on this slave connection after each requests. Although we might -+ * on this secondary connection after each requests. Although we might - * reuse internal structures like memory pools. - * The wanted effect of this is that httpd does not try to clean up - * any dangling data on this connection when a request is done. Which -- * is unneccessary on a h2 stream. -+ * is unnecessary on a h2 stream. - */ -- slave->keepalive = AP_CONN_CLOSE; -- return ap_run_pre_connection(slave, csd); -+ secondary->keepalive = AP_CONN_CLOSE; -+ return ap_run_pre_connection(secondary, csd); - } -- ap_assert(slave->output_filters); -+ ap_assert(secondary->output_filters); - return APR_SUCCESS; - } - ---- a/modules/http2/h2_conn.h -+++ b/modules/http2/h2_conn.h -@@ -23,21 +23,21 @@ - /** - * Setup the connection and our context for HTTP/2 processing - * -- * @param ctx the http2 context to setup - * @param c the connection HTTP/2 is starting on - * @param r the upgrade request that still awaits an answer, optional -+ * @param s the server selected for this connection (can be != c->base_server) - */ --apr_status_t h2_conn_setup(struct h2_ctx *ctx, conn_rec *c, request_rec *r); -+apr_status_t h2_conn_setup(conn_rec *c, request_rec *r, server_rec *s); - - /** - * Run the HTTP/2 connection in synchronous fashion. - * Return when the HTTP/2 session is done - * and the connection will close or a fatal error occurred. - * -- * @param ctx the http2 context to run -+ * @param c the http2 connection to run - * @return APR_SUCCESS when session is done. - */ --apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c); -+apr_status_t h2_conn_run(conn_rec *c); - - /** - * The connection is about to close. If we have not send a GOAWAY -@@ -68,10 +68,10 @@ - const char *h2_conn_mpm_name(void); - int h2_mpm_supported(void); - --conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent); --void h2_slave_destroy(conn_rec *slave); -+conn_rec *h2_secondary_create(conn_rec *master, int sec_id, apr_pool_t *parent); -+void h2_secondary_destroy(conn_rec *secondary); - --apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd); --void h2_slave_run_connection(conn_rec *slave); -+apr_status_t h2_secondary_run_pre_connection(conn_rec *secondary, apr_socket_t *csd); -+void h2_secondary_run_connection(conn_rec *secondary); - - #endif /* defined(__mod_h2__h2_conn__) */ ---- a/modules/http2/h2_conn_io.c -+++ b/modules/http2/h2_conn_io.c -@@ -40,12 +40,17 @@ - * ~= 1300 bytes */ - #define WRITE_SIZE_INITIAL 1300 - --/* Calculated like this: max TLS record size 16*1024 -- * - 40 (IP) - 20 (TCP) - 40 (TCP options) -- * - TLS overhead (60-100) -- * which seems to create less TCP packets overall -+/* The maximum we'd like to write in one chunk is -+ * the max size of a TLS record. When pushing -+ * many frames down the h2 connection, this might -+ * align differently because of headers and other -+ * frames or simply as not sufficient data is -+ * in a response body. -+ * However keeping frames at or below this limit -+ * should make optimizations at the layer that writes -+ * to TLS easier. - */ --#define WRITE_SIZE_MAX (TLS_DATA_MAX - 100) -+#define WRITE_SIZE_MAX (TLS_DATA_MAX) - - - static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level, -@@ -123,21 +128,20 @@ - - } - --apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, -- const h2_config *cfg) -+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, server_rec *s) - { - io->c = c; - io->output = apr_brigade_create(c->pool, c->bucket_alloc); - io->is_tls = h2_h2_is_tls(c); - io->buffer_output = io->is_tls; -- io->flush_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM); -+ io->flush_threshold = (apr_size_t)h2_config_sgeti64(s, H2_CONF_STREAM_MAX_MEM); - - if (io->is_tls) { - /* This is what we start with, - * see https://issues.apache.org/jira/browse/TS-2503 - */ -- io->warmup_size = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE); -- io->cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS) -+ io->warmup_size = h2_config_sgeti64(s, H2_CONF_TLS_WARMUP_SIZE); -+ io->cooldown_usecs = (h2_config_sgeti(s, H2_CONF_TLS_COOLDOWN_SECS) - * APR_USEC_PER_SEC); - io->write_size = (io->cooldown_usecs > 0? - WRITE_SIZE_INITIAL : WRITE_SIZE_MAX); ---- a/modules/http2/h2_conn_io.h -+++ b/modules/http2/h2_conn_io.h -@@ -48,8 +48,7 @@ - apr_size_t slen; - } h2_conn_io; - --apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, -- const struct h2_config *cfg); -+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, server_rec *s); - - /** - * Append data to the buffered output. ---- a/modules/http2/h2_ctx.c -+++ b/modules/http2/h2_ctx.c -@@ -29,8 +29,8 @@ - { - h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx)); - ap_assert(ctx); -+ h2_ctx_server_update(ctx, c->base_server); - ap_set_module_config(c->conn_config, &http2_module, ctx); -- h2_ctx_server_set(ctx, c->base_server); - return ctx; - } - -@@ -79,8 +79,9 @@ - return ctx; - } - --h2_session *h2_ctx_session_get(h2_ctx *ctx) -+h2_session *h2_ctx_get_session(conn_rec *c) - { -+ h2_ctx *ctx = h2_ctx_get(c, 0); - return ctx? ctx->session : NULL; - } - -@@ -89,33 +90,17 @@ - ctx->session = session; - } - --server_rec *h2_ctx_server_get(h2_ctx *ctx) -+h2_ctx *h2_ctx_server_update(h2_ctx *ctx, server_rec *s) - { -- return ctx? ctx->server : NULL; --} -- --h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s) --{ -- ctx->server = s; -+ if (ctx->server != s) { -+ ctx->server = s; -+ } - return ctx; - } - --int h2_ctx_is_task(h2_ctx *ctx) --{ -- return ctx && ctx->task; --} -- --h2_task *h2_ctx_get_task(h2_ctx *ctx) -+h2_task *h2_ctx_get_task(conn_rec *c) - { -+ h2_ctx *ctx = h2_ctx_get(c, 0); - return ctx? ctx->task : NULL; - } - --h2_task *h2_ctx_cget_task(conn_rec *c) --{ -- return h2_ctx_get_task(h2_ctx_get(c, 0)); --} -- --h2_task *h2_ctx_rget_task(request_rec *r) --{ -- return h2_ctx_get_task(h2_ctx_rget(r)); --} ---- a/modules/http2/h2_ctx.h -+++ b/modules/http2/h2_ctx.h -@@ -56,12 +56,11 @@ - */ - h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto); - --/* Set the server_rec relevant for this context. -+/* Update the server_rec relevant for this context. A server for -+ * a connection may change during SNI handling, for example. - */ --h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s); --server_rec *h2_ctx_server_get(h2_ctx *ctx); -+h2_ctx *h2_ctx_server_update(h2_ctx *ctx, server_rec *s); - --struct h2_session *h2_ctx_session_get(h2_ctx *ctx); - void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session); - - /** -@@ -69,10 +68,8 @@ - */ - const char *h2_ctx_protocol_get(const conn_rec *c); - --int h2_ctx_is_task(h2_ctx *ctx); -+struct h2_session *h2_ctx_get_session(conn_rec *c); -+struct h2_task *h2_ctx_get_task(conn_rec *c); - --struct h2_task *h2_ctx_get_task(h2_ctx *ctx); --struct h2_task *h2_ctx_cget_task(conn_rec *c); --struct h2_task *h2_ctx_rget_task(request_rec *r); - - #endif /* defined(__mod_h2__h2_ctx__) */ ---- a/modules/http2/h2_filter.c -+++ b/modules/http2/h2_filter.c -@@ -54,6 +54,7 @@ - const char *data; - ssize_t n; - -+ (void)c; - status = apr_bucket_read(b, &data, &len, block); - - while (status == APR_SUCCESS && len > 0) { -@@ -71,10 +72,10 @@ - } - else { - session->io.bytes_read += n; -- if (len <= n) { -+ if ((apr_ssize_t)len <= n) { - break; - } -- len -= n; -+ len -= (apr_size_t)n; - data += n; - } - } -@@ -277,6 +278,7 @@ - apr_bucket_brigade *dest, - const apr_bucket *src) - { -+ (void)beam; - if (H2_BUCKET_IS_OBSERVER(src)) { - h2_bucket_observer *l = (h2_bucket_observer *)src->data; - apr_bucket *b = h2_bucket_observer_create(dest->bucket_alloc, -@@ -311,8 +313,7 @@ - bbout(bb, " \"settings\": {\n"); - bbout(bb, " \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", m->max_streams); - bbout(bb, " \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 16*1024); -- bbout(bb, " \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", -- h2_config_geti(s->config, H2_CONF_WIN_SIZE)); -+ bbout(bb, " \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", h2_config_sgeti(s->s, H2_CONF_WIN_SIZE)); - bbout(bb, " \"SETTINGS_ENABLE_PUSH\": %d\n", h2_session_push_enabled(s)); - bbout(bb, " }%s\n", last? "" : ","); - } -@@ -369,7 +370,7 @@ - x.s = s; - x.idx = 0; - bbout(bb, " \"streams\": {"); -- h2_mplx_stream_do(s->mplx, add_stream, &x); -+ h2_mplx_m_stream_do(s->mplx, add_stream, &x); - bbout(bb, "\n }%s\n", last? "" : ","); - } - -@@ -431,41 +432,38 @@ - - static apr_status_t h2_status_insert(h2_task *task, apr_bucket *b) - { -- conn_rec *c = task->c->master; -- h2_ctx *h2ctx = h2_ctx_get(c, 0); -- h2_session *session; -- h2_stream *stream; -+ h2_mplx *m = task->mplx; -+ h2_stream *stream = h2_mplx_t_stream_get(m, task); -+ h2_session *s; -+ conn_rec *c; -+ - apr_bucket_brigade *bb; - apr_bucket *e; - int32_t connFlowIn, connFlowOut; - -- -- if (!h2ctx || (session = h2_ctx_session_get(h2ctx)) == NULL) { -- return APR_SUCCESS; -- } -- -- stream = h2_session_stream_get(session, task->stream_id); - if (!stream) { - /* stream already done */ - return APR_SUCCESS; - } -+ s = stream->session; -+ c = s->c; - - bb = apr_brigade_create(stream->pool, c->bucket_alloc); - -- connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2); -- connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2); -+ connFlowIn = nghttp2_session_get_effective_local_window_size(s->ngh2); -+ connFlowOut = nghttp2_session_get_remote_window_size(s->ngh2); - - bbout(bb, "{\n"); - bbout(bb, " \"version\": \"draft-01\",\n"); -- add_settings(bb, session, 0); -- add_peer_settings(bb, session, 0); -+ add_settings(bb, s, 0); -+ add_peer_settings(bb, s, 0); - bbout(bb, " \"connFlowIn\": %d,\n", connFlowIn); - bbout(bb, " \"connFlowOut\": %d,\n", connFlowOut); -- bbout(bb, " \"sentGoAway\": %d,\n", session->local.shutdown); -+ bbout(bb, " \"sentGoAway\": %d,\n", s->local.shutdown); - -- add_streams(bb, session, 0); -+ add_streams(bb, s, 0); - -- add_stats(bb, session, stream, 1); -+ add_stats(bb, s, stream, 1); - bbout(bb, "}\n"); - - while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) { -@@ -495,9 +493,54 @@ - return APR_SUCCESS; - } - -+static apr_status_t discard_body(request_rec *r, apr_off_t maxlen) -+{ -+ apr_bucket_brigade *bb; -+ int seen_eos; -+ apr_status_t rv; -+ -+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); -+ seen_eos = 0; -+ do { -+ apr_bucket *bucket; -+ -+ rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, -+ APR_BLOCK_READ, HUGE_STRING_LEN); -+ -+ if (rv != APR_SUCCESS) { -+ apr_brigade_destroy(bb); -+ return rv; -+ } -+ -+ for (bucket = APR_BRIGADE_FIRST(bb); -+ bucket != APR_BRIGADE_SENTINEL(bb); -+ bucket = APR_BUCKET_NEXT(bucket)) -+ { -+ const char *data; -+ apr_size_t len; -+ -+ if (APR_BUCKET_IS_EOS(bucket)) { -+ seen_eos = 1; -+ break; -+ } -+ if (bucket->length == 0) { -+ continue; -+ } -+ rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); -+ if (rv != APR_SUCCESS) { -+ apr_brigade_destroy(bb); -+ return rv; -+ } -+ maxlen -= bucket->length; -+ } -+ apr_brigade_cleanup(bb); -+ } while (!seen_eos && maxlen >= 0); -+ -+ return APR_SUCCESS; -+} -+ - int h2_filter_h2_status_handler(request_rec *r) - { -- h2_ctx *ctx = h2_ctx_rget(r); - conn_rec *c = r->connection; - h2_task *task; - apr_bucket_brigade *bb; -@@ -511,10 +554,12 @@ - return DECLINED; - } - -- task = ctx? h2_ctx_get_task(ctx) : NULL; -+ task = h2_ctx_get_task(r->connection); - if (task) { -- -- if ((status = ap_discard_request_body(r)) != OK) { -+ /* In this handler, we do some special sauce to send footers back, -+ * IFF we received footers in the request. This is used in our test -+ * cases, since CGI has no way of handling those. */ -+ if ((status = discard_body(r, 1024)) != OK) { - return status; - } - ---- a/modules/http2/h2_from_h1.c -+++ b/modules/http2/h2_from_h1.c -@@ -315,6 +315,7 @@ - int http_status; - apr_array_header_t *hlines; - apr_bucket_brigade *tmp; -+ apr_bucket_brigade *saveto; - } h2_response_parser; - - static apr_status_t parse_header(h2_response_parser *parser, char *line) { -@@ -351,13 +352,17 @@ - parser->tmp = apr_brigade_create(task->pool, task->c->bucket_alloc); - } - status = apr_brigade_split_line(parser->tmp, bb, APR_BLOCK_READ, -- HUGE_STRING_LEN); -+ len); - if (status == APR_SUCCESS) { - --len; - status = apr_brigade_flatten(parser->tmp, line, &len); - if (status == APR_SUCCESS) { - /* we assume a non-0 containing line and remove trailing crlf. */ - line[len] = '\0'; -+ /* -+ * XXX: What to do if there is an LF but no CRLF? -+ * Should we error out? -+ */ - if (len >= 2 && !strcmp(H2_CRLF, line + len - 2)) { - len -= 2; - line[len] = '\0'; -@@ -367,10 +372,47 @@ - task->id, line); - } - else { -+ apr_off_t brigade_length; -+ -+ /* -+ * If the brigade parser->tmp becomes longer than our buffer -+ * for flattening we never have a chance to get a complete -+ * line. This can happen if we are called multiple times after -+ * previous calls did not find a H2_CRLF and we returned -+ * APR_EAGAIN. In this case parser->tmp (correctly) grows -+ * with each call to apr_brigade_split_line. -+ * -+ * XXX: Currently a stack based buffer of HUGE_STRING_LEN is -+ * used. This means we cannot cope with lines larger than -+ * HUGE_STRING_LEN which might be an issue. -+ */ -+ status = apr_brigade_length(parser->tmp, 0, &brigade_length); -+ if ((status != APR_SUCCESS) || (brigade_length > len)) { -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, task->c, APLOGNO(10257) -+ "h2_task(%s): read response, line too long", -+ task->id); -+ return APR_ENOSPC; -+ } - /* this does not look like a complete line yet */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, - "h2_task(%s): read response, incomplete line: %s", - task->id, line); -+ if (!parser->saveto) { -+ parser->saveto = apr_brigade_create(task->pool, -+ task->c->bucket_alloc); -+ } -+ /* -+ * Be on the save side and save the parser->tmp brigade -+ * as it could contain transient buckets which could be -+ * invalid next time we are here. -+ * -+ * NULL for the filter parameter is ok since we -+ * provide our own brigade as second parameter -+ * and ap_save_brigade does not need to create one. -+ */ -+ ap_save_brigade(NULL, &(parser->saveto), &(parser->tmp), -+ parser->tmp->p); -+ APR_BRIGADE_CONCAT(parser->tmp, parser->saveto); - return APR_EAGAIN; - } - } -@@ -594,18 +636,20 @@ - } - } - -- if (r->header_only) { -+ if (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, -- "h2_task(%s): header_only, cleanup output brigade", -+ "h2_task(%s): headers only, cleanup output brigade", - task->id); - b = body_bucket? body_bucket : APR_BRIGADE_FIRST(bb); - while (b != APR_BRIGADE_SENTINEL(bb)) { - next = APR_BUCKET_NEXT(b); - if (APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b)) { - break; -- } -- APR_BUCKET_REMOVE(b); -- apr_bucket_destroy(b); -+ } -+ if (!H2_BUCKET_IS_HEADERS(b)) { -+ APR_BUCKET_REMOVE(b); -+ apr_bucket_destroy(b); -+ } - b = next; - } - } ---- a/modules/http2/h2_h2.c -+++ b/modules/http2/h2_h2.c -@@ -463,19 +463,18 @@ - return opt_ssl_is_https && opt_ssl_is_https(c); - } - --int h2_is_acceptable_connection(conn_rec *c, int require_all) -+int h2_is_acceptable_connection(conn_rec *c, request_rec *r, int require_all) - { - int is_tls = h2_h2_is_tls(c); -- const h2_config *cfg = h2_config_get(c); - -- if (is_tls && h2_config_geti(cfg, H2_CONF_MODERN_TLS_ONLY) > 0) { -+ if (is_tls && h2_config_cgeti(c, H2_CONF_MODERN_TLS_ONLY) > 0) { - /* Check TLS connection for modern TLS parameters, as defined in - * RFC 7540 and https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility - */ - apr_pool_t *pool = c->pool; - server_rec *s = c->base_server; - char *val; -- -+ - if (!opt_ssl_var_lookup) { - /* unable to check */ - return 0; -@@ -521,33 +520,29 @@ - return 1; - } - --int h2_allows_h2_direct(conn_rec *c) -+static int h2_allows_h2_direct(conn_rec *c) - { -- const h2_config *cfg = h2_config_get(c); - int is_tls = h2_h2_is_tls(c); - const char *needed_protocol = is_tls? "h2" : "h2c"; -- int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT); -+ int h2_direct = h2_config_cgeti(c, H2_CONF_DIRECT); - - if (h2_direct < 0) { - h2_direct = is_tls? 0 : 1; - } -- return (h2_direct -- && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol)); -+ return (h2_direct && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol)); - } - --int h2_allows_h2_upgrade(conn_rec *c) -+int h2_allows_h2_upgrade(request_rec *r) - { -- const h2_config *cfg = h2_config_get(c); -- int h2_upgrade = h2_config_geti(cfg, H2_CONF_UPGRADE); -- -- return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(c)); -+ int h2_upgrade = h2_config_rgeti(r, H2_CONF_UPGRADE); -+ return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(r->connection)); - } - - /******************************************************************************* - * Register various hooks - */ - static const char* const mod_ssl[] = { "mod_ssl.c", NULL}; --static const char* const mod_reqtimeout[] = { "mod_reqtimeout.c", NULL}; -+static const char* const mod_reqtimeout[] = { "mod_ssl.c", "mod_reqtimeout.c", NULL}; - - void h2_h2_register_hooks(void) - { -@@ -558,7 +553,7 @@ - * a chance to take over before it. - */ - ap_hook_process_connection(h2_h2_process_conn, -- mod_ssl, mod_reqtimeout, APR_HOOK_LAST); -+ mod_reqtimeout, NULL, APR_HOOK_LAST); - - /* One last chance to properly say goodbye if we have not done so - * already. */ -@@ -581,14 +576,17 @@ - { - apr_status_t status; - h2_ctx *ctx; -+ server_rec *s; - - if (c->master) { - return DECLINED; - } - - ctx = h2_ctx_get(c, 0); -+ s = ctx? ctx->server : c->base_server; -+ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn"); -- if (h2_ctx_is_task(ctx)) { -+ if (ctx && ctx->task) { - /* our stream pseudo connection */ - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, task, declined"); - return DECLINED; -@@ -601,19 +599,19 @@ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, " - "new connection using protocol '%s', direct=%d, " - "tls acceptable=%d", proto, h2_allows_h2_direct(c), -- h2_is_acceptable_connection(c, 1)); -+ h2_is_acceptable_connection(c, NULL, 1)); - } - - if (!strcmp(AP_PROTOCOL_HTTP1, proto) - && h2_allows_h2_direct(c) -- && h2_is_acceptable_connection(c, 1)) { -+ && h2_is_acceptable_connection(c, NULL, 1)) { - /* Fresh connection still is on http/1.1 and H2Direct is enabled. - * Otherwise connection is in a fully acceptable state. - * -> peek at the first 24 incoming bytes - */ - apr_bucket_brigade *temp; -- char *s = NULL; -- apr_size_t slen; -+ char *peek = NULL; -+ apr_size_t peeklen; - - temp = apr_brigade_create(c->pool, c->bucket_alloc); - status = ap_get_brigade(c->input_filters, temp, -@@ -626,8 +624,8 @@ - return DECLINED; - } - -- apr_brigade_pflatten(temp, &s, &slen, c->pool); -- if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { -+ apr_brigade_pflatten(temp, &peek, &peeklen, c->pool); -+ if ((peeklen >= 24) && !memcmp(H2_MAGIC_TOKEN, peek, 24)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - "h2_h2, direct mode detected"); - if (!ctx) { -@@ -638,7 +636,7 @@ - else if (APLOGctrace2(c)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, - "h2_h2, not detected in %d bytes(base64): %s", -- (int)slen, h2_util_base64url_encode(s, slen, c->pool)); -+ (int)peeklen, h2_util_base64url_encode(peek, peeklen, c->pool)); - } - - apr_brigade_destroy(temp); -@@ -647,15 +645,16 @@ - - if (ctx) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn"); -- if (!h2_ctx_session_get(ctx)) { -- status = h2_conn_setup(ctx, c, NULL); -+ -+ if (!h2_ctx_get_session(c)) { -+ status = h2_conn_setup(c, NULL, s); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c, "conn_setup"); - if (status != APR_SUCCESS) { - h2_ctx_clear(c); - return !OK; - } - } -- h2_conn_run(ctx, c); -+ h2_conn_run(c); - return OK; - } - -@@ -667,7 +666,7 @@ - { - h2_ctx *ctx; - -- /* slave connection? */ -+ /* secondary connection? */ - if (c->master) { - return DECLINED; - } -@@ -684,16 +683,17 @@ - - static void check_push(request_rec *r, const char *tag) - { -- const h2_config *conf = h2_config_rget(r); -- if (!r->expecting_100 -- && conf && conf->push_list && conf->push_list->nelts > 0) { -+ apr_array_header_t *push_list = h2_config_push_list(r); -+ -+ if (!r->expecting_100 && push_list && push_list->nelts > 0) { - int i, old_status; - const char *old_line; -+ - ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, - "%s, early announcing %d resources for push", -- tag, conf->push_list->nelts); -- for (i = 0; i < conf->push_list->nelts; ++i) { -- h2_push_res *push = &APR_ARRAY_IDX(conf->push_list, i, h2_push_res); -+ tag, push_list->nelts); -+ for (i = 0; i < push_list->nelts; ++i) { -+ h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res); - apr_table_add(r->headers_out, "Link", - apr_psprintf(r->pool, "<%s>; rel=preload%s", - push->uri_ref, push->critical? "; critical" : "")); -@@ -710,10 +710,9 @@ - - static int h2_h2_post_read_req(request_rec *r) - { -- /* slave connection? */ -+ /* secondary connection? */ - if (r->connection->master) { -- h2_ctx *ctx = h2_ctx_rget(r); -- struct h2_task *task = h2_ctx_get_task(ctx); -+ struct h2_task *task = h2_ctx_get_task(r->connection); - /* This hook will get called twice on internal redirects. Take care - * that we manipulate filters only once. */ - if (task && !task->filters_set) { -@@ -730,7 +729,7 @@ - ap_add_output_filter("H2_RESPONSE", task, r, r->connection); - - for (f = r->input_filters; f; f = f->next) { -- if (!strcmp("H2_SLAVE_IN", f->frec->name)) { -+ if (!strcmp("H2_SECONDARY_IN", f->frec->name)) { - f->r = r; - break; - } -@@ -744,17 +743,15 @@ - - static int h2_h2_late_fixups(request_rec *r) - { -- /* slave connection? */ -+ /* secondary connection? */ - if (r->connection->master) { -- h2_ctx *ctx = h2_ctx_rget(r); -- struct h2_task *task = h2_ctx_get_task(ctx); -+ struct h2_task *task = h2_ctx_get_task(r->connection); - if (task) { - /* check if we copy vs. setaside files in this location */ -- task->output.copy_files = h2_config_geti(h2_config_rget(r), -- H2_CONF_COPY_FILES); -+ task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES); - if (task->output.copy_files) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, -- "h2_slave_out(%s): copy_files on", task->id); -+ "h2_secondary_out(%s): copy_files on", task->id); - h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL); - } - check_push(r, "late_fixup"); ---- a/modules/http2/h2_h2.h -+++ b/modules/http2/h2_h2.h -@@ -57,23 +57,15 @@ - * the handshake is still ongoing. - * @return != 0 iff connection requirements are met - */ --int h2_is_acceptable_connection(conn_rec *c, int require_all); -- --/** -- * Check if the "direct" HTTP/2 mode of protocol handling is enabled -- * for the given connection. -- * @param c the connection to check -- * @return != 0 iff direct mode is enabled -- */ --int h2_allows_h2_direct(conn_rec *c); -+int h2_is_acceptable_connection(conn_rec *c, request_rec *r, int require_all); - - /** - * Check if the "Upgrade" HTTP/1.1 mode of protocol switching is enabled -- * for the given connection. -- * @param c the connection to check -+ * for the given request. -+ * @param r the request to check - * @return != 0 iff Upgrade switching is enabled - */ --int h2_allows_h2_upgrade(conn_rec *c); -+int h2_allows_h2_upgrade(request_rec *r); - - - #endif /* defined(__mod_h2__h2_h2__) */ ---- a/modules/http2/h2_headers.c -+++ b/modules/http2/h2_headers.c -@@ -28,6 +28,7 @@ - - #include "h2_private.h" - #include "h2_h2.h" -+#include "h2_config.h" - #include "h2_util.h" - #include "h2_request.h" - #include "h2_headers.h" -@@ -101,8 +102,9 @@ - const apr_bucket *src) - { - if (H2_BUCKET_IS_HEADERS(src)) { -- h2_headers *r = ((h2_bucket_headers *)src->data)->headers; -- apr_bucket *b = h2_bucket_headers_create(dest->bucket_alloc, r); -+ h2_headers *src_headers = ((h2_bucket_headers *)src->data)->headers; -+ apr_bucket *b = h2_bucket_headers_create(dest->bucket_alloc, -+ h2_headers_clone(dest->p, src_headers)); - APR_BRIGADE_INSERT_TAIL(dest, b); - return b; - } -@@ -128,28 +130,41 @@ - { - h2_headers *headers = h2_headers_create(status, header, r->notes, 0, pool); - if (headers->status == HTTP_FORBIDDEN) { -- const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden"); -- if (cause) { -- /* This request triggered a TLS renegotiation that is now allowed -- * in HTTP/2. Tell the client that it should use HTTP/1.1 for this. -- */ -- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, headers->status, r, -- APLOGNO(03061) -- "h2_headers(%ld): renegotiate forbidden, cause: %s", -- (long)r->connection->id, cause); -- headers->status = H2_ERR_HTTP_1_1_REQUIRED; -+ request_rec *r_prev; -+ for (r_prev = r; r_prev != NULL; r_prev = r_prev->prev) { -+ const char *cause = apr_table_get(r_prev->notes, "ssl-renegotiate-forbidden"); -+ if (cause) { -+ /* This request triggered a TLS renegotiation that is not allowed -+ * in HTTP/2. Tell the client that it should use HTTP/1.1 for this. -+ */ -+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, headers->status, r, -+ APLOGNO(03061) -+ "h2_headers(%ld): renegotiate forbidden, cause: %s", -+ (long)r->connection->id, cause); -+ headers->status = H2_ERR_HTTP_1_1_REQUIRED; -+ break; -+ } - } - } - if (is_unsafe(r->server)) { -- apr_table_setn(headers->notes, H2_HDR_CONFORMANCE, -- H2_HDR_CONFORMANCE_UNSAFE); -+ apr_table_setn(headers->notes, H2_HDR_CONFORMANCE, H2_HDR_CONFORMANCE_UNSAFE); -+ } -+ if (h2_config_rgeti(r, H2_CONF_PUSH) == 0 && h2_config_sgeti(r->server, H2_CONF_PUSH) != 0) { -+ apr_table_setn(headers->notes, H2_PUSH_MODE_NOTE, "0"); - } - return headers; - } - - h2_headers *h2_headers_copy(apr_pool_t *pool, h2_headers *h) - { -- return h2_headers_create(h->status, h->headers, h->notes, h->raw_bytes, pool); -+ return h2_headers_create(h->status, apr_table_copy(pool, h->headers), -+ apr_table_copy(pool, h->notes), h->raw_bytes, pool); -+} -+ -+h2_headers *h2_headers_clone(apr_pool_t *pool, h2_headers *h) -+{ -+ return h2_headers_create(h->status, apr_table_clone(pool, h->headers), -+ apr_table_clone(pool, h->notes), h->raw_bytes, pool); - } - - h2_headers *h2_headers_die(apr_status_t type, ---- a/modules/http2/h2_headers.h -+++ b/modules/http2/h2_headers.h -@@ -59,12 +59,18 @@ - apr_table_t *header, apr_pool_t *pool); - - /** -- * Clone the headers into another pool. This will not copy any -+ * Copy the headers into another pool. This will not copy any - * header strings. - */ - h2_headers *h2_headers_copy(apr_pool_t *pool, h2_headers *h); - - /** -+ * Clone the headers into another pool. This will also clone any -+ * header strings. -+ */ -+h2_headers *h2_headers_clone(apr_pool_t *pool, h2_headers *h); -+ -+/** - * Create the headers for the given error. - * @param stream_id id of the stream to create the headers for - * @param type the error code ---- a/modules/http2/h2_mplx.c -+++ b/modules/http2/h2_mplx.c -@@ -40,7 +40,6 @@ - #include "h2_ctx.h" - #include "h2_h2.h" - #include "h2_mplx.h" --#include "h2_ngn_shed.h" - #include "h2_request.h" - #include "h2_stream.h" - #include "h2_session.h" -@@ -54,9 +53,21 @@ - h2_mplx *m; - h2_stream *stream; - apr_time_t now; -+ apr_size_t count; - } stream_iter_ctx; - --apr_status_t h2_mplx_child_init(apr_pool_t *pool, server_rec *s) -+/** -+ * Naming convention for static functions: -+ * - m_*: function only called from the master connection -+ * - s_*: function only called from a secondary connection -+ * - t_*: function only called from a h2_task holder -+ * - mst_*: function called from everyone -+ */ -+ -+static apr_status_t s_mplx_be_happy(h2_mplx *m, h2_task *task); -+static apr_status_t m_be_annoyed(h2_mplx *m); -+ -+apr_status_t h2_mplx_m_child_init(apr_pool_t *pool, server_rec *s) - { - return APR_SUCCESS; - } -@@ -72,46 +83,40 @@ - #define H2_MPLX_ENTER_ALWAYS(m) \ - apr_thread_mutex_lock(m->lock) - --#define H2_MPLX_ENTER_MAYBE(m, lock) \ -- if (lock) apr_thread_mutex_lock(m->lock) -+#define H2_MPLX_ENTER_MAYBE(m, dolock) \ -+ if (dolock) apr_thread_mutex_lock(m->lock) - --#define H2_MPLX_LEAVE_MAYBE(m, lock) \ -- if (lock) apr_thread_mutex_unlock(m->lock) -+#define H2_MPLX_LEAVE_MAYBE(m, dolock) \ -+ if (dolock) apr_thread_mutex_unlock(m->lock) - --static void check_data_for(h2_mplx *m, h2_stream *stream, int lock); -+static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked); - --static void stream_output_consumed(void *ctx, -- h2_bucket_beam *beam, apr_off_t length) -+static void mst_stream_output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) - { -- h2_stream *stream = ctx; -- h2_task *task = stream->task; -- -- if (length > 0 && task && task->assigned) { -- h2_req_engine_out_consumed(task->assigned, task->c, length); -- } - } - --static void stream_input_ev(void *ctx, h2_bucket_beam *beam) -+static void mst_stream_input_ev(void *ctx, h2_bucket_beam *beam) - { - h2_stream *stream = ctx; - h2_mplx *m = stream->session->mplx; - apr_atomic_set32(&m->event_pending, 1); - } - --static void stream_input_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) -+static void m_stream_input_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) - { - h2_stream_in_consumed(ctx, length); - } - --static void stream_joined(h2_mplx *m, h2_stream *stream) -+static void ms_stream_joined(h2_mplx *m, h2_stream *stream) - { -- ap_assert(!stream->task || stream->task->worker_done); -+ ap_assert(!h2_task_has_started(stream->task) || stream->task->worker_done); - -+ h2_ififo_remove(m->readyq, stream->id); - h2_ihash_remove(m->shold, stream->id); - h2_ihash_add(m->spurge, stream); - } - --static void stream_cleanup(h2_mplx *m, h2_stream *stream) -+static void m_stream_cleanup(h2_mplx *m, h2_stream *stream) - { - ap_assert(stream->state == H2_SS_CLEANUP); - -@@ -128,15 +133,16 @@ - - h2_ihash_remove(m->streams, stream->id); - h2_iq_remove(m->q, stream->id); -- h2_ififo_remove(m->readyq, stream->id); -- h2_ihash_add(m->shold, stream); - -- if (!stream->task || stream->task->worker_done) { -- stream_joined(m, stream); -+ if (!h2_task_has_started(stream->task) || stream->task->done_done) { -+ ms_stream_joined(m, stream); - } -- else if (stream->task) { -- stream->task->c->aborted = 1; -- apr_thread_cond_broadcast(m->task_thawed); -+ else { -+ h2_ififo_remove(m->readyq, stream->id); -+ h2_ihash_add(m->shold, stream); -+ if (stream->task) { -+ stream->task->c->aborted = 1; -+ } - } - } - -@@ -151,29 +157,23 @@ - * their HTTP/1 cousins, the separate allocator seems to work better - * than protecting a shared h2_session one with an own lock. - */ --h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, -- const h2_config *conf, -- h2_workers *workers) -+h2_mplx *h2_mplx_m_create(conn_rec *c, server_rec *s, apr_pool_t *parent, -+ h2_workers *workers) - { - apr_status_t status = APR_SUCCESS; - apr_allocator_t *allocator; - apr_thread_mutex_t *mutex; - h2_mplx *m; -- h2_ctx *ctx = h2_ctx_get(c, 0); -- ap_assert(conf); - - m = apr_pcalloc(parent, sizeof(h2_mplx)); - if (m) { - m->id = c->id; - m->c = c; -- m->s = (ctx? h2_ctx_server_get(ctx) : NULL); -- if (!m->s) { -- m->s = c->base_server; -- } -+ m->s = s; - - /* We create a pool with its own allocator to be used for -- * processing slave connections. This is the only way to have the -- * processing independant of its parent pool in the sense that it -+ * processing secondary connections. This is the only way to have the -+ * processing independent of its parent pool in the sense that it - * can work in another thread. Also, the new allocator needs its own - * mutex to synchronize sub-pools. - */ -@@ -204,17 +204,10 @@ - return NULL; - } - -- status = apr_thread_cond_create(&m->task_thawed, m->pool); -- if (status != APR_SUCCESS) { -- apr_pool_destroy(m->pool); -- return NULL; -- } -- -- m->max_streams = h2_config_geti(conf, H2_CONF_MAX_STREAMS); -- m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM); -+ m->max_streams = h2_config_sgeti(s, H2_CONF_MAX_STREAMS); -+ m->stream_max_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM); - - m->streams = h2_ihash_create(m->pool, offsetof(h2_stream,id)); -- m->sredo = h2_ihash_create(m->pool, offsetof(h2_stream,id)); - m->shold = h2_ihash_create(m->pool, offsetof(h2_stream,id)); - m->spurge = h2_ihash_create(m->pool, offsetof(h2_stream,id)); - m->q = h2_iq_create(m->pool, m->max_streams); -@@ -228,19 +221,15 @@ - m->workers = workers; - m->max_active = workers->max_workers; - m->limit_active = 6; /* the original h1 max parallel connections */ -- m->last_limit_change = m->last_idle_block = apr_time_now(); -- m->limit_change_interval = apr_time_from_msec(100); -- -- m->spare_slaves = apr_array_make(m->pool, 10, sizeof(conn_rec*)); -+ m->last_mood_change = apr_time_now(); -+ m->mood_update_interval = apr_time_from_msec(100); - -- m->ngn_shed = h2_ngn_shed_create(m->pool, m->c, m->max_streams, -- m->stream_max_mem); -- h2_ngn_shed_set_ctx(m->ngn_shed , m); -+ m->spare_secondary = apr_array_make(m->pool, 10, sizeof(conn_rec*)); - } - return m; - } - --int h2_mplx_shutdown(h2_mplx *m) -+int h2_mplx_m_shutdown(h2_mplx *m) - { - int max_stream_started = 0; - -@@ -254,7 +243,7 @@ - return max_stream_started; - } - --static int input_consumed_signal(h2_mplx *m, h2_stream *stream) -+static int m_input_consumed_signal(h2_mplx *m, h2_stream *stream) - { - if (stream->input) { - return h2_beam_report_consumption(stream->input); -@@ -262,12 +251,12 @@ - return 0; - } - --static int report_consumption_iter(void *ctx, void *val) -+static int m_report_consumption_iter(void *ctx, void *val) - { - h2_stream *stream = val; - h2_mplx *m = ctx; - -- input_consumed_signal(m, stream); -+ m_input_consumed_signal(m, stream); - if (stream->state == H2_SS_CLOSED_L - && (!stream->task || stream->task->worker_done)) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, -@@ -278,7 +267,7 @@ - return 1; - } - --static int output_consumed_signal(h2_mplx *m, h2_task *task) -+static int s_output_consumed_signal(h2_mplx *m, h2_task *task) - { - if (task->output.beam) { - return h2_beam_report_consumption(task->output.beam); -@@ -286,7 +275,7 @@ - return 0; - } - --static int stream_destroy_iter(void *ctx, void *val) -+static int m_stream_destroy_iter(void *ctx, void *val) - { - h2_mplx *m = ctx; - h2_stream *stream = val; -@@ -296,7 +285,7 @@ - - if (stream->input) { - /* Process outstanding events before destruction */ -- input_consumed_signal(m, stream); -+ m_input_consumed_signal(m, stream); - h2_beam_log(stream->input, m->c, APLOG_TRACE2, "stream_destroy"); - h2_beam_destroy(stream->input); - stream->input = NULL; -@@ -304,12 +293,12 @@ - - if (stream->task) { - h2_task *task = stream->task; -- conn_rec *slave; -- int reuse_slave = 0; -+ conn_rec *secondary; -+ int reuse_secondary = 0; - - stream->task = NULL; -- slave = task->c; -- if (slave) { -+ secondary = task->c; -+ if (secondary) { - /* On non-serialized requests, the IO logging has not accounted for any - * meta data send over the network: response headers and h2 frame headers. we - * counted this on the stream and need to add this now. -@@ -318,26 +307,25 @@ - if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) { - apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets; - if (unaccounted > 0) { -- h2_task_logio_add_bytes_out(slave, unaccounted); -+ h2_task_logio_add_bytes_out(secondary, unaccounted); - } - } - -- if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) { -- reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2)) -- && !task->rst_error); -+ if (m->s->keep_alive_max == 0 || secondary->keepalives < m->s->keep_alive_max) { -+ reuse_secondary = ((m->spare_secondary->nelts < (m->limit_active * 3 / 2)) -+ && !task->rst_error); - } - -- task->c = NULL; -- if (reuse_slave) { -+ if (reuse_secondary) { - h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, -- APLOGNO(03385) "h2_task_destroy, reuse slave"); -+ APLOGNO(03385) "h2_task_destroy, reuse secondary"); - h2_task_destroy(task); -- APR_ARRAY_PUSH(m->spare_slaves, conn_rec*) = slave; -+ APR_ARRAY_PUSH(m->spare_secondary, conn_rec*) = secondary; - } - else { - h2_beam_log(task->output.beam, m->c, APLOG_TRACE1, -- "h2_task_destroy, destroy slave"); -- h2_slave_destroy(slave); -+ "h2_task_destroy, destroy secondary"); -+ h2_secondary_destroy(secondary); - } - } - } -@@ -345,11 +333,11 @@ - return 0; - } - --static void purge_streams(h2_mplx *m, int lock) -+static void m_purge_streams(h2_mplx *m, int lock) - { - if (!h2_ihash_empty(m->spurge)) { - H2_MPLX_ENTER_MAYBE(m, lock); -- while (!h2_ihash_iter(m->spurge, stream_destroy_iter, m)) { -+ while (!h2_ihash_iter(m->spurge, m_stream_destroy_iter, m)) { - /* repeat until empty */ - } - H2_MPLX_LEAVE_MAYBE(m, lock); -@@ -361,13 +349,13 @@ - void *ctx; - } stream_iter_ctx_t; - --static int stream_iter_wrap(void *ctx, void *stream) -+static int m_stream_iter_wrap(void *ctx, void *stream) - { - stream_iter_ctx_t *x = ctx; - return x->cb(stream, x->ctx); - } - --apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) -+apr_status_t h2_mplx_m_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) - { - stream_iter_ctx_t x; - -@@ -375,13 +363,13 @@ - - x.cb = cb; - x.ctx = ctx; -- h2_ihash_iter(m->streams, stream_iter_wrap, &x); -+ h2_ihash_iter(m->streams, m_stream_iter_wrap, &x); - - H2_MPLX_LEAVE(m); - return APR_SUCCESS; - } - --static int report_stream_iter(void *ctx, void *val) { -+static int m_report_stream_iter(void *ctx, void *val) { - h2_mplx *m = ctx; - h2_stream *stream = val; - h2_task *task = stream->task; -@@ -394,10 +382,10 @@ - if (task) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */ - H2_STRM_MSG(stream, "->03198: %s %s %s" -- "[started=%d/done=%d/frozen=%d]"), -+ "[started=%d/done=%d]"), - task->request->method, task->request->authority, - task->request->path, task->worker_started, -- task->worker_done, task->frozen); -+ task->worker_done); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */ -@@ -406,7 +394,7 @@ - return 1; - } - --static int unexpected_stream_iter(void *ctx, void *val) { -+static int m_unexpected_stream_iter(void *ctx, void *val) { - h2_mplx *m = ctx; - h2_stream *stream = val; - ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, /* NO APLOGNO */ -@@ -415,7 +403,7 @@ - return 1; - } - --static int stream_cancel_iter(void *ctx, void *val) { -+static int m_stream_cancel_iter(void *ctx, void *val) { - h2_mplx *m = ctx; - h2_stream *stream = val; - -@@ -429,14 +417,14 @@ - h2_stream_rst(stream, H2_ERR_NO_ERROR); - /* All connection data has been sent, simulate cleanup */ - h2_stream_dispatch(stream, H2_SEV_EOS_SENT); -- stream_cleanup(m, stream); -+ m_stream_cleanup(m, stream); - return 0; - } - --void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) -+void h2_mplx_m_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) - { - apr_status_t status; -- int i, wait_secs = 60; -+ int i, wait_secs = 60, old_aborted; - - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, - "h2_mplx(%ld): start release", m->id); -@@ -447,15 +435,23 @@ - - H2_MPLX_ENTER_ALWAYS(m); - -+ /* While really terminating any secondary connections, treat the master -+ * connection as aborted. It's not as if we could send any more data -+ * at this point. */ -+ old_aborted = m->c->aborted; -+ m->c->aborted = 1; -+ - /* How to shut down a h2 connection: - * 1. cancel all streams still active */ -- while (!h2_ihash_iter(m->streams, stream_cancel_iter, m)) { -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -+ "h2_mplx(%ld): release, %d/%d/%d streams (total/hold/purge), %d active tasks", -+ m->id, (int)h2_ihash_count(m->streams), -+ (int)h2_ihash_count(m->shold), (int)h2_ihash_count(m->spurge), m->tasks_active); -+ while (!h2_ihash_iter(m->streams, m_stream_cancel_iter, m)) { - /* until empty */ - } - -- /* 2. terminate ngn_shed, no more streams -- * should be scheduled or in the active set */ -- h2_ngn_shed_abort(m->ngn_shed); -+ /* 2. no more streams should be scheduled or in the active set */ - ap_assert(h2_ihash_empty(m->streams)); - ap_assert(h2_iq_empty(m->q)); - -@@ -473,65 +469,60 @@ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(03198) - "h2_mplx(%ld): waited %d sec for %d tasks", - m->id, i*wait_secs, (int)h2_ihash_count(m->shold)); -- h2_ihash_iter(m->shold, report_stream_iter, m); -+ h2_ihash_iter(m->shold, m_report_stream_iter, m); - } - } -- ap_assert(m->tasks_active == 0); - m->join_wait = NULL; -- -- /* 4. close the h2_req_enginge shed */ -- h2_ngn_shed_destroy(m->ngn_shed); -- m->ngn_shed = NULL; -- -+ - /* 4. With all workers done, all streams should be in spurge */ -+ ap_assert(m->tasks_active == 0); - if (!h2_ihash_empty(m->shold)) { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(03516) - "h2_mplx(%ld): unexpected %d streams in hold", - m->id, (int)h2_ihash_count(m->shold)); -- h2_ihash_iter(m->shold, unexpected_stream_iter, m); -+ h2_ihash_iter(m->shold, m_unexpected_stream_iter, m); - } - -+ m->c->aborted = old_aborted; - H2_MPLX_LEAVE(m); - -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -- "h2_mplx(%ld): released", m->id); -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, "h2_mplx(%ld): released", m->id); - } - --apr_status_t h2_mplx_stream_cleanup(h2_mplx *m, h2_stream *stream) -+apr_status_t h2_mplx_m_stream_cleanup(h2_mplx *m, h2_stream *stream) - { - H2_MPLX_ENTER(m); - - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, - H2_STRM_MSG(stream, "cleanup")); -- stream_cleanup(m, stream); -+ m_stream_cleanup(m, stream); - - H2_MPLX_LEAVE(m); - return APR_SUCCESS; - } - --h2_stream *h2_mplx_stream_get(h2_mplx *m, int id) -+h2_stream *h2_mplx_t_stream_get(h2_mplx *m, h2_task *task) - { - h2_stream *s = NULL; - - H2_MPLX_ENTER_ALWAYS(m); - -- s = h2_ihash_get(m->streams, id); -+ s = h2_ihash_get(m->streams, task->stream_id); - - H2_MPLX_LEAVE(m); - return s; - } - --static void output_produced(void *ctx, h2_bucket_beam *beam, apr_off_t bytes) -+static void mst_output_produced(void *ctx, h2_bucket_beam *beam, apr_off_t bytes) - { - h2_stream *stream = ctx; - h2_mplx *m = stream->session->mplx; - -- check_data_for(m, stream, 1); -+ mst_check_data_for(m, stream, 0); - } - --static apr_status_t out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) -+static apr_status_t t_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) - { -- apr_status_t status = APR_SUCCESS; - h2_stream *stream = h2_ihash_get(m->streams, stream_id); - - if (!stream || !stream->task || m->aborted) { -@@ -542,26 +533,26 @@ - stream->output = beam; - - if (APLOGctrace2(m->c)) { -- h2_beam_log(beam, m->c, APLOG_TRACE2, "out_open"); -+ h2_beam_log(beam, stream->task->c, APLOG_TRACE2, "out_open"); - } - else { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c, -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->task->c, - "h2_mplx(%s): out open", stream->task->id); - } - -- h2_beam_on_consumed(stream->output, NULL, stream_output_consumed, stream); -- h2_beam_on_produced(stream->output, output_produced, stream); -+ h2_beam_on_consumed(stream->output, NULL, mst_stream_output_consumed, stream); -+ h2_beam_on_produced(stream->output, mst_output_produced, stream); - if (stream->task->output.copy_files) { - h2_beam_on_file_beam(stream->output, h2_beam_no_files, NULL); - } - - /* we might see some file buckets in the output, see - * if we have enough handles reserved. */ -- check_data_for(m, stream, 0); -- return status; -+ mst_check_data_for(m, stream, 1); -+ return APR_SUCCESS; - } - --apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) -+apr_status_t h2_mplx_t_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam) - { - apr_status_t status; - -@@ -571,14 +562,14 @@ - status = APR_ECONNABORTED; - } - else { -- status = out_open(m, stream_id, beam); -+ status = t_out_open(m, stream_id, beam); - } - - H2_MPLX_LEAVE(m); - return status; - } - --static apr_status_t out_close(h2_mplx *m, h2_task *task) -+static apr_status_t s_out_close(h2_mplx *m, h2_task *task) - { - apr_status_t status = APR_SUCCESS; - h2_stream *stream; -@@ -595,17 +586,17 @@ - return APR_ECONNABORTED; - } - -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c, -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, task->c, - "h2_mplx(%s): close", task->id); - status = h2_beam_close(task->output.beam); -- h2_beam_log(task->output.beam, m->c, APLOG_TRACE2, "out_close"); -- output_consumed_signal(m, task); -- check_data_for(m, stream, 0); -+ h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "out_close"); -+ s_output_consumed_signal(m, task); -+ mst_check_data_for(m, stream, 1); - return status; - } - --apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, -- apr_thread_cond_t *iowait) -+apr_status_t h2_mplx_m_out_trywait(h2_mplx *m, apr_interval_time_t timeout, -+ apr_thread_cond_t *iowait) - { - apr_status_t status; - -@@ -614,12 +605,12 @@ - if (m->aborted) { - status = APR_ECONNABORTED; - } -- else if (h2_mplx_has_master_events(m)) { -+ else if (h2_mplx_m_has_master_events(m)) { - status = APR_SUCCESS; - } - else { -- purge_streams(m, 0); -- h2_ihash_iter(m->streams, report_consumption_iter, m); -+ m_purge_streams(m, 0); -+ h2_ihash_iter(m->streams, m_report_consumption_iter, m); - m->added_output = iowait; - status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout); - if (APLOGctrace2(m->c)) { -@@ -634,19 +625,27 @@ - return status; - } - --static void check_data_for(h2_mplx *m, h2_stream *stream, int lock) -+static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked) - { -+ /* If m->lock is already held, we must release during h2_ififo_push() -+ * which can wait on its not_full condition, causing a deadlock because -+ * no one would then be able to acquire m->lock to empty the fifo. -+ */ -+ H2_MPLX_LEAVE_MAYBE(m, mplx_is_locked); - if (h2_ififo_push(m->readyq, stream->id) == APR_SUCCESS) { -+ H2_MPLX_ENTER_ALWAYS(m); - apr_atomic_set32(&m->event_pending, 1); -- H2_MPLX_ENTER_MAYBE(m, lock); - if (m->added_output) { - apr_thread_cond_signal(m->added_output); - } -- H2_MPLX_LEAVE_MAYBE(m, lock); -+ H2_MPLX_LEAVE_MAYBE(m, !mplx_is_locked); -+ } -+ else { -+ H2_MPLX_ENTER_MAYBE(m, mplx_is_locked); - } - } - --apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) -+apr_status_t h2_mplx_m_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) - { - apr_status_t status; - -@@ -666,22 +665,22 @@ - return status; - } - --static void register_if_needed(h2_mplx *m) -+static void ms_register_if_needed(h2_mplx *m, int from_master) - { - if (!m->aborted && !m->is_registered && !h2_iq_empty(m->q)) { - apr_status_t status = h2_workers_register(m->workers, m); - if (status == APR_SUCCESS) { - m->is_registered = 1; - } -- else { -+ else if (from_master) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, m->c, APLOGNO(10021) - "h2_mplx(%ld): register at workers", m->id); - } - } - } - --apr_status_t h2_mplx_process(h2_mplx *m, struct h2_stream *stream, -- h2_stream_pri_cmp *cmp, void *ctx) -+apr_status_t h2_mplx_m_process(h2_mplx *m, struct h2_stream *stream, -+ h2_stream_pri_cmp *cmp, void *ctx) - { - apr_status_t status; - -@@ -695,13 +694,13 @@ - h2_ihash_add(m->streams, stream); - if (h2_stream_is_ready(stream)) { - /* already have a response */ -- check_data_for(m, stream, 0); -+ mst_check_data_for(m, stream, 1); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, - H2_STRM_MSG(stream, "process, add to readyq")); - } - else { - h2_iq_add(m->q, stream->id, cmp, ctx); -- register_if_needed(m); -+ ms_register_if_needed(m, 1); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, - H2_STRM_MSG(stream, "process, added to q")); - } -@@ -711,7 +710,7 @@ - return status; - } - --static h2_task *next_stream_task(h2_mplx *m) -+static h2_task *s_next_stream_task(h2_mplx *m) - { - h2_stream *stream; - int sid; -@@ -720,40 +719,39 @@ - - stream = h2_ihash_get(m->streams, sid); - if (stream) { -- conn_rec *slave, **pslave; -+ conn_rec *secondary, **psecondary; - -- pslave = (conn_rec **)apr_array_pop(m->spare_slaves); -- if (pslave) { -- slave = *pslave; -- slave->aborted = 0; -+ psecondary = (conn_rec **)apr_array_pop(m->spare_secondary); -+ if (psecondary) { -+ secondary = *psecondary; -+ secondary->aborted = 0; - } - else { -- slave = h2_slave_create(m->c, stream->id, m->pool); -+ secondary = h2_secondary_create(m->c, stream->id, m->pool); - } - - if (!stream->task) { -- - if (sid > m->max_stream_started) { - m->max_stream_started = sid; - } - if (stream->input) { -- h2_beam_on_consumed(stream->input, stream_input_ev, -- stream_input_consumed, stream); -+ h2_beam_on_consumed(stream->input, mst_stream_input_ev, -+ m_stream_input_consumed, stream); - } - -- stream->task = h2_task_create(slave, stream->id, -+ stream->task = h2_task_create(secondary, stream->id, - stream->request, m, stream->input, - stream->session->s->timeout, - m->stream_max_mem); - if (!stream->task) { -- ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, slave, -+ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, secondary, - H2_STRM_LOG(APLOGNO(02941), stream, - "create task")); - return NULL; - } -- - } - -+ stream->task->started_at = apr_time_now(); - ++m->tasks_active; - return stream->task; - } -@@ -761,7 +759,7 @@ - return NULL; - } - --apr_status_t h2_mplx_pop_task(h2_mplx *m, h2_task **ptask) -+apr_status_t h2_mplx_s_pop_task(h2_mplx *m, h2_task **ptask) - { - apr_status_t rv = APR_EOF; - -@@ -777,7 +775,7 @@ - rv = APR_EOF; - } - else { -- *ptask = next_stream_task(m); -+ *ptask = s_next_stream_task(m); - rv = (*ptask != NULL && !h2_iq_empty(m->q))? APR_EAGAIN : APR_SUCCESS; - } - if (APR_EAGAIN != rv) { -@@ -787,127 +785,87 @@ - return rv; - } - --static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn) -+static void s_task_done(h2_mplx *m, h2_task *task) - { - h2_stream *stream; - -- if (task->frozen) { -- /* this task was handed over to an engine for processing -- * and the original worker has finished. That means the -- * engine may start processing now. */ -- h2_task_thaw(task); -- apr_thread_cond_broadcast(m->task_thawed); -- return; -- } -- -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, - "h2_mplx(%ld): task(%s) done", m->id, task->id); -- out_close(m, task); -- -- if (ngn) { -- apr_off_t bytes = 0; -- h2_beam_send(task->output.beam, NULL, APR_NONBLOCK_READ); -- bytes += h2_beam_get_buffered(task->output.beam); -- if (bytes > 0) { -- /* we need to report consumed and current buffered output -- * to the engine. The request will be streamed out or cancelled, -- * no more data is coming from it and the engine should update -- * its calculations before we destroy this information. */ -- h2_req_engine_out_consumed(ngn, task->c, bytes); -- } -- } -- -- if (task->engine) { -- if (!m->aborted && !task->c->aborted -- && !h2_req_engine_is_shutdown(task->engine)) { -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(10022) -- "h2_mplx(%ld): task(%s) has not-shutdown " -- "engine(%s)", m->id, task->id, -- h2_req_engine_get_id(task->engine)); -- } -- h2_ngn_shed_done_ngn(m->ngn_shed, task->engine); -- } -+ s_out_close(m, task); - - task->worker_done = 1; - task->done_at = apr_time_now(); -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, - "h2_mplx(%s): request done, %f ms elapsed", task->id, - (task->done_at - task->started_at) / 1000.0); - -- if (task->started_at > m->last_idle_block) { -- /* this task finished without causing an 'idle block', e.g. -- * a block by flow control. -- */ -- if (task->done_at- m->last_limit_change >= m->limit_change_interval -- && m->limit_active < m->max_active) { -- /* Well behaving stream, allow it more workers */ -- m->limit_active = H2MIN(m->limit_active * 2, -- m->max_active); -- m->last_limit_change = task->done_at; -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -- "h2_mplx(%ld): increase worker limit to %d", -- m->id, m->limit_active); -- } -+ if (task->c && !task->c->aborted && task->started_at > m->last_mood_change) { -+ s_mplx_be_happy(m, task); - } - -+ ap_assert(task->done_done == 0); -+ - stream = h2_ihash_get(m->streams, task->stream_id); - if (stream) { - /* stream not done yet. */ -- if (!m->aborted && h2_ihash_get(m->sredo, stream->id)) { -+ if (!m->aborted && task->redo) { - /* reset and schedule again */ - h2_task_redo(task); -- h2_ihash_remove(m->sredo, stream->id); - h2_iq_add(m->q, stream->id, NULL, NULL); -+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, -+ H2_STRM_MSG(stream, "redo, added to q")); - } - else { - /* stream not cleaned up, stay around */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, -+ task->done_done = 1; -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, - H2_STRM_MSG(stream, "task_done, stream open")); - if (stream->input) { - h2_beam_leave(stream->input); - } - - /* more data will not arrive, resume the stream */ -- check_data_for(m, stream, 0); -+ mst_check_data_for(m, stream, 1); - } - } - else if ((stream = h2_ihash_get(m->shold, task->stream_id)) != NULL) { - /* stream is done, was just waiting for this. */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, -+ task->done_done = 1; -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, - H2_STRM_MSG(stream, "task_done, in hold")); - if (stream->input) { - h2_beam_leave(stream->input); - } -- stream_joined(m, stream); -+ ms_stream_joined(m, stream); - } - else if ((stream = h2_ihash_get(m->spurge, task->stream_id)) != NULL) { -- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, -+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, task->c, - H2_STRM_LOG(APLOGNO(03517), stream, "already in spurge")); - ap_assert("stream should not be in spurge" == NULL); - } - else { -- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(03518) -+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, task->c, APLOGNO(03518) - "h2_mplx(%s): task_done, stream not found", - task->id); - ap_assert("stream should still be available" == NULL); - } - } - --void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask) -+void h2_mplx_s_task_done(h2_mplx *m, h2_task *task, h2_task **ptask) - { - H2_MPLX_ENTER_ALWAYS(m); - -- task_done(m, task, NULL); - --m->tasks_active; -+ s_task_done(m, task); - - if (m->join_wait) { - apr_thread_cond_signal(m->join_wait); - } - if (ptask) { - /* caller wants another task */ -- *ptask = next_stream_task(m); -+ *ptask = s_next_stream_task(m); - } -- register_if_needed(m); -+ ms_register_if_needed(m, 0); - - H2_MPLX_LEAVE(m); - } -@@ -916,94 +874,161 @@ - * h2_mplx DoS protection - ******************************************************************************/ - --static int latest_repeatable_unsubmitted_iter(void *data, void *val) -+static int m_timed_out_busy_iter(void *data, void *val) - { - stream_iter_ctx *ctx = data; - h2_stream *stream = val; -- -- if (stream->task && !stream->task->worker_done -- && h2_task_can_redo(stream->task) -- && !h2_ihash_get(ctx->m->sredo, stream->id)) { -- if (!h2_stream_is_ready(stream)) { -- /* this task occupies a worker, the response has not been submitted -- * yet, not been cancelled and it is a repeatable request -- * -> it can be re-scheduled later */ -- if (!ctx->stream -- || (ctx->stream->task->started_at < stream->task->started_at)) { -- /* we did not have one or this one was started later */ -- ctx->stream = stream; -- } -- } -+ if (h2_task_has_started(stream->task) && !stream->task->worker_done -+ && (ctx->now - stream->task->started_at) > stream->task->timeout) { -+ /* timed out stream occupying a worker, found */ -+ ctx->stream = stream; -+ return 0; - } - return 1; - } - --static h2_stream *get_latest_repeatable_unsubmitted_stream(h2_mplx *m) -+static h2_stream *m_get_timed_out_busy_stream(h2_mplx *m) - { - stream_iter_ctx ctx; - ctx.m = m; - ctx.stream = NULL; -- h2_ihash_iter(m->streams, latest_repeatable_unsubmitted_iter, &ctx); -+ ctx.now = apr_time_now(); -+ h2_ihash_iter(m->streams, m_timed_out_busy_iter, &ctx); - return ctx.stream; - } - --static int timed_out_busy_iter(void *data, void *val) -+static int m_latest_repeatable_unsubmitted_iter(void *data, void *val) - { - stream_iter_ctx *ctx = data; - h2_stream *stream = val; -- if (stream->task && !stream->task->worker_done -- && (ctx->now - stream->task->started_at) > stream->task->timeout) { -- /* timed out stream occupying a worker, found */ -- ctx->stream = stream; -- return 0; -+ -+ if (!stream->task) goto leave; -+ if (!h2_task_has_started(stream->task) || stream->task->worker_done) goto leave; -+ if (h2_stream_is_ready(stream)) goto leave; -+ if (stream->task->redo) { -+ ++ctx->count; -+ goto leave; -+ } -+ if (h2_task_can_redo(stream->task)) { -+ /* this task occupies a worker, the response has not been submitted -+ * yet, not been cancelled and it is a repeatable request -+ * -> we could redo it later */ -+ if (!ctx->stream -+ || (ctx->stream->task->started_at < stream->task->started_at)) { -+ /* we did not have one or this one was started later */ -+ ctx->stream = stream; -+ } - } -+leave: - return 1; - } - --static h2_stream *get_timed_out_busy_stream(h2_mplx *m) -+static apr_status_t m_assess_task_to_throttle(h2_task **ptask, h2_mplx *m) - { - stream_iter_ctx ctx; -+ -+ /* count the running tasks already marked for redo and get one that could -+ * be throttled */ -+ *ptask = NULL; - ctx.m = m; - ctx.stream = NULL; -- ctx.now = apr_time_now(); -- h2_ihash_iter(m->streams, timed_out_busy_iter, &ctx); -- return ctx.stream; -+ ctx.count = 0; -+ h2_ihash_iter(m->streams, m_latest_repeatable_unsubmitted_iter, &ctx); -+ if (m->tasks_active - ctx.count > m->limit_active) { -+ /* we are above the limit of running tasks, accounting for the ones -+ * already throttled. */ -+ if (ctx.stream && ctx.stream->task) { -+ *ptask = ctx.stream->task; -+ return APR_EAGAIN; -+ } -+ /* above limit, be seeing no candidate for easy throttling */ -+ if (m_get_timed_out_busy_stream(m)) { -+ /* Too many busy workers, unable to cancel enough streams -+ * and with a busy, timed out stream, we tell the client -+ * to go away... */ -+ return APR_TIMEUP; -+ } -+ } -+ return APR_SUCCESS; - } - --static apr_status_t unschedule_slow_tasks(h2_mplx *m) -+static apr_status_t m_unschedule_slow_tasks(h2_mplx *m) - { -- h2_stream *stream; -- int n; -+ h2_task *task; -+ apr_status_t rv; - - /* Try to get rid of streams that occupy workers. Look for safe requests - * that are repeatable. If none found, fail the connection. - */ -- n = (m->tasks_active - m->limit_active - (int)h2_ihash_count(m->sredo)); -- while (n > 0 && (stream = get_latest_repeatable_unsubmitted_stream(m))) { -+ while (APR_EAGAIN == (rv = m_assess_task_to_throttle(&task, m))) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, - "h2_mplx(%s): unschedule, resetting task for redo later", -- stream->task->id); -- h2_task_rst(stream->task, H2_ERR_CANCEL); -- h2_ihash_add(m->sredo, stream); -- --n; -+ task->id); -+ task->redo = 1; -+ h2_task_rst(task, H2_ERR_CANCEL); - } - -- if ((m->tasks_active - h2_ihash_count(m->sredo)) > m->limit_active) { -- h2_stream *stream = get_timed_out_busy_stream(m); -- if (stream) { -- /* Too many busy workers, unable to cancel enough streams -- * and with a busy, timed out stream, we tell the client -- * to go away... */ -- return APR_TIMEUP; -- } -+ return rv; -+} -+ -+static apr_status_t s_mplx_be_happy(h2_mplx *m, h2_task *task) -+{ -+ apr_time_t now; -+ -+ --m->irritations_since; -+ now = apr_time_now(); -+ if (m->limit_active < m->max_active -+ && (now - m->last_mood_change >= m->mood_update_interval -+ || m->irritations_since < -m->limit_active)) { -+ m->limit_active = H2MIN(m->limit_active * 2, m->max_active); -+ m->last_mood_change = now; -+ m->irritations_since = 0; -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, -+ "h2_mplx(%ld): mood update, increasing worker limit to %d", -+ m->id, m->limit_active); - } - return APR_SUCCESS; - } - --apr_status_t h2_mplx_idle(h2_mplx *m) -+static apr_status_t m_be_annoyed(h2_mplx *m) - { - apr_status_t status = APR_SUCCESS; - apr_time_t now; -+ -+ ++m->irritations_since; -+ now = apr_time_now(); -+ if (m->limit_active > 2 && -+ ((now - m->last_mood_change >= m->mood_update_interval) -+ || (m->irritations_since >= m->limit_active))) { -+ -+ if (m->limit_active > 16) { -+ m->limit_active = 16; -+ } -+ else if (m->limit_active > 8) { -+ m->limit_active = 8; -+ } -+ else if (m->limit_active > 4) { -+ m->limit_active = 4; -+ } -+ else if (m->limit_active > 2) { -+ m->limit_active = 2; -+ } -+ m->last_mood_change = now; -+ m->irritations_since = 0; -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -+ "h2_mplx(%ld): mood update, decreasing worker limit to %d", -+ m->id, m->limit_active); -+ } -+ -+ if (m->tasks_active > m->limit_active) { -+ status = m_unschedule_slow_tasks(m); -+ } -+ return status; -+} -+ -+apr_status_t h2_mplx_m_idle(h2_mplx *m) -+{ -+ apr_status_t status = APR_SUCCESS; - apr_size_t scount; - - H2_MPLX_ENTER(m); -@@ -1023,31 +1048,7 @@ - * of busy workers we allow for this connection until it - * well behaves. - */ -- now = apr_time_now(); -- m->last_idle_block = now; -- if (m->limit_active > 2 -- && now - m->last_limit_change >= m->limit_change_interval) { -- if (m->limit_active > 16) { -- m->limit_active = 16; -- } -- else if (m->limit_active > 8) { -- m->limit_active = 8; -- } -- else if (m->limit_active > 4) { -- m->limit_active = 4; -- } -- else if (m->limit_active > 2) { -- m->limit_active = 2; -- } -- m->last_limit_change = now; -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -- "h2_mplx(%ld): decrease worker limit to %d", -- m->id, m->limit_active); -- } -- -- if (m->tasks_active > m->limit_active) { -- status = unschedule_slow_tasks(m); -- } -+ status = m_be_annoyed(m); - } - else if (!h2_iq_empty(m->q)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -@@ -1077,167 +1078,30 @@ - h2_beam_is_closed(stream->output), - (long)h2_beam_get_buffered(stream->output)); - h2_ihash_add(m->streams, stream); -- check_data_for(m, stream, 0); -+ mst_check_data_for(m, stream, 1); - stream->out_checked = 1; - status = APR_EAGAIN; - } - } - } - } -- register_if_needed(m); -+ ms_register_if_needed(m, 1); - - H2_MPLX_LEAVE(m); - return status; - } - - /******************************************************************************* -- * HTTP/2 request engines -- ******************************************************************************/ -- --typedef struct { -- h2_mplx * m; -- h2_req_engine *ngn; -- int streams_updated; --} ngn_update_ctx; -- --static int ngn_update_window(void *ctx, void *val) --{ -- ngn_update_ctx *uctx = ctx; -- h2_stream *stream = val; -- if (stream->task && stream->task->assigned == uctx->ngn -- && output_consumed_signal(uctx->m, stream->task)) { -- ++uctx->streams_updated; -- } -- return 1; --} -- --static apr_status_t ngn_out_update_windows(h2_mplx *m, h2_req_engine *ngn) --{ -- ngn_update_ctx ctx; -- -- ctx.m = m; -- ctx.ngn = ngn; -- ctx.streams_updated = 0; -- h2_ihash_iter(m->streams, ngn_update_window, &ctx); -- -- return ctx.streams_updated? APR_SUCCESS : APR_EAGAIN; --} -- --apr_status_t h2_mplx_req_engine_push(const char *ngn_type, -- request_rec *r, -- http2_req_engine_init *einit) --{ -- apr_status_t status; -- h2_mplx *m; -- h2_task *task; -- h2_stream *stream; -- -- task = h2_ctx_rget_task(r); -- if (!task) { -- return APR_ECONNABORTED; -- } -- m = task->mplx; -- -- H2_MPLX_ENTER(m); -- -- stream = h2_ihash_get(m->streams, task->stream_id); -- if (stream) { -- status = h2_ngn_shed_push_request(m->ngn_shed, ngn_type, r, einit); -- } -- else { -- status = APR_ECONNABORTED; -- } -- -- H2_MPLX_LEAVE(m); -- return status; --} -- --apr_status_t h2_mplx_req_engine_pull(h2_req_engine *ngn, -- apr_read_type_e block, -- int capacity, -- request_rec **pr) --{ -- h2_ngn_shed *shed = h2_ngn_shed_get_shed(ngn); -- h2_mplx *m = h2_ngn_shed_get_ctx(shed); -- apr_status_t status; -- int want_shutdown; -- -- H2_MPLX_ENTER(m); -- -- want_shutdown = (block == APR_BLOCK_READ); -- -- /* Take this opportunity to update output consummation -- * for this engine */ -- ngn_out_update_windows(m, ngn); -- -- if (want_shutdown && !h2_iq_empty(m->q)) { -- /* For a blocking read, check first if requests are to be -- * had and, if not, wait a short while before doing the -- * blocking, and if unsuccessful, terminating read. -- */ -- status = h2_ngn_shed_pull_request(shed, ngn, capacity, 1, pr); -- if (APR_STATUS_IS_EAGAIN(status)) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, -- "h2_mplx(%ld): start block engine pull", m->id); -- apr_thread_cond_timedwait(m->task_thawed, m->lock, -- apr_time_from_msec(20)); -- status = h2_ngn_shed_pull_request(shed, ngn, capacity, 1, pr); -- } -- } -- else { -- status = h2_ngn_shed_pull_request(shed, ngn, capacity, -- want_shutdown, pr); -- } -- -- H2_MPLX_LEAVE(m); -- return status; --} -- --void h2_mplx_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn, -- apr_status_t status) --{ -- h2_task *task = h2_ctx_cget_task(r_conn); -- -- if (task) { -- h2_mplx *m = task->mplx; -- h2_stream *stream; -- -- H2_MPLX_ENTER_ALWAYS(m); -- -- stream = h2_ihash_get(m->streams, task->stream_id); -- -- ngn_out_update_windows(m, ngn); -- h2_ngn_shed_done_task(m->ngn_shed, ngn, task); -- -- if (status != APR_SUCCESS && stream -- && h2_task_can_redo(task) -- && !h2_ihash_get(m->sredo, stream->id)) { -- h2_ihash_add(m->sredo, stream); -- } -- -- if (task->engine) { -- /* cannot report that as done until engine returns */ -- } -- else { -- task_done(m, task, ngn); -- } -- -- H2_MPLX_LEAVE(m); -- } --} -- --/******************************************************************************* - * mplx master events dispatching - ******************************************************************************/ - --int h2_mplx_has_master_events(h2_mplx *m) -+int h2_mplx_m_has_master_events(h2_mplx *m) - { - return apr_atomic_read32(&m->event_pending) > 0; - } - --apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, -- stream_ev_callback *on_resume, -- void *on_ctx) -+apr_status_t h2_mplx_m_dispatch_master_events(h2_mplx *m, stream_ev_callback *on_resume, -+ void *on_ctx) - { - h2_stream *stream; - int n, id; -@@ -1247,8 +1111,8 @@ - apr_atomic_set32(&m->event_pending, 0); - - /* update input windows for streams */ -- h2_ihash_iter(m->streams, report_consumption_iter, m); -- purge_streams(m, 1); -+ h2_ihash_iter(m->streams, m_report_consumption_iter, m); -+ m_purge_streams(m, 1); - - n = h2_ififo_count(m->readyq); - while (n > 0 -@@ -1263,13 +1127,13 @@ - return APR_SUCCESS; - } - --apr_status_t h2_mplx_keep_active(h2_mplx *m, h2_stream *stream) -+apr_status_t h2_mplx_m_keep_active(h2_mplx *m, h2_stream *stream) - { -- check_data_for(m, stream, 1); -+ mst_check_data_for(m, stream, 0); - return APR_SUCCESS; - } - --int h2_mplx_awaits_data(h2_mplx *m) -+int h2_mplx_m_awaits_data(h2_mplx *m) - { - int waiting = 1; - -@@ -1278,11 +1142,24 @@ - if (h2_ihash_empty(m->streams)) { - waiting = 0; - } -- else if (!m->tasks_active && !h2_ififo_count(m->readyq) -- && h2_iq_empty(m->q)) { -+ else if (!m->tasks_active && !h2_ififo_count(m->readyq) && h2_iq_empty(m->q)) { - waiting = 0; - } - - H2_MPLX_LEAVE(m); - return waiting; - } -+ -+apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id) -+{ -+ h2_stream *stream; -+ apr_status_t status = APR_SUCCESS; -+ -+ H2_MPLX_ENTER_ALWAYS(m); -+ stream = h2_ihash_get(m->streams, stream_id); -+ if (stream && stream->task) { -+ status = m_be_annoyed(m); -+ } -+ H2_MPLX_LEAVE(m); -+ return status; -+} ---- a/modules/http2/h2_mplx.h -+++ b/modules/http2/h2_mplx.h -@@ -31,8 +31,10 @@ - * queued in the multiplexer. If a task thread tries to write more - * data, it is blocked until space becomes available. - * -- * Writing input is never blocked. In order to use flow control on the input, -- * the mplx can be polled for input data consumption. -+ * Naming Convention: -+ * "h2_mplx_m_" are methods only to be called by the main connection -+ * "h2_mplx_s_" are method only to be called by a secondary connection -+ * "h2_mplx_t_" are method only to be called by a task handler (can be master or secondary) - */ - - struct apr_pool_t; -@@ -47,8 +49,6 @@ - struct apr_thread_cond_t; - struct h2_workers; - struct h2_iqueue; --struct h2_ngn_shed; --struct h2_req_engine; - - #include <apr_queue.h> - -@@ -65,7 +65,6 @@ - unsigned int is_registered; /* is registered at h2_workers */ - - struct h2_ihash_t *streams; /* all streams currently processing */ -- struct h2_ihash_t *sredo; /* all streams that need to be re-started */ - struct h2_ihash_t *shold; /* all streams done with task ongoing */ - struct h2_ihash_t *spurge; /* all streams done, ready for destroy */ - -@@ -79,41 +78,35 @@ - int tasks_active; /* # of tasks being processed from this mplx */ - int limit_active; /* current limit on active tasks, dynamic */ - int max_active; /* max, hard limit # of active tasks in a process */ -- apr_time_t last_idle_block; /* last time, this mplx entered IDLE while -- * streams were ready */ -- apr_time_t last_limit_change; /* last time, worker limit changed */ -- apr_interval_time_t limit_change_interval; -+ -+ apr_time_t last_mood_change; /* last time, we worker limit changed */ -+ apr_interval_time_t mood_update_interval; /* how frequent we update at most */ -+ int irritations_since; /* irritations (>0) or happy events (<0) since last mood change */ - - apr_thread_mutex_t *lock; - struct apr_thread_cond_t *added_output; -- struct apr_thread_cond_t *task_thawed; - struct apr_thread_cond_t *join_wait; - - apr_size_t stream_max_mem; - - apr_pool_t *spare_io_pool; -- apr_array_header_t *spare_slaves; /* spare slave connections */ -+ apr_array_header_t *spare_secondary; /* spare secondary connections */ - - struct h2_workers *workers; -- -- struct h2_ngn_shed *ngn_shed; - }; - -- -- - /******************************************************************************* -- * Object lifecycle and information. -+ * From the main connection processing: h2_mplx_m_* - ******************************************************************************/ - --apr_status_t h2_mplx_child_init(apr_pool_t *pool, server_rec *s); -+apr_status_t h2_mplx_m_child_init(apr_pool_t *pool, server_rec *s); - - /** - * Create the multiplexer for the given HTTP2 session. - * Implicitly has reference count 1. - */ --h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master, -- const struct h2_config *conf, -- struct h2_workers *workers); -+h2_mplx *h2_mplx_m_create(conn_rec *c, server_rec *s, apr_pool_t *master, -+ struct h2_workers *workers); - - /** - * Decreases the reference counter of this mplx and waits for it -@@ -123,26 +116,14 @@ - * @param m the mplx to be released and destroyed - * @param wait condition var to wait on for ref counter == 0 - */ --void h2_mplx_release_and_join(h2_mplx *m, struct apr_thread_cond_t *wait); -- --apr_status_t h2_mplx_pop_task(h2_mplx *m, struct h2_task **ptask); -- --void h2_mplx_task_done(h2_mplx *m, struct h2_task *task, struct h2_task **ptask); -+void h2_mplx_m_release_and_join(h2_mplx *m, struct apr_thread_cond_t *wait); - - /** - * Shut down the multiplexer gracefully. Will no longer schedule new streams - * but let the ongoing ones finish normally. - * @return the highest stream id being/been processed - */ --int h2_mplx_shutdown(h2_mplx *m); -- --int h2_mplx_is_busy(h2_mplx *m); -- --/******************************************************************************* -- * IO lifetime of streams. -- ******************************************************************************/ -- --struct h2_stream *h2_mplx_stream_get(h2_mplx *m, int id); -+int h2_mplx_m_shutdown(h2_mplx *m); - - /** - * Notifies mplx that a stream has been completely handled on the main -@@ -151,20 +132,16 @@ - * @param m the mplx itself - * @param stream the stream ready for cleanup - */ --apr_status_t h2_mplx_stream_cleanup(h2_mplx *m, struct h2_stream *stream); -+apr_status_t h2_mplx_m_stream_cleanup(h2_mplx *m, struct h2_stream *stream); - - /** - * Waits on output data from any stream in this session to become available. - * Returns APR_TIMEUP if no data arrived in the given time. - */ --apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, -- struct apr_thread_cond_t *iowait); -+apr_status_t h2_mplx_m_out_trywait(h2_mplx *m, apr_interval_time_t timeout, -+ struct apr_thread_cond_t *iowait); - --apr_status_t h2_mplx_keep_active(h2_mplx *m, struct h2_stream *stream); -- --/******************************************************************************* -- * Stream processing. -- ******************************************************************************/ -+apr_status_t h2_mplx_m_keep_active(h2_mplx *m, struct h2_stream *stream); - - /** - * Process a stream request. -@@ -175,8 +152,8 @@ - * @param cmp the stream priority compare function - * @param ctx context data for the compare function - */ --apr_status_t h2_mplx_process(h2_mplx *m, struct h2_stream *stream, -- h2_stream_pri_cmp *cmp, void *ctx); -+apr_status_t h2_mplx_m_process(h2_mplx *m, struct h2_stream *stream, -+ h2_stream_pri_cmp *cmp, void *ctx); - - /** - * Stream priorities have changed, reschedule pending requests. -@@ -185,7 +162,7 @@ - * @param cmp the stream priority compare function - * @param ctx context data for the compare function - */ --apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); -+apr_status_t h2_mplx_m_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); - - typedef apr_status_t stream_ev_callback(void *ctx, struct h2_stream *stream); - -@@ -193,7 +170,7 @@ - * Check if the multiplexer has events for the master connection pending. - * @return != 0 iff there are events pending - */ --int h2_mplx_has_master_events(h2_mplx *m); -+int h2_mplx_m_has_master_events(h2_mplx *m); - - /** - * Dispatch events for the master connection, such as -@@ -201,130 +178,46 @@ - * @param on_resume new output data has arrived for a suspended stream - * @param ctx user supplied argument to invocation. - */ --apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, -- stream_ev_callback *on_resume, -- void *ctx); -+apr_status_t h2_mplx_m_dispatch_master_events(h2_mplx *m, stream_ev_callback *on_resume, -+ void *ctx); - --int h2_mplx_awaits_data(h2_mplx *m); -+int h2_mplx_m_awaits_data(h2_mplx *m); - - typedef int h2_mplx_stream_cb(struct h2_stream *s, void *ctx); - --apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); -+apr_status_t h2_mplx_m_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); - --/******************************************************************************* -- * Output handling of streams. -- ******************************************************************************/ -+apr_status_t h2_mplx_m_client_rst(h2_mplx *m, int stream_id); - - /** -- * Opens the output for the given stream with the specified response. -+ * Master connection has entered idle mode. -+ * @param m the mplx instance of the master connection -+ * @return != SUCCESS iff connection should be terminated - */ --apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id, -- struct h2_bucket_beam *beam); -+apr_status_t h2_mplx_m_idle(h2_mplx *m); - - /******************************************************************************* -- * h2_mplx list Manipulation. -+ * From a secondary connection processing: h2_mplx_s_* - ******************************************************************************/ -- --/** -- * The magic pointer value that indicates the head of a h2_mplx list -- * @param b The mplx list -- * @return The magic pointer value -- */ --#define H2_MPLX_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_mplx, link) -- --/** -- * Determine if the mplx list is empty -- * @param b The list to check -- * @return true or false -- */ --#define H2_MPLX_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_mplx, link) -- --/** -- * Return the first mplx in a list -- * @param b The list to query -- * @return The first mplx in the list -- */ --#define H2_MPLX_LIST_FIRST(b) APR_RING_FIRST(b) -- --/** -- * Return the last mplx in a list -- * @param b The list to query -- * @return The last mplx int he list -- */ --#define H2_MPLX_LIST_LAST(b) APR_RING_LAST(b) -- --/** -- * Insert a single mplx at the front of a list -- * @param b The list to add to -- * @param e The mplx to insert -- */ --#define H2_MPLX_LIST_INSERT_HEAD(b, e) do { \ --h2_mplx *ap__b = (e); \ --APR_RING_INSERT_HEAD((b), ap__b, h2_mplx, link); \ --} while (0) -- --/** -- * Insert a single mplx at the end of a list -- * @param b The list to add to -- * @param e The mplx to insert -- */ --#define H2_MPLX_LIST_INSERT_TAIL(b, e) do { \ --h2_mplx *ap__b = (e); \ --APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link); \ --} while (0) -- --/** -- * Get the next mplx in the list -- * @param e The current mplx -- * @return The next mplx -- */ --#define H2_MPLX_NEXT(e) APR_RING_NEXT((e), link) --/** -- * Get the previous mplx in the list -- * @param e The current mplx -- * @return The previous mplx -- */ --#define H2_MPLX_PREV(e) APR_RING_PREV((e), link) -- --/** -- * Remove a mplx from its list -- * @param e The mplx to remove -- */ --#define H2_MPLX_REMOVE(e) APR_RING_REMOVE((e), link) -+apr_status_t h2_mplx_s_pop_task(h2_mplx *m, struct h2_task **ptask); -+void h2_mplx_s_task_done(h2_mplx *m, struct h2_task *task, struct h2_task **ptask); - - /******************************************************************************* -- * h2_mplx DoS protection -+ * From a h2_task owner: h2_mplx_s_* -+ * (a task is transfered from master to secondary connection and back in -+ * its normal lifetime). - ******************************************************************************/ - - /** -- * Master connection has entered idle mode. -- * @param m the mplx instance of the master connection -- * @return != SUCCESS iff connection should be terminated -+ * Opens the output for the given stream with the specified response. - */ --apr_status_t h2_mplx_idle(h2_mplx *m); -+apr_status_t h2_mplx_t_out_open(h2_mplx *mplx, int stream_id, -+ struct h2_bucket_beam *beam); - --/******************************************************************************* -- * h2_req_engine handling -- ******************************************************************************/ -+/** -+ * Get the stream that belongs to the given task. -+ */ -+struct h2_stream *h2_mplx_t_stream_get(h2_mplx *m, struct h2_task *task); - --typedef void h2_output_consumed(void *ctx, conn_rec *c, apr_off_t consumed); --typedef apr_status_t h2_mplx_req_engine_init(struct h2_req_engine *engine, -- const char *id, -- const char *type, -- apr_pool_t *pool, -- apr_size_t req_buffer_size, -- request_rec *r, -- h2_output_consumed **pconsumed, -- void **pbaton); -- --apr_status_t h2_mplx_req_engine_push(const char *ngn_type, -- request_rec *r, -- h2_mplx_req_engine_init *einit); --apr_status_t h2_mplx_req_engine_pull(struct h2_req_engine *ngn, -- apr_read_type_e block, -- int capacity, -- request_rec **pr); --void h2_mplx_req_engine_done(struct h2_req_engine *ngn, conn_rec *r_conn, -- apr_status_t status); - - #endif /* defined(__mod_h2__h2_mplx__) */ ---- a/modules/http2/h2_proxy_session.c -+++ b/modules/http2/h2_proxy_session.c -@@ -45,6 +45,7 @@ - unsigned int suspended : 1; - unsigned int waiting_on_100 : 1; - unsigned int waiting_on_ping : 1; -+ unsigned int headers_ended : 1; - uint32_t error_code; - - apr_bucket_brigade *input; -@@ -61,7 +62,123 @@ - static void ping_arrived(h2_proxy_session *session); - static apr_status_t check_suspended(h2_proxy_session *session); - static void stream_resume(h2_proxy_stream *stream); -+static apr_status_t submit_trailers(h2_proxy_stream *stream); - -+/* -+ * The H2_PING connection sub-state: a state independant of the H2_SESSION state -+ * of the connection: -+ * - H2_PING_ST_NONE: no interference with request handling, ProxyTimeout in effect. -+ * When entered, all suspended streams are unsuspended again. -+ * - H2_PING_ST_AWAIT_ANY: new requests are suspended, a possibly configured "ping" -+ * timeout is in effect. Any frame received transits to H2_PING_ST_NONE. -+ * - H2_PING_ST_AWAIT_PING: same as above, but only a PING frame transits -+ * to H2_PING_ST_NONE. -+ * -+ * An AWAIT state is entered on a new connection or when re-using a connection and -+ * the last frame received has been some time ago. The latter sends a PING frame -+ * and insists on an answer, the former is satisfied by any frame received from the -+ * backend. -+ * -+ * This works for new connections as there is always at least one SETTINGS frame -+ * that the backend sends. When re-using connection, we send a PING and insist on -+ * receiving one back, as there might be frames in our connection buffers from -+ * some time ago. Since some servers have protections against PING flooding, we -+ * only ever have one PING unanswered. -+ * -+ * Requests are suspended while in a PING state, as we do not want to send data -+ * before we can be reasonably sure that the connection is working (at least on -+ * the h2 protocol level). This also means that the session can do blocking reads -+ * when expecting PING answers. -+ */ -+static void set_ping_timeout(h2_proxy_session *session) -+{ -+ if (session->ping_timeout != -1 && session->save_timeout == -1) { -+ apr_socket_t *socket = NULL; -+ -+ socket = ap_get_conn_socket(session->c); -+ if (socket) { -+ apr_socket_timeout_get(socket, &session->save_timeout); -+ apr_socket_timeout_set(socket, session->ping_timeout); -+ } -+ } -+} -+ -+static void unset_ping_timeout(h2_proxy_session *session) -+{ -+ if (session->save_timeout != -1) { -+ apr_socket_t *socket = NULL; -+ -+ socket = ap_get_conn_socket(session->c); -+ if (socket) { -+ apr_socket_timeout_set(socket, session->save_timeout); -+ session->save_timeout = -1; -+ } -+ } -+} -+ -+static void enter_ping_state(h2_proxy_session *session, h2_ping_state_t state) -+{ -+ if (session->ping_state == state) return; -+ switch (session->ping_state) { -+ case H2_PING_ST_NONE: -+ /* leaving NONE, enforce timeout, send frame maybe */ -+ if (H2_PING_ST_AWAIT_PING == state) { -+ unset_ping_timeout(session); -+ nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup"); -+ } -+ set_ping_timeout(session); -+ session->ping_state = state; -+ break; -+ default: -+ /* no switching between the != NONE states */ -+ if (H2_PING_ST_NONE == state) { -+ session->ping_state = state; -+ unset_ping_timeout(session); -+ ping_arrived(session); -+ } -+ break; -+ } -+} -+ -+static void ping_new_session(h2_proxy_session *session, proxy_conn_rec *p_conn) -+{ -+ session->save_timeout = -1; -+ session->ping_timeout = (p_conn->worker->s->ping_timeout_set? -+ p_conn->worker->s->ping_timeout : -1); -+ session->ping_state = H2_PING_ST_NONE; -+ enter_ping_state(session, H2_PING_ST_AWAIT_ANY); -+} -+ -+static void ping_reuse_session(h2_proxy_session *session) -+{ -+ if (H2_PING_ST_NONE == session->ping_state) { -+ apr_interval_time_t age = apr_time_now() - session->last_frame_received; -+ if (age > apr_time_from_sec(1)) { -+ enter_ping_state(session, H2_PING_ST_AWAIT_PING); -+ } -+ } -+} -+ -+static void ping_ev_frame_received(h2_proxy_session *session, const nghttp2_frame *frame) -+{ -+ session->last_frame_received = apr_time_now(); -+ switch (session->ping_state) { -+ case H2_PING_ST_NONE: -+ /* nop */ -+ break; -+ case H2_PING_ST_AWAIT_ANY: -+ enter_ping_state(session, H2_PING_ST_NONE); -+ break; -+ case H2_PING_ST_AWAIT_PING: -+ if (NGHTTP2_PING == frame->hd.type) { -+ enter_ping_state(session, H2_PING_ST_NONE); -+ } -+ /* we may receive many other frames while we are waiting for the -+ * PING answer. They may come all from our connection buffers and -+ * say nothing about the current state of the backend. */ -+ break; -+ } -+} - - static apr_status_t proxy_session_pre_close(void *theconn) - { -@@ -152,7 +269,8 @@ - session->id, buffer); - } - -- session->last_frame_received = apr_time_now(); -+ ping_ev_frame_received(session, frame); -+ /* Action for frame types: */ - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - stream = nghttp2_session_get_stream_user_data(ngh2, frame->hd.stream_id); -@@ -193,10 +311,6 @@ - stream_resume(stream); - break; - case NGHTTP2_PING: -- if (session->check_ping) { -- session->check_ping = 0; -- ping_arrived(session); -- } - break; - case NGHTTP2_PUSH_PROMISE: - break; -@@ -241,7 +355,8 @@ - return 1; - } - --static void process_proxy_header(h2_proxy_stream *stream, const char *n, const char *v) -+static void process_proxy_header(apr_table_t *headers, h2_proxy_stream *stream, -+ const char *n, const char *v) - { - static const struct { - const char *name; -@@ -262,20 +377,18 @@ - if (!dconf->preserve_host) { - for (i = 0; transform_hdrs[i].name; ++i) { - if (!ap_cstr_casecmp(transform_hdrs[i].name, n)) { -- apr_table_add(r->headers_out, n, -- (*transform_hdrs[i].func)(r, dconf, v)); -+ apr_table_add(headers, n, (*transform_hdrs[i].func)(r, dconf, v)); - return; - } - } - if (!ap_cstr_casecmp("Link", n)) { - dconf = ap_get_module_config(r->per_dir_config, &proxy_module); -- apr_table_add(r->headers_out, n, -- h2_proxy_link_reverse_map(r, dconf, -- stream->real_server_uri, stream->p_server_uri, v)); -+ apr_table_add(headers, n, h2_proxy_link_reverse_map(r, dconf, -+ stream->real_server_uri, stream->p_server_uri, v)); - return; - } - } -- apr_table_add(r->headers_out, n, v); -+ apr_table_add(headers, n, v); - } - - static apr_status_t h2_proxy_stream_add_header_out(h2_proxy_stream *stream, -@@ -299,8 +412,13 @@ - return APR_SUCCESS; - } - -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, -+ "h2_proxy_stream(%s-%d): on_header %s: %s", -+ stream->session->id, stream->id, n, v); - if (!h2_proxy_res_ignore_header(n, nlen)) { - char *hname, *hvalue; -+ apr_table_t *headers = (stream->headers_ended? -+ stream->r->trailers_out : stream->r->headers_out); - - hname = apr_pstrndup(stream->pool, n, nlen); - h2_proxy_util_camel_case_header(hname, nlen); -@@ -309,7 +427,7 @@ - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, - "h2_proxy_stream(%s-%d): got header %s: %s", - stream->session->id, stream->id, hname, hvalue); -- process_proxy_header(stream, hname, hvalue); -+ process_proxy_header(headers, stream, hname, hvalue); - } - return APR_SUCCESS; - } -@@ -374,6 +492,7 @@ - server_name, portstr) - ); - } -+ if (r->status >= 200) stream->headers_ended = 1; - - if (APLOGrtrace2(stream->r)) { - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, stream->r, -@@ -429,12 +548,6 @@ - stream_id, NGHTTP2_STREAM_CLOSED); - return NGHTTP2_ERR_STREAM_CLOSING; - } -- if (stream->standalone) { -- nghttp2_session_consume(ngh2, stream_id, len); -- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, stream->r, -- "h2_proxy_session(%s): stream %d, win_update %d bytes", -- session->id, stream_id, (int)len); -- } - return 0; - } - -@@ -493,12 +606,12 @@ - stream = nghttp2_session_get_stream_user_data(ngh2, stream_id); - if (!stream) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(03361) -- "h2_proxy_stream(%s): data_read, stream %d not found", -- stream->session->id, stream_id); -+ "h2_proxy_stream(NULL): data_read, stream %d not found", -+ stream_id); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - -- if (stream->session->check_ping) { -+ if (stream->session->ping_state != H2_PING_ST_NONE) { - /* suspend until we hear from the other side */ - stream->waiting_on_ping = 1; - status = APR_EAGAIN; -@@ -553,9 +666,14 @@ - stream->data_sent += readlen; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, stream->r, APLOGNO(03468) - "h2_proxy_stream(%d): request DATA %ld, %ld" -- " total, flags=%d", -- stream->id, (long)readlen, (long)stream->data_sent, -+ " total, flags=%d", stream->id, (long)readlen, (long)stream->data_sent, - (int)*data_flags); -+ if ((*data_flags & NGHTTP2_DATA_FLAG_EOF) && !apr_is_empty_table(stream->r->trailers_in)) { -+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, stream->r, APLOGNO(10179) -+ "h2_proxy_stream(%d): submit trailers", stream->id); -+ *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; -+ submit_trailers(stream); -+ } - return readlen; - } - else if (APR_STATUS_IS_EAGAIN(status)) { -@@ -641,23 +759,20 @@ - - nghttp2_option_new(&option); - nghttp2_option_set_peer_max_concurrent_streams(option, 100); -- nghttp2_option_set_no_auto_window_update(option, 1); -+ nghttp2_option_set_no_auto_window_update(option, 0); - - nghttp2_session_client_new2(&session->ngh2, cbs, session, option); - - nghttp2_option_del(option); - nghttp2_session_callbacks_del(cbs); - -+ ping_new_session(session, p_conn); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03362) - "setup session for %s", p_conn->hostname); - } - else { - h2_proxy_session *session = p_conn->data; -- apr_interval_time_t age = apr_time_now() - session->last_frame_received; -- if (age > apr_time_from_sec(1)) { -- session->check_ping = 1; -- nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup"); -- } -+ ping_reuse_session(session); - } - return p_conn->data; - } -@@ -740,6 +855,8 @@ - stream->real_server_uri = apr_psprintf(stream->pool, "%s://%s", scheme, authority); - stream->p_server_uri = apr_psprintf(stream->pool, "%s://%s", puri.scheme, authority); - path = apr_uri_unparse(stream->pool, &puri, APR_URI_UNP_OMITSITEPART); -+ -+ - h2_proxy_req_make(stream->req, stream->pool, r->method, scheme, - authority, path, r->headers_in); - -@@ -826,6 +943,16 @@ - return APR_EGENERAL; - } - -+static apr_status_t submit_trailers(h2_proxy_stream *stream) -+{ -+ h2_proxy_ngheader *hd; -+ int rv; -+ -+ hd = h2_proxy_util_nghd_make(stream->pool, stream->r->trailers_in); -+ rv = nghttp2_submit_trailer(stream->session->ngh2, stream->id, hd->nv, hd->nvlen); -+ return rv == 0? APR_SUCCESS: APR_EGENERAL; -+} -+ - static apr_status_t feed_brigade(h2_proxy_session *session, apr_bucket_brigade *bb) - { - apr_status_t status = APR_SUCCESS; -@@ -882,7 +1009,7 @@ - apr_socket_t *socket = NULL; - apr_time_t save_timeout = -1; - -- if (block) { -+ if (block && timeout > 0) { - socket = ap_get_conn_socket(session->c); - if (socket) { - apr_socket_timeout_get(socket, &save_timeout); -@@ -954,6 +1081,14 @@ - dispatch_event(session, H2_PROXYS_EV_STREAM_RESUMED, 0, NULL); - } - -+static int is_waiting_for_backend(h2_proxy_session *session) -+{ -+ return ((session->ping_state != H2_PING_ST_NONE) -+ || ((session->suspended->nelts <= 0) -+ && !nghttp2_session_want_write(session->ngh2) -+ && nghttp2_session_want_read(session->ngh2))); -+} -+ - static apr_status_t check_suspended(h2_proxy_session *session) - { - h2_proxy_stream *stream; -@@ -1408,7 +1543,22 @@ - break; - - case H2_PROXYS_ST_WAIT: -- if (check_suspended(session) == APR_EAGAIN) { -+ if (is_waiting_for_backend(session)) { -+ /* we can do a blocking read with the default timeout (as -+ * configured via ProxyTimeout in our socket. There is -+ * nothing we want to send or check until we get more data -+ * from the backend. */ -+ status = h2_proxy_session_read(session, 1, 0); -+ if (status == APR_SUCCESS) { -+ have_read = 1; -+ dispatch_event(session, H2_PROXYS_EV_DATA_READ, 0, NULL); -+ } -+ else { -+ dispatch_event(session, H2_PROXYS_EV_CONN_ERROR, status, NULL); -+ return status; -+ } -+ } -+ else if (check_suspended(session) == APR_EAGAIN) { - /* no stream has become resumed. Do a blocking read with - * ever increasing timeouts... */ - if (session->wait_timeout < 25) { -@@ -1423,7 +1573,7 @@ - ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c, - APLOGNO(03365) - "h2_proxy_session(%s): WAIT read, timeout=%fms", -- session->id, (float)session->wait_timeout/1000.0); -+ session->id, session->wait_timeout/1000.0); - if (status == APR_SUCCESS) { - have_read = 1; - dispatch_event(session, H2_PROXYS_EV_DATA_READ, 0, NULL); -@@ -1543,42 +1693,3 @@ - int updated; - } win_update_ctx; - --static int win_update_iter(void *udata, void *val) --{ -- win_update_ctx *ctx = udata; -- h2_proxy_stream *stream = val; -- -- if (stream->r && stream->r->connection == ctx->c) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, ctx->session->c, -- "h2_proxy_session(%s-%d): win_update %ld bytes", -- ctx->session->id, (int)stream->id, (long)ctx->bytes); -- nghttp2_session_consume(ctx->session->ngh2, stream->id, ctx->bytes); -- ctx->updated = 1; -- return 0; -- } -- return 1; --} -- -- --void h2_proxy_session_update_window(h2_proxy_session *session, -- conn_rec *c, apr_off_t bytes) --{ -- if (!h2_proxy_ihash_empty(session->streams)) { -- win_update_ctx ctx; -- ctx.session = session; -- ctx.c = c; -- ctx.bytes = bytes; -- ctx.updated = 0; -- h2_proxy_ihash_iter(session->streams, win_update_iter, &ctx); -- -- if (!ctx.updated) { -- /* could not find the stream any more, possibly closed, update -- * the connection window at least */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, -- "h2_proxy_session(%s): win_update conn %ld bytes", -- session->id, (long)bytes); -- nghttp2_session_consume_connection(session->ngh2, (size_t)bytes); -- } -- } --} -- ---- a/modules/http2/h2_proxy_session.h -+++ b/modules/http2/h2_proxy_session.h -@@ -60,6 +60,11 @@ - H2_PROXYS_EV_PRE_CLOSE, /* connection will close after this */ - } h2_proxys_event_t; - -+typedef enum { -+ H2_PING_ST_NONE, /* normal connection mode, ProxyTimeout rules */ -+ H2_PING_ST_AWAIT_ANY, /* waiting for any frame from backend */ -+ H2_PING_ST_AWAIT_PING, /* waiting for PING frame from backend */ -+} h2_ping_state_t; - - typedef struct h2_proxy_session h2_proxy_session; - typedef void h2_proxy_request_done(h2_proxy_session *s, request_rec *r, -@@ -74,7 +79,6 @@ - nghttp2_session *ngh2; /* the nghttp2 session itself */ - - unsigned int aborted : 1; -- unsigned int check_ping : 1; - unsigned int h2_front : 1; /* if front-end connection is HTTP/2 */ - - h2_proxy_request_done *done; -@@ -94,6 +98,10 @@ - - apr_bucket_brigade *input; - apr_bucket_brigade *output; -+ -+ h2_ping_state_t ping_state; -+ apr_time_t ping_timeout; -+ apr_time_t save_timeout; - }; - - h2_proxy_session *h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn, -@@ -120,9 +128,6 @@ - - void h2_proxy_session_cleanup(h2_proxy_session *s, h2_proxy_request_done *done); - --void h2_proxy_session_update_window(h2_proxy_session *s, -- conn_rec *c, apr_off_t bytes); -- - #define H2_PROXY_REQ_URL_NOTE "h2-proxy-req-url" - - #endif /* h2_proxy_session_h */ ---- a/modules/http2/h2_proxy_util.c -+++ b/modules/http2/h2_proxy_util.c -@@ -452,6 +452,22 @@ - return ngh; - } - -+h2_proxy_ngheader *h2_proxy_util_nghd_make(apr_pool_t *p, apr_table_t *headers) -+{ -+ -+ h2_proxy_ngheader *ngh; -+ size_t n; -+ -+ n = 0; -+ apr_table_do(count_header, &n, headers, NULL); -+ -+ ngh = apr_pcalloc(p, sizeof(h2_proxy_ngheader)); -+ ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv)); -+ apr_table_do(add_table_header, ngh, headers, NULL); -+ -+ return ngh; -+} -+ - /******************************************************************************* - * header HTTP/1 <-> HTTP/2 conversions - ******************************************************************************/ -@@ -609,6 +625,7 @@ - apr_table_t *headers) - { - h1_ctx x; -+ const char *val; - - req->method = method; - req->scheme = scheme; -@@ -623,6 +640,11 @@ - x.pool = pool; - x.headers = req->headers; - apr_table_do(set_h1_header, &x, headers, NULL); -+ if ((val = apr_table_get(headers, "TE")) && ap_find_token(pool, val, "trailers")) { -+ /* client accepts trailers, forward this information */ -+ apr_table_addn(req->headers, "TE", "trailers"); -+ } -+ apr_table_setn(req->headers, "te", "trailers"); - return APR_SUCCESS; - } - -@@ -915,12 +937,12 @@ - nlen = (int)strlen(ns); - delta = nlen - olen; - plen = ctx->slen + delta + 1; -- p = apr_pcalloc(ctx->pool, plen); -+ p = apr_palloc(ctx->pool, plen); - memcpy(p, ctx->s, start); - memcpy(p + start, ns, nlen); - strcpy(p + start + nlen, ctx->s + end); - ctx->s = p; -- ctx->slen = (int)strlen(p); -+ ctx->slen = plen - 1; /* (int)strlen(p) */ - if (ctx->i >= end) { - ctx->i += delta; - } ---- a/modules/http2/h2_proxy_util.h -+++ b/modules/http2/h2_proxy_util.h -@@ -168,6 +168,8 @@ - h2_proxy_ngheader *h2_proxy_util_nghd_make_req(apr_pool_t *p, - const struct h2_proxy_request *req); - -+h2_proxy_ngheader *h2_proxy_util_nghd_make(apr_pool_t *p, apr_table_t *headers); -+ - /******************************************************************************* - * h2_proxy_request helpers - ******************************************************************************/ -@@ -183,7 +185,7 @@ - - apr_time_t request_time; - -- unsigned int chunked : 1; /* iff requst body needs to be forwarded as chunked */ -+ unsigned int chunked : 1; /* iff request body needs to be forwarded as chunked */ - unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */ - }; - ---- a/modules/http2/h2_push.c -+++ b/modules/http2/h2_push.c -@@ -464,33 +464,6 @@ - return NULL; - } - --/******************************************************************************* -- * push diary -- * -- * - The push diary keeps track of resources already PUSHed via HTTP/2 on this -- * connection. It records a hash value from the absolute URL of the resource -- * pushed. -- * - Lacking openssl, it uses 'apr_hashfunc_default' for the value -- * - with openssl, it uses SHA256 to calculate the hash value -- * - whatever the method to generate the hash, the diary keeps a maximum of 64 -- * bits per hash, limiting the memory consumption to about -- * H2PushDiarySize * 8 -- * bytes. Entries are sorted by most recently used and oldest entries are -- * forgotten first. -- * - Clients can initialize/replace the push diary by sending a 'Cache-Digest' -- * header. Currently, this is the base64url encoded value of the cache digest -- * as specified in https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ -- * This draft can be expected to evolve and the definition of the header -- * will be added there and refined. -- * - The cache digest header is a Golomb Coded Set of hash values, but it may -- * limit the amount of bits per hash value even further. For a good description -- * of GCS, read here: -- * http://giovanni.bajo.it/post/47119962313/golomb-coded-sets-smaller-than-bloom-filters -- * - The means that the push diary might be initialized with hash values of much -- * less than 64 bits, leading to more false positives, but smaller digest size. -- ******************************************************************************/ -- -- - #define GCSLOG_LEVEL APLOG_TRACE1 - - typedef struct h2_push_diary_entry { -@@ -617,38 +590,48 @@ - return -1; - } - --static h2_push_diary_entry *move_to_last(h2_push_diary *diary, apr_size_t idx) -+static void move_to_last(h2_push_diary *diary, apr_size_t idx) - { - h2_push_diary_entry *entries = (h2_push_diary_entry*)diary->entries->elts; - h2_push_diary_entry e; -- apr_size_t lastidx = diary->entries->nelts-1; -+ int lastidx; - -+ /* Move an existing entry to the last place */ -+ if (diary->entries->nelts <= 0) -+ return; -+ - /* move entry[idx] to the end */ -+ lastidx = diary->entries->nelts - 1; - if (idx < lastidx) { - e = entries[idx]; -- memmove(entries+idx, entries+idx+1, sizeof(e) * (lastidx - idx)); -+ memmove(entries+idx, entries+idx+1, sizeof(h2_push_diary_entry) * (lastidx - idx)); - entries[lastidx] = e; - } -- return &entries[lastidx]; - } - --static void h2_push_diary_append(h2_push_diary *diary, h2_push_diary_entry *e) -+static void remove_first(h2_push_diary *diary) - { -- h2_push_diary_entry *ne; -+ h2_push_diary_entry *entries = (h2_push_diary_entry*)diary->entries->elts; -+ int lastidx; - -- if (diary->entries->nelts < diary->N) { -- /* append a new diary entry at the end */ -- APR_ARRAY_PUSH(diary->entries, h2_push_diary_entry) = *e; -- ne = &APR_ARRAY_IDX(diary->entries, diary->entries->nelts-1, h2_push_diary_entry); -- } -- else { -- /* replace content with new digest. keeps memory usage constant once diary is full */ -- ne = move_to_last(diary, 0); -- *ne = *e; -+ /* move remaining entries to index 0 */ -+ lastidx = diary->entries->nelts - 1; -+ if (lastidx > 0) { -+ --diary->entries->nelts; -+ memmove(entries, entries+1, sizeof(h2_push_diary_entry) * diary->entries->nelts); - } -+} -+ -+static void h2_push_diary_append(h2_push_diary *diary, h2_push_diary_entry *e) -+{ -+ while (diary->entries->nelts >= diary->N) { -+ remove_first(diary); -+ } -+ /* append a new diary entry at the end */ -+ APR_ARRAY_PUSH(diary->entries, h2_push_diary_entry) = *e; - /* Intentional no APLOGNO */ - ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, diary->entries->pool, -- "push_diary_append: %"APR_UINT64_T_HEX_FMT, ne->hash); -+ "push_diary_append: %"APR_UINT64_T_HEX_FMT, e->hash); - } - - apr_array_header_t *h2_push_diary_update(h2_session *session, apr_array_header_t *pushes) -@@ -691,30 +674,12 @@ - const struct h2_request *req, - const struct h2_headers *res) - { -- h2_session *session = stream->session; -- const char *cache_digest = apr_table_get(req->headers, "Cache-Digest"); - apr_array_header_t *pushes; -- apr_status_t status; - -- if (cache_digest && session->push_diary) { -- status = h2_push_diary_digest64_set(session->push_diary, req->authority, -- cache_digest, stream->pool); -- if (status != APR_SUCCESS) { -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, -- H2_SSSN_LOG(APLOGNO(03057), session, -- "push diary set from Cache-Digest: %s"), cache_digest); -- } -- } - pushes = h2_push_collect(stream->pool, req, stream->push_policy, res); - return h2_push_diary_update(stream->session, pushes); - } - --static apr_int32_t h2_log2inv(unsigned char log2) --{ -- return log2? (1 << log2) : 1; --} -- -- - typedef struct { - h2_push_diary *diary; - unsigned char log2p; -@@ -829,16 +794,11 @@ - apr_size_t hash_count; - - nelts = diary->entries->nelts; -- -- if (nelts > APR_UINT32_MAX) { -- /* should not happen */ -- return APR_ENOTIMPL; -- } - N = ceil_power_of_2(nelts); - log2n = h2_log2(N); - - /* Now log2p is the max number of relevant bits, so that -- * log2p + log2n == mask_bits. We can uise a lower log2p -+ * log2p + log2n == mask_bits. We can use a lower log2p - * and have a shorter set encoding... - */ - log2pmax = h2_log2(ceil_power_of_2(maxP)); -@@ -895,166 +855,3 @@ - return APR_SUCCESS; - } - --typedef struct { -- h2_push_diary *diary; -- apr_pool_t *pool; -- unsigned char log2p; -- const unsigned char *data; -- apr_size_t datalen; -- apr_size_t offset; -- unsigned int bit; -- apr_uint64_t last_val; --} gset_decoder; -- --static int gset_decode_next_bit(gset_decoder *decoder) --{ -- if (++decoder->bit >= 8) { -- if (++decoder->offset >= decoder->datalen) { -- return -1; -- } -- decoder->bit = 0; -- } -- return (decoder->data[decoder->offset] & cbit_mask[decoder->bit])? 1 : 0; --} -- --static apr_status_t gset_decode_next(gset_decoder *decoder, apr_uint64_t *phash) --{ -- apr_uint64_t flex = 0, fixed = 0, delta; -- int i; -- -- /* read 1 bits until we encounter 0, then read log2n(diary-P) bits. -- * On a malformed bit-string, this will not fail, but produce results -- * which are pbly too large. Luckily, the diary will modulo the hash. -- */ -- while (1) { -- int bit = gset_decode_next_bit(decoder); -- if (bit == -1) { -- return APR_EINVAL; -- } -- if (!bit) { -- break; -- } -- ++flex; -- } -- -- for (i = 0; i < decoder->log2p; ++i) { -- int bit = gset_decode_next_bit(decoder); -- if (bit == -1) { -- return APR_EINVAL; -- } -- fixed = (fixed << 1) | bit; -- } -- -- delta = (flex << decoder->log2p) | fixed; -- *phash = delta + decoder->last_val; -- decoder->last_val = *phash; -- -- /* Intentional no APLOGNO */ -- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, decoder->pool, -- "h2_push_diary_digest_dec: val=%"APR_UINT64_T_HEX_FMT", delta=%" -- APR_UINT64_T_HEX_FMT", flex=%d, fixed=%"APR_UINT64_T_HEX_FMT, -- *phash, delta, (int)flex, fixed); -- -- return APR_SUCCESS; --} -- --/** -- * Initialize the push diary by a cache digest as described in -- * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ -- * . -- * @param diary the diary to set the digest into -- * @param data the binary cache digest -- * @param len the length of the cache digest -- * @return APR_EINVAL if digest was not successfully parsed -- */ --apr_status_t h2_push_diary_digest_set(h2_push_diary *diary, const char *authority, -- const char *data, apr_size_t len) --{ -- gset_decoder decoder; -- unsigned char log2n, log2p; -- int N, i; -- apr_pool_t *pool = diary->entries->pool; -- h2_push_diary_entry e; -- apr_status_t status = APR_SUCCESS; -- -- if (len < 2) { -- /* at least this should be there */ -- return APR_EINVAL; -- } -- log2n = data[0]; -- log2p = data[1]; -- diary->mask_bits = log2n + log2p; -- if (diary->mask_bits > 64) { -- /* cannot handle */ -- return APR_ENOTIMPL; -- } -- -- /* whatever is in the digest, it replaces the diary entries */ -- apr_array_clear(diary->entries); -- if (!authority || !strcmp("*", authority)) { -- diary->authority = NULL; -- } -- else if (!diary->authority || strcmp(diary->authority, authority)) { -- diary->authority = apr_pstrdup(diary->entries->pool, authority); -- } -- -- N = h2_log2inv(log2n + log2p); -- -- decoder.diary = diary; -- decoder.pool = pool; -- decoder.log2p = log2p; -- decoder.data = (const unsigned char*)data; -- decoder.datalen = len; -- decoder.offset = 1; -- decoder.bit = 8; -- decoder.last_val = 0; -- -- diary->N = N; -- /* Determine effective N we use for storage */ -- if (!N) { -- /* a totally empty cache digest. someone tells us that she has no -- * entries in the cache at all. Use our own preferences for N+mask -- */ -- diary->N = diary->NMax; -- return APR_SUCCESS; -- } -- else if (N > diary->NMax) { -- /* Store not more than diary is configured to hold. We open us up -- * to DOS attacks otherwise. */ -- diary->N = diary->NMax; -- } -- -- /* Intentional no APLOGNO */ -- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, -- "h2_push_diary_digest_set: N=%d, log2n=%d, " -- "diary->mask_bits=%d, dec.log2p=%d", -- (int)diary->N, (int)log2n, diary->mask_bits, -- (int)decoder.log2p); -- -- for (i = 0; i < diary->N; ++i) { -- if (gset_decode_next(&decoder, &e.hash) != APR_SUCCESS) { -- /* the data may have less than N values */ -- break; -- } -- h2_push_diary_append(diary, &e); -- } -- -- /* Intentional no APLOGNO */ -- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, -- "h2_push_diary_digest_set: diary now with %d entries, mask_bits=%d", -- (int)diary->entries->nelts, diary->mask_bits); -- return status; --} -- --apr_status_t h2_push_diary_digest64_set(h2_push_diary *diary, const char *authority, -- const char *data64url, apr_pool_t *pool) --{ -- const char *data; -- apr_size_t len = h2_util_base64url_decode(&data, data64url, pool); -- /* Intentional no APLOGNO */ -- ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, -- "h2_push_diary_digest64_set: digest=%s, dlen=%d", -- data64url, (int)len); -- return h2_push_diary_digest_set(diary, authority, data, len); --} -- ---- a/modules/http2/h2_push.h -+++ b/modules/http2/h2_push.h -@@ -35,6 +35,44 @@ - H2_PUSH_DIGEST_SHA256 - } h2_push_digest_type; - -+/******************************************************************************* -+ * push diary -+ * -+ * - The push diary keeps track of resources already PUSHed via HTTP/2 on this -+ * connection. It records a hash value from the absolute URL of the resource -+ * pushed. -+ * - Lacking openssl, -+ * - with openssl, it uses SHA256 to calculate the hash value, otherwise it -+ * falls back to apr_hashfunc_default() -+ * - whatever the method to generate the hash, the diary keeps a maximum of 64 -+ * bits per hash, limiting the memory consumption to about -+ * H2PushDiarySize * 8 -+ * bytes. Entries are sorted by most recently used and oldest entries are -+ * forgotten first. -+ * - While useful by itself to avoid duplicated PUSHes on the same connection, -+ * the original idea was that clients provided a 'Cache-Digest' header with -+ * the values of *their own* cached resources. This was described in -+ * <https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/> -+ * and some subsequent revisions that tweaked values but kept the overall idea. -+ * - The draft was abandoned by the IETF http-wg, as support from major clients, -+ * e.g. browsers, was lacking for various reasons. -+ * - For these reasons, mod_h2 abandoned its support for client supplied values -+ * but keeps the diary. It seems to provide value for applications using PUSH, -+ * is configurable in size and defaults to a very moderate amount of memory -+ * used. -+ * - The cache digest header is a Golomb Coded Set of hash values, but it may -+ * limit the amount of bits per hash value even further. For a good description -+ * of GCS, read here: -+ * <http://giovanni.bajo.it/post/47119962313/golomb-coded-sets-smaller-than-bloom-filters> -+ ******************************************************************************/ -+ -+ -+/* -+ * The push diary is based on the abandoned draft -+ * <https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/> -+ * that describes how to use golomb filters. -+ */ -+ - typedef struct h2_push_diary h2_push_diary; - - typedef void h2_push_digest_calc(h2_push_diary *diary, apr_uint64_t *phash, h2_push *push); -@@ -101,20 +139,4 @@ - int maxP, const char *authority, - const char **pdata, apr_size_t *plen); - --/** -- * Initialize the push diary by a cache digest as described in -- * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ -- * . -- * @param diary the diary to set the digest into -- * @param authority the authority to set the data for -- * @param data the binary cache digest -- * @param len the length of the cache digest -- * @return APR_EINVAL if digest was not successfully parsed -- */ --apr_status_t h2_push_diary_digest_set(h2_push_diary *diary, const char *authority, -- const char *data, apr_size_t len); -- --apr_status_t h2_push_diary_digest64_set(h2_push_diary *diary, const char *authority, -- const char *data64url, apr_pool_t *pool); -- - #endif /* defined(__mod_h2__h2_push__) */ ---- a/modules/http2/h2_request.c -+++ b/modules/http2/h2_request.c -@@ -17,6 +17,7 @@ - #include <assert.h> - - #include <apr_strings.h> -+#include <ap_mmn.h> - - #include <httpd.h> - #include <http_core.h> -@@ -46,9 +47,9 @@ - static int set_h1_header(void *ctx, const char *key, const char *value) - { - h1_ctx *x = ctx; -- x->status = h2_req_add_header(x->headers, x->pool, key, strlen(key), -- value, strlen(value)); -- return (x->status == APR_SUCCESS)? 1 : 0; -+ int was_added; -+ h2_req_add_header(x->headers, x->pool, key, strlen(key), value, strlen(value), 0, &was_added); -+ return 1; - } - - apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool, -@@ -84,8 +85,7 @@ - req->path = path; - req->headers = apr_table_make(pool, 10); - if (r->server) { -- req->serialize = h2_config_geti(h2_config_sget(r->server), -- H2_CONF_SER_HEADERS); -+ req->serialize = h2_config_rgeti(r, H2_CONF_SER_HEADERS); - } - - x.pool = pool; -@@ -99,10 +99,12 @@ - - apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, - const char *name, size_t nlen, -- const char *value, size_t vlen) -+ const char *value, size_t vlen, -+ size_t max_field_len, int *pwas_added) - { - apr_status_t status = APR_SUCCESS; - -+ *pwas_added = 0; - if (nlen <= 0) { - return status; - } -@@ -143,8 +145,9 @@ - } - } - else { -- /* non-pseudo header, append to work bucket of stream */ -- status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen); -+ /* non-pseudo header, add to table */ -+ status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen, -+ max_field_len, pwas_added); - } - - return status; -@@ -156,7 +159,7 @@ - - /* rfc7540, ch. 8.1.2.3: - * - if we have :authority, it overrides any Host header -- * - :authority MUST be ommited when converting h1->h2, so we -+ * - :authority MUST be omitted when converting h1->h2, so we - * might get a stream without, but then Host needs to be there */ - if (!req->authority) { - const char *host = apr_table_get(req->headers, "Host"); -@@ -206,13 +209,11 @@ - return dst; - } - --request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) -+#if !AP_MODULE_MAGIC_AT_LEAST(20150222, 13) -+static request_rec *my_ap_create_request(conn_rec *c) - { -- int access_status = HTTP_OK; -- const char *rpath; - apr_pool_t *p; - request_rec *r; -- const char *s; - - apr_pool_create(&p, c->pool); - apr_pool_tag(p, "request"); -@@ -226,8 +227,8 @@ - r->ap_auth_type = NULL; - - r->allowed_methods = ap_make_method_list(p, 2); -- -- r->headers_in = apr_table_clone(r->pool, req->headers); -+ -+ r->headers_in = apr_table_make(r->pool, 5); - r->trailers_in = apr_table_make(r->pool, 5); - r->subprocess_env = apr_table_make(r->pool, 25); - r->headers_out = apr_table_make(r->pool, 12); -@@ -262,6 +263,24 @@ - r->useragent_addr = c->client_addr; - r->useragent_ip = c->client_ip; - -+ return r; -+} -+#endif -+ -+request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) -+{ -+ int access_status = HTTP_OK; -+ const char *rpath; -+ const char *s; -+ -+#if AP_MODULE_MAGIC_AT_LEAST(20150222, 13) -+ request_rec *r = ap_create_request(c); -+#else -+ request_rec *r = my_ap_create_request(c); -+#endif -+ -+ r->headers_in = apr_table_clone(r->pool, req->headers); -+ - ap_run_pre_read_request(r, c); - - /* Time to populate r with the data we have. */ -@@ -272,6 +291,9 @@ - if (r->method_number == M_GET && r->method[0] == 'H') { - r->header_only = 1; - } -+ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", -+ req->method, req->path ? req->path : ""); -+ r->headers_in = apr_table_clone(r->pool, req->headers); - - rpath = (req->path ? req->path : ""); - ap_parse_uri(r, rpath); -@@ -288,7 +310,9 @@ - */ - r->hostname = NULL; - ap_update_vhost_from_headers(r); -- -+ r->protocol = "HTTP/2.0"; -+ r->proto_num = HTTP_VERSION(2, 0); -+ - /* we may have switched to another server */ - r->per_dir_config = r->server->lookup_defaults; - -@@ -337,3 +361,4 @@ - } - - -+ ---- a/modules/http2/h2_request.h -+++ b/modules/http2/h2_request.h -@@ -24,7 +24,8 @@ - - apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, - const char *name, size_t nlen, -- const char *value, size_t vlen); -+ const char *value, size_t vlen, -+ size_t max_field_len, int *pwas_added); - - apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool, - const char *name, size_t nlen, ---- a/modules/http2/h2_session.c -+++ b/modules/http2/h2_session.c -@@ -106,7 +106,7 @@ - - static void cleanup_unprocessed_streams(h2_session *session) - { -- h2_mplx_stream_do(session->mplx, rst_unprocessed_stream, session); -+ h2_mplx_m_stream_do(session->mplx, rst_unprocessed_stream, session); - } - - static h2_stream *h2_session_open_stream(h2_session *session, int stream_id, -@@ -385,14 +385,19 @@ - break; - case NGHTTP2_RST_STREAM: - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067) -- "h2_stream(%ld-%d): RST_STREAM by client, errror=%d", -+ "h2_stream(%ld-%d): RST_STREAM by client, error=%d", - session->id, (int)frame->hd.stream_id, - (int)frame->rst_stream.error_code); - stream = h2_session_stream_get(session, frame->hd.stream_id); - if (stream && stream->initiated_on) { -+ /* A stream reset on a request we sent it. Normal, when the -+ * client does not want it. */ - ++session->pushes_reset; - } - else { -+ /* A stream reset on a request it sent us. Could happen in a browser -+ * when the user navigates away or cancels loading - maybe. */ -+ h2_mplx_m_client_rst(session->mplx, frame->hd.stream_id); - ++session->streams_reset; - } - break; -@@ -462,7 +467,7 @@ - } - - static int h2_session_continue_data(h2_session *session) { -- if (h2_mplx_has_master_events(session->mplx)) { -+ if (h2_mplx_m_has_master_events(session->mplx)) { - return 0; - } - if (h2_conn_io_needs_flush(&session->io)) { -@@ -495,9 +500,7 @@ - return NGHTTP2_ERR_WOULDBLOCK; - } - -- if (frame->data.padlen > H2_MAX_PADLEN) { -- return NGHTTP2_ERR_PROTO; -- } -+ ap_assert(frame->data.padlen <= (H2_MAX_PADLEN+1)); - padlen = (unsigned char)frame->data.padlen; - - stream = h2_session_stream_get(session, stream_id); -@@ -513,8 +516,9 @@ - H2_STRM_MSG(stream, "send_data_cb for %ld bytes"), - (long)length); - -- status = h2_conn_io_write(&session->io, (const char *)framehd, 9); -+ status = h2_conn_io_write(&session->io, (const char *)framehd, H2_FRAME_HDR_LEN); - if (padlen && status == APR_SUCCESS) { -+ --padlen; - status = h2_conn_io_write(&session->io, (const char *)&padlen, 1); - } - -@@ -622,6 +626,39 @@ - } - #endif - -+static ssize_t select_padding_cb(nghttp2_session *ngh2, -+ const nghttp2_frame *frame, -+ size_t max_payloadlen, void *user_data) -+{ -+ h2_session *session = user_data; -+ ssize_t frame_len = frame->hd.length + H2_FRAME_HDR_LEN; /* the total length without padding */ -+ ssize_t padded_len = frame_len; -+ -+ /* Determine # of padding bytes to append to frame. Unless session->padding_always -+ * the number my be capped by the ui.write_size that currently applies. -+ */ -+ if (session->padding_max) { -+ int n = ap_random_pick(0, session->padding_max); -+ padded_len = H2MIN(max_payloadlen + H2_FRAME_HDR_LEN, frame_len + n); -+ } -+ -+ if (padded_len != frame_len) { -+ if (!session->padding_always && session->io.write_size -+ && (padded_len > session->io.write_size) -+ && (frame_len <= session->io.write_size)) { -+ padded_len = session->io.write_size; -+ } -+ if (APLOGctrace2(session->c)) { -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, -+ "select padding from [%d, %d]: %d (frame length: 0x%04x, write size: %d)", -+ (int)frame_len, (int)max_payloadlen+H2_FRAME_HDR_LEN, -+ (int)(padded_len - frame_len), (int)padded_len, (int)session->io.write_size); -+ } -+ return padded_len - H2_FRAME_HDR_LEN; -+ } -+ return frame->hd.length; -+} -+ - #define NGH2_SET_CALLBACK(callbacks, name, fn)\ - nghttp2_session_callbacks_set_##name##_callback(callbacks, fn) - -@@ -647,6 +684,7 @@ - #ifdef H2_NG2_INVALID_HEADER_CB - NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb); - #endif -+ NGH2_SET_CALLBACK(*pcb, select_padding, select_padding_cb); - return APR_SUCCESS; - } - -@@ -691,7 +729,7 @@ - * Remove all streams greater than this number without submitting - * a RST_STREAM frame, since that should be clear from the GOAWAY - * we send. */ -- session->local.accepted_max = h2_mplx_shutdown(session->mplx); -+ session->local.accepted_max = h2_mplx_m_shutdown(session->mplx); - session->local.error = error; - } - else { -@@ -741,7 +779,7 @@ - } - - transit(session, trigger, H2_SESSION_ST_CLEANUP); -- h2_mplx_release_and_join(session->mplx, session->iowait); -+ h2_mplx_m_release_and_join(session->mplx, session->iowait); - session->mplx = NULL; - - ap_assert(session->ngh2); -@@ -757,13 +795,12 @@ - { - conn_rec *c = data; - h2_session *session; -- h2_ctx *ctx = h2_ctx_get(c, 0); - -- if (ctx && (session = h2_ctx_session_get(ctx))) { -+ if ((session = h2_ctx_get_session(c))) { - /* if the session is still there, now is the last chance - * to perform cleanup. Normally, cleanup should have happened - * earlier in the connection pre_close. Main reason is that -- * any ongoing requests on slave connections might still access -+ * any ongoing requests on secondary connections might still access - * data which has, at this time, already been freed. An example - * is mod_ssl that uses request hooks. */ - ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, -@@ -775,11 +812,8 @@ - return APR_SUCCESS; - } - --static apr_status_t h2_session_create_int(h2_session **psession, -- conn_rec *c, -- request_rec *r, -- h2_ctx *ctx, -- h2_workers *workers) -+apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *r, -+ server_rec *s, h2_workers *workers) - { - nghttp2_session_callbacks *callbacks = NULL; - nghttp2_option *options = NULL; -@@ -820,19 +854,16 @@ - session->id = c->id; - session->c = c; - session->r = r; -- session->s = h2_ctx_server_get(ctx); -+ session->s = s; - session->pool = pool; -- session->config = h2_config_sget(session->s); - session->workers = workers; - - session->state = H2_SESSION_ST_INIT; - session->local.accepting = 1; - session->remote.accepting = 1; - -- session->max_stream_count = h2_config_geti(session->config, -- H2_CONF_MAX_STREAMS); -- session->max_stream_mem = h2_config_geti(session->config, -- H2_CONF_STREAM_MAX_MEM); -+ session->max_stream_count = h2_config_sgeti(s, H2_CONF_MAX_STREAMS); -+ session->max_stream_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM); - - status = apr_thread_cond_create(&session->iowait, session->pool); - if (status != APR_SUCCESS) { -@@ -862,14 +893,18 @@ - session->monitor->on_state_event = on_stream_state_event; - session->monitor->on_event = on_stream_event; - -- session->mplx = h2_mplx_create(c, session->pool, session->config, -- workers); -+ session->mplx = h2_mplx_m_create(c, s, session->pool, workers); - - /* connection input filter that feeds the session */ - session->cin = h2_filter_cin_create(session); - ap_add_input_filter("H2_IN", session->cin, r, c); - -- h2_conn_io_init(&session->io, c, session->config); -+ h2_conn_io_init(&session->io, c, s); -+ session->padding_max = h2_config_sgeti(s, H2_CONF_PADDING_BITS); -+ if (session->padding_max) { -+ session->padding_max = (0x01 << session->padding_max) - 1; -+ } -+ session->padding_always = h2_config_sgeti(s, H2_CONF_PADDING_ALWAYS); - session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc); - - status = init_callbacks(c, &callbacks); -@@ -888,8 +923,7 @@ - apr_pool_destroy(pool); - return status; - } -- nghttp2_option_set_peer_max_concurrent_streams( -- options, (uint32_t)session->max_stream_count); -+ nghttp2_option_set_peer_max_concurrent_streams(options, (uint32_t)session->max_stream_count); - /* We need to handle window updates ourself, otherwise we - * get flooded by nghttp2. */ - nghttp2_option_set_no_auto_window_update(options, 1); -@@ -907,7 +941,7 @@ - return APR_ENOMEM; - } - -- n = h2_config_geti(session->config, H2_CONF_PUSH_DIARY_SIZE); -+ n = h2_config_sgeti(s, H2_CONF_PUSH_DIARY_SIZE); - session->push_diary = h2_push_diary_create(session->pool, n); - - if (APLOGcdebug(c)) { -@@ -924,22 +958,11 @@ - (int)session->push_diary->N); - } - -- apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup); -+ apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup); -+ - return APR_SUCCESS; - } - --apr_status_t h2_session_create(h2_session **psession, -- conn_rec *c, h2_ctx *ctx, h2_workers *workers) --{ -- return h2_session_create_int(psession, c, NULL, ctx, workers); --} -- --apr_status_t h2_session_rcreate(h2_session **psession, -- request_rec *r, h2_ctx *ctx, h2_workers *workers) --{ -- return h2_session_create_int(psession, r->connection, r, ctx, workers); --} -- - static apr_status_t h2_session_start(h2_session *session, int *rv) - { - apr_status_t status = APR_SUCCESS; -@@ -1004,7 +1027,7 @@ - settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - settings[slen].value = (uint32_t)session->max_stream_count; - ++slen; -- win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE); -+ win_size = h2_config_sgeti(session->s, H2_CONF_WIN_SIZE); - if (win_size != H2_INITIAL_WINDOW_SIZE) { - settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - settings[slen].value = win_size; -@@ -1156,7 +1179,7 @@ - stream = h2_session_open_stream(session, nid, is->id); - if (!stream) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, -- H2_STRM_LOG(APLOGNO(03077), stream, -+ H2_STRM_LOG(APLOGNO(03077), is, - "failed to create stream obj %d"), nid); - /* kill the push_promise */ - nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid, -@@ -1262,7 +1285,7 @@ - - rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, -- ""H2_STRM_LOG(APLOGNO(03203), stream, -+ H2_STRM_LOG(APLOGNO(03203), stream, - "PUSH %s, weight=%d, depends=%d, returned=%d"), - ptype, ps.weight, ps.stream_id, rv); - status = (rv < 0)? APR_EGENERAL : APR_SUCCESS; -@@ -1280,7 +1303,7 @@ - { - /* iff we can and they can and want */ - return (session->remote.accepting /* remote GOAWAY received */ -- && h2_config_geti(session->config, H2_CONF_PUSH) -+ && h2_config_sgeti(session->s, H2_CONF_PUSH) - && nghttp2_session_get_remote_settings(session->ngh2, - NGHTTP2_SETTINGS_ENABLE_PUSH)); - } -@@ -1324,6 +1347,7 @@ - int eos) - { - apr_status_t status = APR_SUCCESS; -+ const char *s; - int rv = 0; - - ap_assert(session); -@@ -1391,8 +1415,12 @@ - && (headers->status < 400) - && (headers->status != 304) - && h2_session_push_enabled(session)) { -- -- h2_stream_submit_pushes(stream, headers); -+ /* PUSH is possible and enabled on server, unless the request -+ * denies it, submit resources to push */ -+ s = apr_table_get(headers->notes, H2_PUSH_MODE_NOTE); -+ if (!s || strcmp(s, "0")) { -+ h2_stream_submit_pushes(stream, headers); -+ } - } - - if (!stream->pref_priority) { -@@ -1414,7 +1442,7 @@ - } - - if (headers->status == 103 -- && !h2_config_geti(session->config, H2_CONF_EARLY_HINTS)) { -+ && !h2_config_sgeti(session->s, H2_CONF_EARLY_HINTS)) { - /* suppress sending this to the client, it might have triggered - * pushes and served its purpose nevertheless */ - rv = 0; -@@ -1524,7 +1552,7 @@ - if (stream) { - ap_assert(!stream->scheduled); - if (h2_stream_prep_processing(stream) == APR_SUCCESS) { -- h2_mplx_process(session->mplx, stream, stream_pri_cmp, session); -+ h2_mplx_m_process(session->mplx, stream, stream_pri_cmp, session); - } - else { - h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR); -@@ -1680,7 +1708,7 @@ - * that already served requests - not fair. */ - session->idle_sync_until = apr_time_now() + apr_time_from_sec(1); - s = "timeout"; -- timeout = H2MAX(session->s->timeout, session->s->keep_alive_timeout); -+ timeout = session->s->timeout; - update_child_status(session, SERVER_BUSY_READ, "idle"); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"), -@@ -1688,8 +1716,8 @@ - } - else if (session->open_streams) { - s = "timeout"; -- timeout = session->s->keep_alive_timeout; -- update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle"); -+ timeout = session->s->timeout; -+ update_child_status(session, SERVER_BUSY_READ, "idle"); - } - else { - /* normal keepalive setup */ -@@ -1796,7 +1824,7 @@ - session->open_streams); - h2_conn_io_flush(&session->io); - if (session->open_streams > 0) { -- if (h2_mplx_awaits_data(session->mplx)) { -+ if (h2_mplx_m_awaits_data(session->mplx)) { - /* waiting for at least one stream to produce data */ - transit(session, "no io", H2_SESSION_ST_WAIT); - } -@@ -1954,7 +1982,8 @@ - ev_stream_closed(session, stream); - break; - case H2_SS_CLEANUP: -- h2_mplx_stream_cleanup(session->mplx, stream); -+ nghttp2_session_set_stream_user_data(session->ngh2, stream->id, NULL); -+ h2_mplx_m_stream_cleanup(session->mplx, stream); - break; - default: - break; -@@ -2044,7 +2073,7 @@ - static apr_status_t dispatch_master(h2_session *session) { - apr_status_t status; - -- status = h2_mplx_dispatch_master_events(session->mplx, -+ status = h2_mplx_m_dispatch_master_events(session->mplx, - on_stream_resume, session); - if (status == APR_EAGAIN) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c, -@@ -2089,7 +2118,7 @@ - switch (session->state) { - case H2_SESSION_ST_INIT: - ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c); -- if (!h2_is_acceptable_connection(c, 1)) { -+ if (!h2_is_acceptable_connection(c, session->r, 1)) { - update_child_status(session, SERVER_BUSY_READ, - "inadequate security"); - h2_session_shutdown(session, -@@ -2112,7 +2141,7 @@ - break; - - case H2_SESSION_ST_IDLE: -- if (session->idle_until && (apr_time_now() + session->idle_delay) > session->idle_until) { -+ if (session->idle_until && (now + session->idle_delay) > session->idle_until) { - ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c, - H2_SSSN_MSG(session, "idle, timeout reached, closing")); - if (session->idle_delay) { -@@ -2146,6 +2175,14 @@ - session->have_read = 1; - } - else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { -+ status = h2_mplx_m_idle(session->mplx); -+ if (status == APR_EAGAIN) { -+ break; -+ } -+ else if (status != APR_SUCCESS) { -+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, -+ H2_ERR_ENHANCE_YOUR_CALM, "less is more"); -+ } - status = APR_EAGAIN; - goto out; - } -@@ -2168,7 +2205,7 @@ - /* We wait in smaller increments, using a 1 second timeout. - * That gives us the chance to check for MPMQ_STOPPING often. - */ -- status = h2_mplx_idle(session->mplx); -+ status = h2_mplx_m_idle(session->mplx); - if (status == APR_EAGAIN) { - break; - } -@@ -2282,7 +2319,7 @@ - "h2_session: wait for data, %ld micros", - (long)session->wait_us); - } -- status = h2_mplx_out_trywait(session->mplx, session->wait_us, -+ status = h2_mplx_m_out_trywait(session->mplx, session->wait_us, - session->iowait); - if (status == APR_SUCCESS) { - session->wait_us = 0; -@@ -2319,7 +2356,7 @@ - dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); - } - if (session->reprioritize) { -- h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session); -+ h2_mplx_m_reprioritize(session->mplx, stream_pri_cmp, session); - session->reprioritize = 0; - } - } ---- a/modules/http2/h2_session.h -+++ b/modules/http2/h2_session.h -@@ -80,12 +80,13 @@ - request_rec *r; /* the request that started this in case - * of 'h2c', NULL otherwise */ - server_rec *s; /* server/vhost we're starting on */ -- const struct h2_config *config; /* Relevant config for this session */ - apr_pool_t *pool; /* pool to use in session */ - struct h2_mplx *mplx; /* multiplexer for stream data */ - struct h2_workers *workers; /* for executing stream tasks */ - struct h2_filter_cin *cin; /* connection input filter context */ - h2_conn_io io; /* io on httpd conn filters */ -+ int padding_max; /* max number of padding bytes */ -+ int padding_always; /* padding has precedence over I/O optimizations */ - struct nghttp2_session *ngh2; /* the nghttp2 session (internal use) */ - - h2_session_state state; /* state session is in */ -@@ -131,7 +132,7 @@ - const char *last_status_msg; /* the one already reported */ - - struct h2_iqueue *in_pending; /* all streams with input pending */ -- struct h2_iqueue *in_process; /* all streams ready for processing on slave */ -+ struct h2_iqueue *in_process; /* all streams ready for processing on a secondary */ - - } h2_session; - -@@ -142,27 +143,15 @@ - * The session will apply the configured parameter. - * @param psession pointer receiving the created session on success or NULL - * @param c the connection to work on -+ * @param r optional request when protocol was upgraded - * @param cfg the module config to apply - * @param workers the worker pool to use - * @return the created session - */ - apr_status_t h2_session_create(h2_session **psession, -- conn_rec *c, struct h2_ctx *ctx, -+ conn_rec *c, request_rec *r, server_rec *, - struct h2_workers *workers); - --/** -- * Create a new h2_session for the given request. -- * The session will apply the configured parameter. -- * @param psession pointer receiving the created session on success or NULL -- * @param r the request that was upgraded -- * @param cfg the module config to apply -- * @param workers the worker pool to use -- * @return the created session -- */ --apr_status_t h2_session_rcreate(h2_session **psession, -- request_rec *r, struct h2_ctx *ctx, -- struct h2_workers *workers); -- - void h2_session_event(h2_session *session, h2_session_event_t ev, - int err, const char *msg); - ---- a/modules/http2/h2_stream.c -+++ b/modules/http2/h2_stream.c -@@ -365,9 +365,8 @@ - static void set_policy_for(h2_stream *stream, h2_request *r) - { - int enabled = h2_session_push_enabled(stream->session); -- stream->push_policy = h2_push_policy_determine(r->headers, stream->pool, -- enabled); -- r->serialize = h2_config_geti(stream->session->config, H2_CONF_SER_HEADERS); -+ stream->push_policy = h2_push_policy_determine(r->headers, stream->pool, enabled); -+ r->serialize = h2_config_sgeti(stream->session->s, H2_CONF_SER_HEADERS); - } - - apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags, size_t frame_len) -@@ -398,13 +397,8 @@ - /* start pushed stream */ - ap_assert(stream->request == NULL); - ap_assert(stream->rtmp != NULL); -- status = h2_request_end_headers(stream->rtmp, stream->pool, 1, 0); -- if (status != APR_SUCCESS) { -- return status; -- } -- set_policy_for(stream, stream->rtmp); -- stream->request = stream->rtmp; -- stream->rtmp = NULL; -+ status = h2_stream_end_headers(stream, 1, 0); -+ if (status != APR_SUCCESS) goto leave; - break; - - default: -@@ -416,6 +410,7 @@ - if (status == APR_SUCCESS && eos) { - status = transit(stream, on_event(stream, H2_SEV_CLOSED_L)); - } -+leave: - return status; - } - -@@ -451,18 +446,13 @@ - ap_assert(stream->request == NULL); - if (stream->rtmp == NULL) { - /* This can only happen, if the stream has received no header -- * name/value pairs at all. The lastest nghttp2 version have become -+ * name/value pairs at all. The latest nghttp2 version have become - * pretty good at detecting this early. In any case, we have - * to abort the connection here, since this is clearly a protocol error */ - return APR_EINVAL; - } -- status = h2_request_end_headers(stream->rtmp, stream->pool, eos, frame_len); -- if (status != APR_SUCCESS) { -- return status; -- } -- set_policy_for(stream, stream->rtmp); -- stream->request = stream->rtmp; -- stream->rtmp = NULL; -+ status = h2_stream_end_headers(stream, eos, frame_len); -+ if (status != APR_SUCCESS) goto leave; - } - break; - -@@ -473,6 +463,7 @@ - if (status == APR_SUCCESS && eos) { - status = transit(stream, on_event(stream, H2_SEV_CLOSED_R)); - } -+leave: - return status; - } - -@@ -663,11 +654,14 @@ - - static apr_status_t add_trailer(h2_stream *stream, - const char *name, size_t nlen, -- const char *value, size_t vlen) -+ const char *value, size_t vlen, -+ size_t max_field_len, int *pwas_added) - { - conn_rec *c = stream->session->c; - char *hname, *hvalue; -+ const char *existing; - -+ *pwas_added = 0; - if (nlen == 0 || name[0] == ':') { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c, - H2_STRM_LOG(APLOGNO(03060), stream, -@@ -681,9 +675,18 @@ - stream->trailers = apr_table_make(stream->pool, 5); - } - hname = apr_pstrndup(stream->pool, name, nlen); -- hvalue = apr_pstrndup(stream->pool, value, vlen); - h2_util_camel_case_header(hname, nlen); -+ existing = apr_table_get(stream->trailers, hname); -+ if (max_field_len -+ && ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len)) { -+ /* "key: (oldval, )?nval" is too long */ -+ return APR_EINVAL; -+ } -+ if (!existing) *pwas_added = 1; -+ hvalue = apr_pstrndup(stream->pool, value, vlen); - apr_table_mergen(stream->trailers, hname, hvalue); -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, -+ H2_STRM_MSG(stream, "added trailer '%s: %s'"), hname, hvalue); - - return APR_SUCCESS; - } -@@ -693,44 +696,31 @@ - const char *value, size_t vlen) - { - h2_session *session = stream->session; -- int error = 0; -- apr_status_t status; -+ int error = 0, was_added = 0; -+ apr_status_t status = APR_SUCCESS; - - if (stream->has_response) { - return APR_EINVAL; - } -- ++stream->request_headers_added; -+ - if (name[0] == ':') { - if ((vlen) > session->s->limit_req_line) { - /* pseudo header: approximation of request line size check */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, -- H2_STRM_MSG(stream, "pseudo %s too long"), name); -+ if (!h2_stream_is_ready(stream)) { -+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c, -+ H2_STRM_LOG(APLOGNO(10178), stream, -+ "Request pseudo header exceeds " -+ "LimitRequestFieldSize: %s"), name); -+ } - error = HTTP_REQUEST_URI_TOO_LARGE; -+ goto cleanup; - } - } -- else if ((nlen + 2 + vlen) > session->s->limit_req_fieldsize) { -- /* header too long */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, -- H2_STRM_MSG(stream, "header %s too long"), name); -- error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; -- } -- -- if (stream->request_headers_added > session->s->limit_req_fields + 4) { -- /* too many header lines, include 4 pseudo headers */ -- if (stream->request_headers_added -- > session->s->limit_req_fields + 4 + 100) { -- /* yeah, right */ -- h2_stream_rst(stream, H2_ERR_ENHANCE_YOUR_CALM); -- return APR_ECONNRESET; -- } -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, -- H2_STRM_MSG(stream, "too many header lines")); -- error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; -- } - -- if (error) { -- set_error_response(stream, error); -- return APR_EINVAL; -+ if (session->s->limit_req_fields > 0 -+ && stream->request_headers_added > session->s->limit_req_fields) { -+ /* already over limit, count this attempt, but do not take it in */ -+ ++stream->request_headers_added; - } - else if (H2_SS_IDLE == stream->state) { - if (!stream->rtmp) { -@@ -738,16 +728,55 @@ - NULL, NULL, NULL, NULL, NULL, 0); - } - status = h2_request_add_header(stream->rtmp, stream->pool, -- name, nlen, value, vlen); -+ name, nlen, value, vlen, -+ session->s->limit_req_fieldsize, &was_added); -+ if (was_added) ++stream->request_headers_added; - } - else if (H2_SS_OPEN == stream->state) { -- status = add_trailer(stream, name, nlen, value, vlen); -+ status = add_trailer(stream, name, nlen, value, vlen, -+ session->s->limit_req_fieldsize, &was_added); -+ if (was_added) ++stream->request_headers_added; - } - else { - status = APR_EINVAL; -+ goto cleanup; -+ } -+ -+ if (APR_EINVAL == status) { -+ /* header too long */ -+ if (!h2_stream_is_ready(stream)) { -+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c, -+ H2_STRM_LOG(APLOGNO(10180), stream,"Request header exceeds " -+ "LimitRequestFieldSize: %.*s"), -+ (int)H2MIN(nlen, 80), name); -+ } -+ error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; -+ goto cleanup; -+ } -+ -+ if (session->s->limit_req_fields > 0 -+ && stream->request_headers_added > session->s->limit_req_fields) { -+ /* too many header lines */ -+ if (stream->request_headers_added > session->s->limit_req_fields + 100) { -+ /* yeah, right, this request is way over the limit, say goodbye */ -+ h2_stream_rst(stream, H2_ERR_ENHANCE_YOUR_CALM); -+ return APR_ECONNRESET; -+ } -+ if (!h2_stream_is_ready(stream)) { -+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c, -+ H2_STRM_LOG(APLOGNO(10181), stream, "Number of request headers " -+ "exceeds LimitRequestFields")); -+ } -+ error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE; -+ goto cleanup; - } - -- if (status != APR_SUCCESS) { -+cleanup: -+ if (error) { -+ set_error_response(stream, error); -+ return APR_EINVAL; -+ } -+ else if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - H2_STRM_MSG(stream, "header %s not accepted"), name); - h2_stream_dispatch(stream, H2_SEV_CANCELLED); -@@ -755,6 +784,49 @@ - return status; - } - -+typedef struct { -+ apr_size_t maxlen; -+ const char *failed_key; -+} val_len_check_ctx; -+ -+static int table_check_val_len(void *baton, const char *key, const char *value) -+{ -+ val_len_check_ctx *ctx = baton; -+ -+ if (strlen(value) <= ctx->maxlen) return 1; -+ ctx->failed_key = key; -+ return 0; -+} -+ -+apr_status_t h2_stream_end_headers(h2_stream *stream, int eos, size_t raw_bytes) -+{ -+ apr_status_t status; -+ val_len_check_ctx ctx; -+ -+ status = h2_request_end_headers(stream->rtmp, stream->pool, eos, raw_bytes); -+ if (APR_SUCCESS == status) { -+ set_policy_for(stream, stream->rtmp); -+ stream->request = stream->rtmp; -+ stream->rtmp = NULL; -+ -+ ctx.maxlen = stream->session->s->limit_req_fieldsize; -+ ctx.failed_key = NULL; -+ apr_table_do(table_check_val_len, &ctx, stream->request->headers, NULL); -+ if (ctx.failed_key) { -+ if (!h2_stream_is_ready(stream)) { -+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, stream->session->c, -+ H2_STRM_LOG(APLOGNO(10230), stream,"Request header exceeds " -+ "LimitRequestFieldSize: %.*s"), -+ (int)H2MIN(strlen(ctx.failed_key), 80), ctx.failed_key); -+ } -+ set_error_response(stream, HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE); -+ /* keep on returning APR_SUCCESS, so that we send a HTTP response and -+ * do not RST the stream. */ -+ } -+ } -+ return status; -+} -+ - static apr_bucket *get_first_headers_bucket(apr_bucket_brigade *bb) - { - if (bb) { -@@ -855,7 +927,7 @@ - * is requested. But we can reduce the size in case the master - * connection operates in smaller chunks. (TSL warmup) */ - if (stream->session->io.write_size > 0) { -- max_chunk = stream->session->io.write_size - 9; /* header bits */ -+ max_chunk = stream->session->io.write_size - H2_FRAME_HDR_LEN; - } - requested = (*plen > 0)? H2MIN(*plen, max_chunk) : max_chunk; - -@@ -864,7 +936,7 @@ - - if (status == APR_EAGAIN) { - /* TODO: ugly, someone needs to retrieve the response first */ -- h2_mplx_keep_active(stream->session->mplx, stream); -+ h2_mplx_m_keep_active(stream->session->mplx, stream); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - H2_STRM_MSG(stream, "prep, response eagain")); - return status; -@@ -987,7 +1059,7 @@ - const char *ctype = apr_table_get(response->headers, "content-type"); - if (ctype) { - /* FIXME: Not good enough, config needs to come from request->server */ -- return h2_config_get_priority(stream->session->config, ctype); -+ return h2_cconfig_get_priority(stream->session->c, ctype); - } - } - return NULL; ---- a/modules/http2/h2_stream.h -+++ b/modules/http2/h2_stream.h -@@ -198,6 +198,10 @@ - apr_status_t h2_stream_add_header(h2_stream *stream, - const char *name, size_t nlen, - const char *value, size_t vlen); -+ -+/* End the construction of request headers */ -+apr_status_t h2_stream_end_headers(h2_stream *stream, int eos, size_t raw_bytes); -+ - - apr_status_t h2_stream_send_frame(h2_stream *stream, int frame_type, int flags, size_t frame_len); - apr_status_t h2_stream_recv_frame(h2_stream *stream, int frame_type, int flags, size_t frame_len); ---- a/modules/http2/h2_switch.c -+++ b/modules/http2/h2_switch.c -@@ -55,7 +55,6 @@ - int is_tls = h2_h2_is_tls(c); - const char **protos = is_tls? h2_tls_protos : h2_clear_protos; - -- (void)s; - if (!h2_mpm_supported()) { - return DECLINED; - } -@@ -68,7 +67,7 @@ - return DECLINED; - } - -- if (!h2_is_acceptable_connection(c, 0)) { -+ if (!h2_is_acceptable_connection(c, r, 0)) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084) - "protocol propose: connection requirements not met"); - return DECLINED; -@@ -81,7 +80,7 @@ - */ - const char *p; - -- if (!h2_allows_h2_upgrade(c)) { -+ if (!h2_allows_h2_upgrade(r)) { - return DECLINED; - } - -@@ -150,7 +149,7 @@ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - "switching protocol to '%s'", protocol); - h2_ctx_protocol_set(ctx, protocol); -- h2_ctx_server_set(ctx, s); -+ h2_ctx_server_update(ctx, s); - - if (r != NULL) { - apr_status_t status; -@@ -160,12 +159,11 @@ - * right away. - */ - ap_remove_input_filter_byhandle(r->input_filters, "http_in"); -- ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout"); - ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); - - /* Ok, start an h2_conn on this one. */ -- h2_ctx_server_set(ctx, r->server); -- status = h2_conn_setup(ctx, r->connection, r); -+ status = h2_conn_setup(c, r, s); -+ - if (status != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088) - "session setup"); -@@ -173,7 +171,7 @@ - return !OK; - } - -- h2_conn_run(ctx, c); -+ h2_conn_run(c); - } - return OK; - } ---- a/modules/http2/h2_task.c -+++ b/modules/http2/h2_task.c -@@ -86,7 +86,7 @@ - task->request->authority, - task->request->path); - task->output.opened = 1; -- return h2_mplx_out_open(task->mplx, task->stream_id, task->output.beam); -+ return h2_mplx_t_out_open(task->mplx, task->stream_id, task->output.beam); - } - - static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block) -@@ -97,7 +97,7 @@ - apr_brigade_length(bb, 0, &written); - H2_TASK_OUT_LOG(APLOG_TRACE2, task, bb, "h2_task send_out"); - h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(before)"); -- /* engines send unblocking */ -+ - status = h2_beam_send(task->output.beam, bb, - block? APR_BLOCK_READ : APR_NONBLOCK_READ); - h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(after)"); -@@ -126,33 +126,16 @@ - * request_rec out filter chain) into the h2_mplx for further sending - * on the master connection. - */ --static apr_status_t slave_out(h2_task *task, ap_filter_t* f, -- apr_bucket_brigade* bb) -+static apr_status_t secondary_out(h2_task *task, ap_filter_t* f, -+ apr_bucket_brigade* bb) - { - apr_bucket *b; - apr_status_t rv = APR_SUCCESS; - int flush = 0, blocking; - -- if (task->frozen) { -- h2_util_bb_log(task->c, task->stream_id, APLOG_TRACE2, -- "frozen task output write, ignored", bb); -- while (!APR_BRIGADE_EMPTY(bb)) { -- b = APR_BRIGADE_FIRST(bb); -- if (AP_BUCKET_IS_EOR(b)) { -- APR_BUCKET_REMOVE(b); -- task->eor = b; -- } -- else { -- apr_bucket_delete(b); -- } -- } -- return APR_SUCCESS; -- } -- - send: -- /* we send block once we opened the output, so someone is there -- * reading it *and* the task is not assigned to a h2_req_engine */ -- blocking = (!task->assigned && task->output.opened); -+ /* we send block once we opened the output, so someone is there reading it */ -+ blocking = task->output.opened; - for (b = APR_BRIGADE_FIRST(bb); - b != APR_BRIGADE_SENTINEL(bb); - b = APR_BUCKET_NEXT(b)) { -@@ -192,7 +175,7 @@ - if (APR_SUCCESS == rv) { - /* could not write all, buffer the rest */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, task->c, APLOGNO(03405) -- "h2_slave_out(%s): saving brigade", task->id); -+ "h2_secondary_out(%s): saving brigade", task->id); - ap_assert(NULL); - rv = ap_save_brigade(f, &task->output.bb, &bb, task->pool); - flush = 1; -@@ -206,7 +189,7 @@ - } - out: - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, task->c, -- "h2_slave_out(%s): slave_out leave", task->id); -+ "h2_secondary_out(%s): secondary_out leave", task->id); - return rv; - } - -@@ -219,14 +202,14 @@ - } - - /******************************************************************************* -- * task slave connection filters -+ * task secondary connection filters - ******************************************************************************/ - --static apr_status_t h2_filter_slave_in(ap_filter_t* f, -- apr_bucket_brigade* bb, -- ap_input_mode_t mode, -- apr_read_type_e block, -- apr_off_t readbytes) -+static apr_status_t h2_filter_secondary_in(ap_filter_t* f, -+ apr_bucket_brigade* bb, -+ ap_input_mode_t mode, -+ apr_read_type_e block, -+ apr_off_t readbytes) - { - h2_task *task; - apr_status_t status = APR_SUCCESS; -@@ -236,12 +219,12 @@ - apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? - (apr_size_t)readbytes : APR_SIZE_MAX); - -- task = h2_ctx_cget_task(f->c); -+ task = h2_ctx_get_task(f->c); - ap_assert(task); - - if (trace1) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, -- "h2_slave_in(%s): read, mode=%d, block=%d, readbytes=%ld", -+ "h2_secondary_in(%s): read, mode=%d, block=%d, readbytes=%ld", - task->id, mode, block, (long)readbytes); - } - -@@ -271,7 +254,7 @@ - /* Get more input data for our request. */ - if (trace1) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, -- "h2_slave_in(%s): get more data from mplx, block=%d, " -+ "h2_secondary_in(%s): get more data from mplx, block=%d, " - "readbytes=%ld", task->id, block, (long)readbytes); - } - if (task->input.beam) { -@@ -284,7 +267,7 @@ - - if (trace1) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, -- "h2_slave_in(%s): read returned", task->id); -+ "h2_secondary_in(%s): read returned", task->id); - } - if (APR_STATUS_IS_EAGAIN(status) - && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) { -@@ -310,11 +293,9 @@ - } - } - -- /* Nothing there, no more data to get. Return APR_EAGAIN on -- * speculative reads, this is ap_check_pipeline()'s trick to -- * see if the connection needs closing. */ -+ /* Nothing there, no more data to get. Return. */ - if (status == APR_EOF && APR_BRIGADE_EMPTY(task->input.bb)) { -- return (mode == AP_MODE_SPECULATIVE)? APR_EAGAIN : APR_EOF; -+ return status; - } - - if (trace1) { -@@ -325,7 +306,7 @@ - if (APR_BRIGADE_EMPTY(task->input.bb)) { - if (trace1) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, -- "h2_slave_in(%s): no data", task->id); -+ "h2_secondary_in(%s): no data", task->id); - } - return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; - } -@@ -353,7 +334,7 @@ - buffer[len] = 0; - if (trace1) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, -- "h2_slave_in(%s): getline: %s", -+ "h2_secondary_in(%s): getline: %s", - task->id, buffer); - } - } -@@ -363,7 +344,7 @@ - * to support it. Seems to work. */ - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, - APLOGNO(03472) -- "h2_slave_in(%s), unsupported READ mode %d", -+ "h2_secondary_in(%s), unsupported READ mode %d", - task->id, mode); - status = APR_ENOTIMPL; - } -@@ -371,19 +352,19 @@ - if (trace1) { - apr_brigade_length(bb, 0, &bblen); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, -- "h2_slave_in(%s): %ld data bytes", task->id, (long)bblen); -+ "h2_secondary_in(%s): %ld data bytes", task->id, (long)bblen); - } - return status; - } - --static apr_status_t h2_filter_slave_output(ap_filter_t* filter, -- apr_bucket_brigade* brigade) -+static apr_status_t h2_filter_secondary_output(ap_filter_t* filter, -+ apr_bucket_brigade* brigade) - { -- h2_task *task = h2_ctx_cget_task(filter->c); -+ h2_task *task = h2_ctx_get_task(filter->c); - apr_status_t status; - - ap_assert(task); -- status = slave_out(task, filter, brigade); -+ status = secondary_out(task, filter, brigade); - if (status != APR_SUCCESS) { - h2_task_rst(task, H2_ERR_INTERNAL_ERROR); - } -@@ -392,14 +373,14 @@ - - static apr_status_t h2_filter_parse_h1(ap_filter_t* f, apr_bucket_brigade* bb) - { -- h2_task *task = h2_ctx_cget_task(f->c); -+ h2_task *task = h2_ctx_get_task(f->c); - apr_status_t status; - - ap_assert(task); - /* There are cases where we need to parse a serialized http/1.1 - * response. One example is a 100-continue answer in serialized mode - * or via a mod_proxy setup */ -- while (bb && !task->output.sent_response) { -+ while (bb && !task->c->aborted && !task->output.sent_response) { - status = h2_from_h1_parse_response(task, f, bb); - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, - "h2_task(%s): parsed response", task->id); -@@ -425,8 +406,15 @@ - || !strcmp("OPTIONS", task->request->method)); - } - -+int h2_task_has_started(h2_task *task) -+{ -+ return task && task->started_at != 0; -+} -+ - void h2_task_redo(h2_task *task) - { -+ task->started_at = 0; -+ task->worker_done = 0; - task->rst_error = 0; - } - -@@ -468,9 +456,9 @@ - ap_hook_process_connection(h2_task_process_conn, - NULL, NULL, APR_HOOK_FIRST); - -- ap_register_input_filter("H2_SLAVE_IN", h2_filter_slave_in, -+ ap_register_input_filter("H2_SECONDARY_IN", h2_filter_secondary_in, - NULL, AP_FTYPE_NETWORK); -- ap_register_output_filter("H2_SLAVE_OUT", h2_filter_slave_output, -+ ap_register_output_filter("H2_SECONDARY_OUT", h2_filter_secondary_output, - NULL, AP_FTYPE_NETWORK); - ap_register_output_filter("H2_PARSE_H1", h2_filter_parse_h1, - NULL, AP_FTYPE_NETWORK); -@@ -502,17 +490,17 @@ - - ctx = h2_ctx_get(c, 0); - (void)arg; -- if (h2_ctx_is_task(ctx)) { -+ if (ctx->task) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, -- "h2_slave(%s), pre_connection, adding filters", c->log_id); -- ap_add_input_filter("H2_SLAVE_IN", NULL, NULL, c); -+ "h2_secondary(%s), pre_connection, adding filters", c->log_id); -+ ap_add_input_filter("H2_SECONDARY_IN", NULL, NULL, c); - ap_add_output_filter("H2_PARSE_H1", NULL, NULL, c); -- ap_add_output_filter("H2_SLAVE_OUT", NULL, NULL, c); -+ ap_add_output_filter("H2_SECONDARY_OUT", NULL, NULL, c); - } - return OK; - } - --h2_task *h2_task_create(conn_rec *slave, int stream_id, -+h2_task *h2_task_create(conn_rec *secondary, int stream_id, - const h2_request *req, h2_mplx *m, - h2_bucket_beam *input, - apr_interval_time_t timeout, -@@ -521,17 +509,18 @@ - apr_pool_t *pool; - h2_task *task; - -- ap_assert(slave); -+ ap_assert(secondary); - ap_assert(req); - -- apr_pool_create(&pool, slave->pool); -+ apr_pool_create(&pool, secondary->pool); -+ apr_pool_tag(pool, "h2_task"); - task = apr_pcalloc(pool, sizeof(h2_task)); - if (task == NULL) { - return NULL; - } - task->id = "000"; - task->stream_id = stream_id; -- task->c = slave; -+ task->c = secondary; - task->mplx = m; - task->pool = pool; - task->request = req; -@@ -564,41 +553,40 @@ - ap_assert(task); - c = task->c; - task->worker_started = 1; -- task->started_at = apr_time_now(); - - if (c->master) { -- /* Each conn_rec->id is supposed to be unique at a point in time. Since -+ /* See the discussion at <https://github.com/icing/mod_h2/issues/195> -+ * -+ * Each conn_rec->id is supposed to be unique at a point in time. Since - * some modules (and maybe external code) uses this id as an identifier -- * for the request_rec they handle, it needs to be unique for slave -+ * for the request_rec they handle, it needs to be unique for secondary - * connections also. -- * The connection id is generated by the MPM and most MPMs use the formula -- * id := (child_num * max_threads) + thread_num -- * which means that there is a maximum id of about -- * idmax := max_child_count * max_threads -- * If we assume 2024 child processes with 2048 threads max, we get -- * idmax ~= 2024 * 2048 = 2 ** 22 -- * On 32 bit systems, we have not much space left, but on 64 bit systems -- * (and higher?) we can use the upper 32 bits without fear of collision. -- * 32 bits is just what we need, since a connection can only handle so -- * many streams. -+ * -+ * The MPM module assigns the connection ids and mod_unique_id is using -+ * that one to generate identifier for requests. While the implementation -+ * works for HTTP/1.x, the parallel execution of several requests per -+ * connection will generate duplicate identifiers on load. -+ * -+ * The original implementation for secondary connection identifiers used -+ * to shift the master connection id up and assign the stream id to the -+ * lower bits. This was cramped on 32 bit systems, but on 64bit there was -+ * enough space. -+ * -+ * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the -+ * connection id, even on 64bit systems. Therefore collisions in request ids. -+ * -+ * The way master connection ids are generated, there is some space "at the -+ * top" of the lower 32 bits on allmost all systems. If you have a setup -+ * with 64k threads per child and 255 child processes, you live on the edge. -+ * -+ * The new implementation shifts 8 bits and XORs in the worker -+ * id. This will experience collisions with > 256 h2 workers and heavy -+ * load still. There seems to be no way to solve this in all possible -+ * configurations by mod_h2 alone. - */ -- int slave_id, free_bits; -- -+ task->c->id = (c->master->id << 8)^worker_id; - task->id = apr_psprintf(task->pool, "%ld-%d", c->master->id, - task->stream_id); -- if (sizeof(unsigned long) >= 8) { -- free_bits = 32; -- slave_id = task->stream_id; -- } -- else { -- /* Assume we have a more limited number of threads/processes -- * and h2 workers on a 32-bit system. Use the worker instead -- * of the stream id. */ -- free_bits = 8; -- slave_id = worker_id; -- } -- task->c->id = (c->master->id << free_bits)^slave_id; -- c->keepalive = AP_CONN_KEEPALIVE; - } - - h2_beam_create(&task->output.beam, c->pool, task->stream_id, "output", -@@ -613,7 +601,7 @@ - h2_ctx_create_for(c, task); - apr_table_setn(c->notes, H2_TASK_ID_NOTE, task->id); - -- h2_slave_run_pre_connection(c, ap_get_conn_socket(c)); -+ h2_secondary_run_pre_connection(c, ap_get_conn_socket(c)); - - task->input.bb = apr_brigade_create(task->pool, c->bucket_alloc); - if (task->request->serialize) { -@@ -633,18 +621,9 @@ - task->c->current_thread = thread; - ap_run_process_connection(c); - -- if (task->frozen) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "h2_task(%s): process_conn returned frozen task", -- task->id); -- /* cleanup delayed */ -- return APR_EAGAIN; -- } -- else { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "h2_task(%s): processing done", task->id); -- return output_finish(task); -- } -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -+ "h2_task(%s): processing done", task->id); -+ return output_finish(task); - } - - static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c) -@@ -682,14 +661,8 @@ - - ap_process_request(r); - -- if (task->frozen) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "h2_task(%s): process_request frozen", task->id); -- } -- else { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "h2_task(%s): process_request done", task->id); -- } -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -+ "h2_task(%s): process_request done", task->id); - - /* After the call to ap_process_request, the - * request pool may have been deleted. We set -@@ -724,7 +697,7 @@ - } - - ctx = h2_ctx_get(c, 0); -- if (h2_ctx_is_task(ctx)) { -+ if (ctx->task) { - if (!ctx->task->request->serialize) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - "h2_h2, processing request directly"); -@@ -736,33 +709,8 @@ - } - else { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "slave_conn(%ld): has no task", c->id); -+ "secondary_conn(%ld): has no task", c->id); - } - return DECLINED; - } - --apr_status_t h2_task_freeze(h2_task *task) --{ -- if (!task->frozen) { -- task->frozen = 1; -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, APLOGNO(03406) -- "h2_task(%s), frozen", task->id); -- } -- return APR_SUCCESS; --} -- --apr_status_t h2_task_thaw(h2_task *task) --{ -- if (task->frozen) { -- task->frozen = 0; -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, APLOGNO(03407) -- "h2_task(%s), thawed", task->id); -- } -- task->thawed = 1; -- return APR_SUCCESS; --} -- --int h2_task_has_thawed(h2_task *task) --{ -- return task->thawed; --} ---- a/modules/http2/h2_task.h -+++ b/modules/http2/h2_task.h -@@ -35,14 +35,13 @@ - * - * Finally, to keep certain connection level filters, such as ourselves and - * especially mod_ssl ones, from messing with our data, we need a filter -- * of our own to disble those. -+ * of our own to disable those. - */ - - struct h2_bucket_beam; - struct h2_conn; - struct h2_mplx; - struct h2_task; --struct h2_req_engine; - struct h2_request; - struct h2_response_parser; - struct h2_stream; -@@ -80,20 +79,18 @@ - struct h2_mplx *mplx; - - unsigned int filters_set : 1; -- unsigned int frozen : 1; -- unsigned int thawed : 1; - unsigned int worker_started : 1; /* h2_worker started processing */ -- unsigned int worker_done : 1; /* h2_worker finished */ -+ unsigned int redo : 1; /* was throttled, should be restarted later */ -+ -+ int worker_done; /* h2_worker finished */ -+ int done_done; /* task_done has been handled */ - - apr_time_t started_at; /* when processing started */ - apr_time_t done_at; /* when processing was done */ - apr_bucket *eor; -- -- struct h2_req_engine *engine; /* engine hosted by this task */ -- struct h2_req_engine *assigned; /* engine that task has been assigned to */ - }; - --h2_task *h2_task_create(conn_rec *slave, int stream_id, -+h2_task *h2_task_create(conn_rec *secondary, int stream_id, - const h2_request *req, struct h2_mplx *m, - struct h2_bucket_beam *input, - apr_interval_time_t timeout, -@@ -105,6 +102,7 @@ - - void h2_task_redo(h2_task *task); - int h2_task_can_redo(h2_task *task); -+int h2_task_has_started(h2_task *task); - - /** - * Reset the task with the given error code, resets all input/output. -@@ -120,8 +118,4 @@ - extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in; - extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out; - --apr_status_t h2_task_freeze(h2_task *task); --apr_status_t h2_task_thaw(h2_task *task); --int h2_task_has_thawed(h2_task *task); -- - #endif /* defined(__mod_h2__h2_task__) */ ---- a/modules/http2/h2_util.c -+++ b/modules/http2/h2_util.c -@@ -638,15 +638,6 @@ - apr_status_t rv; - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - fifo->aborted = 1; -- apr_thread_mutex_unlock(fifo->lock); -- } -- return rv; --} -- --apr_status_t h2_fifo_interrupt(h2_fifo *fifo) --{ -- apr_status_t rv; -- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - apr_thread_cond_broadcast(fifo->not_empty); - apr_thread_cond_broadcast(fifo->not_full); - apr_thread_mutex_unlock(fifo->lock); -@@ -710,10 +701,6 @@ - { - apr_status_t rv; - -- if (fifo->aborted) { -- return APR_EOF; -- } -- - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - rv = fifo_push_int(fifo, elem, block); - apr_thread_mutex_unlock(fifo->lock); -@@ -754,10 +741,6 @@ - { - apr_status_t rv; - -- if (fifo->aborted) { -- return APR_EOF; -- } -- - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - rv = pull_head(fifo, pelem, block); - apr_thread_mutex_unlock(fifo->lock); -@@ -946,15 +929,6 @@ - apr_status_t rv; - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - fifo->aborted = 1; -- apr_thread_mutex_unlock(fifo->lock); -- } -- return rv; --} -- --apr_status_t h2_ififo_interrupt(h2_ififo *fifo) --{ -- apr_status_t rv; -- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - apr_thread_cond_broadcast(fifo->not_empty); - apr_thread_cond_broadcast(fifo->not_full); - apr_thread_mutex_unlock(fifo->lock); -@@ -1018,10 +992,6 @@ - { - apr_status_t rv; - -- if (fifo->aborted) { -- return APR_EOF; -- } -- - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - rv = ififo_push_int(fifo, id, block); - apr_thread_mutex_unlock(fifo->lock); -@@ -1062,10 +1032,6 @@ - { - apr_status_t rv; - -- if (fifo->aborted) { -- return APR_EOF; -- } -- - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - rv = ipull_head(fifo, pi, block); - apr_thread_mutex_unlock(fifo->lock); -@@ -1088,10 +1054,6 @@ - apr_status_t rv; - int id; - -- if (fifo->aborted) { -- return APR_EOF; -- } -- - if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) { - if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) { - switch (fn(id, ctx)) { -@@ -1117,39 +1079,40 @@ - return ififo_peek(fifo, fn, ctx, 0); - } - --apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) -+static apr_status_t ififo_remove(h2_ififo *fifo, int id) - { -- apr_status_t rv; -+ int rc, i; - - if (fifo->aborted) { - return APR_EOF; - } - -- if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { -- int i, rc; -- int e; -- -- rc = 0; -- for (i = 0; i < fifo->count; ++i) { -- e = fifo->elems[inth_index(fifo, i)]; -- if (e == id) { -- ++rc; -- } -- else if (rc) { -- fifo->elems[inth_index(fifo, i-rc)] = e; -- } -- } -- if (rc) { -- fifo->count -= rc; -- if (fifo->count + rc == fifo->nelems) { -- apr_thread_cond_broadcast(fifo->not_full); -- } -- rv = APR_SUCCESS; -+ rc = 0; -+ for (i = 0; i < fifo->count; ++i) { -+ int e = fifo->elems[inth_index(fifo, i)]; -+ if (e == id) { -+ ++rc; - } -- else { -- rv = APR_EAGAIN; -+ else if (rc) { -+ fifo->elems[inth_index(fifo, i-rc)] = e; - } -- -+ } -+ if (!rc) { -+ return APR_EAGAIN; -+ } -+ fifo->count -= rc; -+ if (fifo->count + rc == fifo->nelems) { -+ apr_thread_cond_broadcast(fifo->not_full); -+ } -+ return APR_SUCCESS; -+} -+ -+apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) -+{ -+ apr_status_t rv; -+ -+ if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { -+ rv = ififo_remove(fifo, id); - apr_thread_mutex_unlock(fifo->lock); - } - return rv; -@@ -1373,7 +1336,7 @@ - return status; - } - else if (blen == 0) { -- /* brigade without data, does it have an EOS bucket somwhere? */ -+ /* brigade without data, does it have an EOS bucket somewhere? */ - *plen = 0; - *peos = h2_util_has_eos(bb, -1); - } -@@ -1840,22 +1803,29 @@ - } - - apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool, -- const char *name, size_t nlen, -- const char *value, size_t vlen) -+ const char *name, size_t nlen, -+ const char *value, size_t vlen, -+ size_t max_field_len, int *pwas_added) - { - char *hname, *hvalue; -+ const char *existing; - -+ *pwas_added = 0; - if (h2_req_ignore_header(name, nlen)) { - return APR_SUCCESS; - } - else if (H2_HD_MATCH_LIT("cookie", name, nlen)) { -- const char *existing = apr_table_get(headers, "cookie"); -+ existing = apr_table_get(headers, "cookie"); - if (existing) { - char *nval; - - /* Cookie header come separately in HTTP/2, but need - * to be merged by "; " (instead of default ", ") - */ -+ if (max_field_len && strlen(existing) + vlen + nlen + 4 > max_field_len) { -+ /* "key: oldval, nval" is too long */ -+ return APR_EINVAL; -+ } - hvalue = apr_pstrndup(pool, value, vlen); - nval = apr_psprintf(pool, "%s; %s", existing, hvalue); - apr_table_setn(headers, "Cookie", nval); -@@ -1869,8 +1839,16 @@ - } - - hname = apr_pstrndup(pool, name, nlen); -- hvalue = apr_pstrndup(pool, value, vlen); - h2_util_camel_case_header(hname, nlen); -+ existing = apr_table_get(headers, hname); -+ if (max_field_len) { -+ if ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len) { -+ /* "key: (oldval, )?nval" is too long */ -+ return APR_EINVAL; -+ } -+ } -+ if (!existing) *pwas_added = 1; -+ hvalue = apr_pstrndup(pool, value, vlen); - apr_table_mergen(headers, hname, hvalue); - - return APR_SUCCESS; -@@ -1960,7 +1938,8 @@ - case NGHTTP2_GOAWAY: { - size_t len = (frame->goaway.opaque_data_len < s_len)? - frame->goaway.opaque_data_len : s_len-1; -- memcpy(scratch, frame->goaway.opaque_data, len); -+ if (len) -+ memcpy(scratch, frame->goaway.opaque_data, len); - scratch[len] = '\0'; - return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', " - "last_stream=%d]", frame->goaway.error_code, ---- a/modules/http2/h2_util.h -+++ b/modules/http2/h2_util.h -@@ -209,7 +209,6 @@ - apr_status_t h2_fifo_set_create(h2_fifo **pfifo, apr_pool_t *pool, int capacity); - - apr_status_t h2_fifo_term(h2_fifo *fifo); --apr_status_t h2_fifo_interrupt(h2_fifo *fifo); - - int h2_fifo_count(h2_fifo *fifo); - -@@ -229,7 +228,7 @@ - - typedef enum { - H2_FIFO_OP_PULL, /* pull the element from the queue, ie discard it */ -- H2_FIFO_OP_REPUSH, /* pull and immediatley re-push it */ -+ H2_FIFO_OP_REPUSH, /* pull and immediately re-push it */ - } h2_fifo_op_t; - - typedef h2_fifo_op_t h2_fifo_peek_fn(void *head, void *ctx); -@@ -280,7 +279,6 @@ - apr_status_t h2_ififo_set_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity); - - apr_status_t h2_ififo_term(h2_ififo *fifo); --apr_status_t h2_ififo_interrupt(h2_ififo *fifo); - - int h2_ififo_count(h2_ififo *fifo); - -@@ -412,9 +410,14 @@ - apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, - const struct h2_request *req); - -+/** -+ * Add a HTTP/2 header and return the table key if it really was added -+ * and not ignored. -+ */ - apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool, - const char *name, size_t nlen, -- const char *value, size_t vlen); -+ const char *value, size_t vlen, -+ size_t max_field_len, int *pwas_added); - - /******************************************************************************* - * h2_request helpers ---- a/modules/http2/h2_version.h -+++ b/modules/http2/h2_version.h -@@ -27,7 +27,7 @@ - * @macro - * Version number of the http2 module as c string - */ --#define MOD_HTTP2_VERSION "1.11.4" -+#define MOD_HTTP2_VERSION "1.15.14" - - /** - * @macro -@@ -35,7 +35,6 @@ - * release. This is a 24 bit number with 8 bits for major number, 8 bits - * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. - */ --#define MOD_HTTP2_VERSION_NUM 0x010b04 -- -+#define MOD_HTTP2_VERSION_NUM 0x010f0e - - #endif /* mod_h2_h2_version_h */ ---- a/modules/http2/h2_workers.c -+++ b/modules/http2/h2_workers.c -@@ -155,7 +155,7 @@ - { - apr_status_t rv; - -- rv = h2_mplx_pop_task(m, &slot->task); -+ rv = h2_mplx_s_pop_task(m, &slot->task); - if (slot->task) { - /* Ok, we got something to give back to the worker for execution. - * If we still have idle workers, we let the worker be sticky, -@@ -234,10 +234,10 @@ - * mplx the opportunity to give us back a new task right away. - */ - if (!slot->aborted && (--slot->sticks > 0)) { -- h2_mplx_task_done(slot->task->mplx, slot->task, &slot->task); -+ h2_mplx_s_task_done(slot->task->mplx, slot->task, &slot->task); - } - else { -- h2_mplx_task_done(slot->task->mplx, slot->task, NULL); -+ h2_mplx_s_task_done(slot->task->mplx, slot->task, NULL); - slot->task = NULL; - } - } -@@ -269,7 +269,6 @@ - } - - h2_fifo_term(workers->mplxs); -- h2_fifo_interrupt(workers->mplxs); - - cleanup_zombies(workers); - } ---- a/modules/http2/mod_http2.c -+++ b/modules/http2/mod_http2.c -@@ -172,27 +172,6 @@ - conn_rec *, request_rec *, char *name); - static int http2_is_h2(conn_rec *); - --static apr_status_t http2_req_engine_push(const char *ngn_type, -- request_rec *r, -- http2_req_engine_init *einit) --{ -- return h2_mplx_req_engine_push(ngn_type, r, einit); --} -- --static apr_status_t http2_req_engine_pull(h2_req_engine *ngn, -- apr_read_type_e block, -- int capacity, -- request_rec **pr) --{ -- return h2_mplx_req_engine_pull(ngn, block, capacity, pr); --} -- --static void http2_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn, -- apr_status_t status) --{ -- h2_mplx_req_engine_done(ngn, r_conn, status); --} -- - static void http2_get_num_workers(server_rec *s, int *minw, int *maxw) - { - h2_get_num_workers(s, minw, maxw); -@@ -220,9 +199,6 @@ - - APR_REGISTER_OPTIONAL_FN(http2_is_h2); - APR_REGISTER_OPTIONAL_FN(http2_var_lookup); -- APR_REGISTER_OPTIONAL_FN(http2_req_engine_push); -- APR_REGISTER_OPTIONAL_FN(http2_req_engine_pull); -- APR_REGISTER_OPTIONAL_FN(http2_req_engine_done); - APR_REGISTER_OPTIONAL_FN(http2_get_num_workers); - - ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks"); -@@ -260,9 +236,8 @@ - { - if (ctx) { - if (r) { -- h2_task *task = h2_ctx_get_task(ctx); -- if (task) { -- h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id); -+ if (ctx->task) { -+ h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task); - if (stream && stream->push_policy != H2_PUSH_NONE) { - return "on"; - } -@@ -273,8 +248,7 @@ - } - } - else if (s) { -- const h2_config *cfg = h2_config_sget(s); -- if (cfg && h2_config_geti(cfg, H2_CONF_PUSH)) { -+ if (h2_config_geti(r, s, H2_CONF_PUSH)) { - return "on"; - } - } -@@ -285,8 +259,7 @@ - conn_rec *c, request_rec *r, h2_ctx *ctx) - { - if (ctx) { -- h2_task *task = h2_ctx_get_task(ctx); -- if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) { -+ if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) { - return "PUSHED"; - } - } -@@ -297,9 +270,8 @@ - conn_rec *c, request_rec *r, h2_ctx *ctx) - { - if (ctx) { -- h2_task *task = h2_ctx_get_task(ctx); -- if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) { -- h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id); -+ if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) { -+ h2_stream *stream = h2_mplx_t_stream_get(ctx->task->mplx, ctx->task); - if (stream) { - return apr_itoa(p, stream->initiated_on); - } -@@ -312,9 +284,8 @@ - conn_rec *c, request_rec *r, h2_ctx *ctx) - { - if (ctx) { -- h2_task *task = h2_ctx_get_task(ctx); -- if (task) { -- return task->id; -+ if (ctx->task) { -+ return ctx->task->id; - } - } - return ""; -@@ -366,7 +337,7 @@ - for (i = 0; i < H2_ALEN(H2_VARS); ++i) { - h2_var_def *vdef = &H2_VARS[i]; - if (!strcmp(vdef->name, name)) { -- h2_ctx *ctx = (r? h2_ctx_rget(r) : -+ h2_ctx *ctx = (r? h2_ctx_get(c, 0) : - h2_ctx_get(c->master? c->master : c, 0)); - return (char *)vdef->lookup(p, s, c, r, ctx); - } -@@ -377,7 +348,7 @@ - static int h2_h2_fixups(request_rec *r) - { - if (r->connection->master) { -- h2_ctx *ctx = h2_ctx_rget(r); -+ h2_ctx *ctx = h2_ctx_get(r->connection, 0); - int i; - - for (i = 0; ctx && i < H2_ALEN(H2_VARS); ++i) { ---- a/modules/http2/mod_http2.dep -+++ b/modules/http2/mod_http2.dep -@@ -694,7 +694,6 @@ - ".\h2_ctx.h"\ - ".\h2_h2.h"\ - ".\h2_mplx.h"\ -- ".\h2_ngn_shed.h"\ - ".\h2_private.h"\ - ".\h2_request.h"\ - ".\h2_stream.h"\ -@@ -754,7 +753,6 @@ - ".\h2_ctx.h"\ - ".\h2_h2.h"\ - ".\h2_mplx.h"\ -- ".\h2_ngn_shed.h"\ - ".\h2_private.h"\ - ".\h2_request.h"\ - ".\h2_task.h"\ ---- a/modules/http2/mod_http2.dsp -+++ b/modules/http2/mod_http2.dsp -@@ -145,10 +145,6 @@ - # End Source File - # Begin Source File - --SOURCE=./h2_ngn_shed.c --# End Source File --# Begin Source File -- - SOURCE=./h2_push.c - # End Source File - # Begin Source File ---- a/modules/http2/mod_http2.h -+++ b/modules/http2/mod_http2.h -@@ -30,22 +30,20 @@ - - - /******************************************************************************* -- * HTTP/2 request engines -+ * START HTTP/2 request engines (DEPRECATED) - ******************************************************************************/ -+ -+/* The following functions were introduced for the experimental mod_proxy_http2 -+ * support, but have been abandoned since. -+ * They are still declared here for backward compatibility, in case someone -+ * tries to build an old mod_proxy_http2 against it, but will disappear -+ * completely sometime in the future. -+ */ - - struct apr_thread_cond_t; -- - typedef struct h2_req_engine h2_req_engine; -- - typedef void http2_output_consumed(void *ctx, conn_rec *c, apr_off_t consumed); - --/** -- * Initialize a h2_req_engine. The structure will be passed in but -- * only the name and master are set. The function should initialize -- * all fields. -- * @param engine the allocated, partially filled structure -- * @param r the first request to process, or NULL -- */ - typedef apr_status_t http2_req_engine_init(h2_req_engine *engine, - const char *id, - const char *type, -@@ -55,35 +53,11 @@ - http2_output_consumed **pconsumed, - void **pbaton); - --/** -- * Push a request to an engine with the specified name for further processing. -- * If no such engine is available, einit is not NULL, einit is called -- * with a new engine record and the caller is responsible for running the -- * new engine instance. -- * @param engine_type the type of the engine to add the request to -- * @param r the request to push to an engine for processing -- * @param einit an optional initialization callback for a new engine -- * of the requested type, should no instance be available. -- * By passing a non-NULL callback, the caller is willing -- * to init and run a new engine itself. -- * @return APR_SUCCESS iff slave was successfully added to an engine -- */ - APR_DECLARE_OPTIONAL_FN(apr_status_t, - http2_req_engine_push, (const char *engine_type, - request_rec *r, - http2_req_engine_init *einit)); - --/** -- * Get a new request for processing in this engine. -- * @param engine the engine which is done processing the slave -- * @param block if call should block waiting for request to come -- * @param capacity how many parallel requests are acceptable -- * @param pr the request that needs processing or NULL -- * @return APR_SUCCESS if new request was assigned -- * APR_EAGAIN if no new request is available -- * APR_EOF if engine may shut down, as no more request will be scheduled -- * APR_ECONNABORTED if the engine needs to shut down immediately -- */ - APR_DECLARE_OPTIONAL_FN(apr_status_t, - http2_req_engine_pull, (h2_req_engine *engine, - apr_read_type_e block, -@@ -98,4 +72,8 @@ - http2_get_num_workers, (server_rec *s, - int *minw, int *max)); - -+/******************************************************************************* -+ * END HTTP/2 request engines (DEPRECATED) -+ ******************************************************************************/ -+ - #endif ---- a/modules/http2/mod_http2.mak -+++ b/modules/http2/mod_http2.mak -@@ -61,7 +61,6 @@ - -@erase "$(INTDIR)\h2_h2.obj" - -@erase "$(INTDIR)\h2_headers.obj" - -@erase "$(INTDIR)\h2_mplx.obj" -- -@erase "$(INTDIR)\h2_ngn_shed.obj" - -@erase "$(INTDIR)\h2_push.obj" - -@erase "$(INTDIR)\h2_request.obj" - -@erase "$(INTDIR)\h2_session.obj" -@@ -138,7 +137,6 @@ - "$(INTDIR)\h2_h2.obj" \ - "$(INTDIR)\h2_headers.obj" \ - "$(INTDIR)\h2_mplx.obj" \ -- "$(INTDIR)\h2_ngn_shed.obj" \ - "$(INTDIR)\h2_push.obj" \ - "$(INTDIR)\h2_request.obj" \ - "$(INTDIR)\h2_session.obj" \ -@@ -207,7 +205,6 @@ - -@erase "$(INTDIR)\h2_h2.obj" - -@erase "$(INTDIR)\h2_headers.obj" - -@erase "$(INTDIR)\h2_mplx.obj" -- -@erase "$(INTDIR)\h2_ngn_shed.obj" - -@erase "$(INTDIR)\h2_push.obj" - -@erase "$(INTDIR)\h2_request.obj" - -@erase "$(INTDIR)\h2_session.obj" -@@ -284,7 +281,6 @@ - "$(INTDIR)\h2_h2.obj" \ - "$(INTDIR)\h2_headers.obj" \ - "$(INTDIR)\h2_mplx.obj" \ -- "$(INTDIR)\h2_ngn_shed.obj" \ - "$(INTDIR)\h2_push.obj" \ - "$(INTDIR)\h2_request.obj" \ - "$(INTDIR)\h2_session.obj" \ -@@ -469,11 +465,6 @@ - "$(INTDIR)\h2_mplx.obj" : $(SOURCE) "$(INTDIR)" - - --SOURCE=./h2_ngn_shed.c -- --"$(INTDIR)\h2_ngn_shed.obj" : $(SOURCE) "$(INTDIR)" -- -- - SOURCE=./h2_push.c - - "$(INTDIR)\h2_push.obj" : $(SOURCE) "$(INTDIR)" ---- a/modules/http2/mod_proxy_http2.c -+++ b/modules/http2/mod_proxy_http2.c -@@ -16,13 +16,14 @@ - - #include <nghttp2/nghttp2.h> - -+#include <ap_mmn.h> - #include <httpd.h> - #include <mod_proxy.h> - #include "mod_http2.h" - - - #include "mod_proxy_http2.h" --#include "h2_request.h" -+#include "h2.h" - #include "h2_proxy_util.h" - #include "h2_version.h" - #include "h2_proxy_session.h" -@@ -46,19 +47,12 @@ - - /* Optional functions from mod_http2 */ - static int (*is_h2)(conn_rec *c); --static apr_status_t (*req_engine_push)(const char *name, request_rec *r, -- http2_req_engine_init *einit); --static apr_status_t (*req_engine_pull)(h2_req_engine *engine, -- apr_read_type_e block, -- int capacity, -- request_rec **pr); --static void (*req_engine_done)(h2_req_engine *engine, conn_rec *r_conn, -- apr_status_t status); -- -+ - typedef struct h2_proxy_ctx { -+ const char *id; -+ conn_rec *master; - conn_rec *owner; - apr_pool_t *pool; -- request_rec *rbase; - server_rec *server; - const char *proxy_func; - char server_portstr[32]; -@@ -66,19 +60,15 @@ - proxy_worker *worker; - proxy_server_conf *conf; - -- h2_req_engine *engine; -- const char *engine_id; -- const char *engine_type; -- apr_pool_t *engine_pool; - apr_size_t req_buffer_size; -- h2_proxy_fifo *requests; - int capacity; - -- unsigned standalone : 1; - unsigned is_ssl : 1; -- unsigned flushall : 1; - -- apr_status_t r_status; /* status of our first request work */ -+ request_rec *r; /* the request processed in this ctx */ -+ apr_status_t r_status; /* status of request work */ -+ int r_done; /* request was processed, not necessarily successfully */ -+ int r_may_retry; /* request may be retried */ - h2_proxy_session *session; /* current http2 session against backend */ - } h2_proxy_ctx; - -@@ -104,16 +94,6 @@ - MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown"); - - is_h2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2); -- req_engine_push = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_push); -- req_engine_pull = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_pull); -- req_engine_done = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_done); -- -- /* we need all of them */ -- if (!req_engine_push || !req_engine_pull || !req_engine_done) { -- req_engine_push = NULL; -- req_engine_pull = NULL; -- req_engine_done = NULL; -- } - - return status; - } -@@ -204,45 +184,6 @@ - return OK; - } - --static void out_consumed(void *baton, conn_rec *c, apr_off_t bytes) --{ -- h2_proxy_ctx *ctx = baton; -- -- if (ctx->session) { -- h2_proxy_session_update_window(ctx->session, c, bytes); -- } --} -- --static apr_status_t proxy_engine_init(h2_req_engine *engine, -- const char *id, -- const char *type, -- apr_pool_t *pool, -- apr_size_t req_buffer_size, -- request_rec *r, -- http2_output_consumed **pconsumed, -- void **pctx) --{ -- h2_proxy_ctx *ctx = ap_get_module_config(r->connection->conn_config, -- &proxy_http2_module); -- if (!ctx) { -- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03368) -- "h2_proxy_session, engine init, no ctx found"); -- return APR_ENOTIMPL; -- } -- -- ctx->pool = pool; -- ctx->engine = engine; -- ctx->engine_id = id; -- ctx->engine_type = type; -- ctx->engine_pool = pool; -- ctx->req_buffer_size = req_buffer_size; -- ctx->capacity = H2MIN(100, h2_proxy_fifo_capacity(ctx->requests)); -- -- *pconsumed = out_consumed; -- *pctx = ctx; -- return APR_SUCCESS; --} -- - static apr_status_t add_request(h2_proxy_session *session, request_rec *r) - { - h2_proxy_ctx *ctx = session->user_data; -@@ -252,7 +193,7 @@ - url = apr_table_get(r->notes, H2_PROXY_REQ_URL_NOTE); - apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu", - ctx->p_conn->connection->local_addr->port)); -- status = h2_proxy_session_submit(session, url, r, ctx->standalone); -+ status = h2_proxy_session_submit(session, url, r, 1); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, r->connection, APLOGNO(03351) - "pass request body failed to %pI (%s) from %s (%s)", -@@ -266,43 +207,15 @@ - static void request_done(h2_proxy_ctx *ctx, request_rec *r, - apr_status_t status, int touched) - { -- const char *task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE); -- -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, -- "h2_proxy_session(%s): request done %s, touched=%d", -- ctx->engine_id, task_id, touched); -- if (status != APR_SUCCESS) { -- if (!touched) { -- /* untouched request, need rescheduling */ -- status = h2_proxy_fifo_push(ctx->requests, r); -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, -- APLOGNO(03369) -- "h2_proxy_session(%s): rescheduled request %s", -- ctx->engine_id, task_id); -- return; -- } -- else { -- const char *uri; -- uri = apr_uri_unparse(r->pool, &r->parsed_uri, 0); -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, -- APLOGNO(03471) "h2_proxy_session(%s): request %s -> %s " -- "not complete, cannot repeat", -- ctx->engine_id, task_id, uri); -- } -- } -- -- if (r == ctx->rbase) { -+ if (r == ctx->r) { -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, -+ "h2_proxy_session(%s): request done, touched=%d", -+ ctx->id, touched); -+ ctx->r_done = 1; -+ if (touched) ctx->r_may_retry = 0; - ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS - : HTTP_SERVICE_UNAVAILABLE); - } -- -- if (req_engine_done && ctx->engine) { -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, -- APLOGNO(03370) -- "h2_proxy_session(%s): finished request %s", -- ctx->engine_id, task_id); -- req_engine_done(ctx->engine, r->connection, status); -- } - } - - static void session_req_done(h2_proxy_session *session, request_rec *r, -@@ -311,43 +224,15 @@ - request_done(session->user_data, r, status, touched); - } - --static apr_status_t next_request(h2_proxy_ctx *ctx, int before_leave) --{ -- if (h2_proxy_fifo_count(ctx->requests) > 0) { -- return APR_SUCCESS; -- } -- else if (req_engine_pull && ctx->engine) { -- apr_status_t status; -- request_rec *r = NULL; -- -- status = req_engine_pull(ctx->engine, before_leave? -- APR_BLOCK_READ: APR_NONBLOCK_READ, -- ctx->capacity, &r); -- if (status == APR_SUCCESS && r) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, ctx->owner, -- "h2_proxy_engine(%s): pulled request (%s) %s", -- ctx->engine_id, -- before_leave? "before leave" : "regular", -- r->the_request); -- h2_proxy_fifo_push(ctx->requests, r); -- } -- return APR_STATUS_IS_EAGAIN(status)? APR_SUCCESS : status; -- } -- return APR_EOF; --} -- --static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx) { -+static apr_status_t ctx_run(h2_proxy_ctx *ctx) { - apr_status_t status = OK; - int h2_front; -- request_rec *r; - - /* Step Four: Send the Request in a new HTTP/2 stream and - * loop until we got the response or encounter errors. - */ -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, -- "eng(%s): setup session", ctx->engine_id); - h2_front = is_h2? is_h2(ctx->owner) : 0; -- ctx->session = h2_proxy_session_setup(ctx->engine_id, ctx->p_conn, ctx->conf, -+ ctx->session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf, - h2_front, 30, - h2_proxy_log2((int)ctx->req_buffer_size), - session_req_done); -@@ -358,105 +243,45 @@ - } - - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373) -- "eng(%s): run session %s", ctx->engine_id, ctx->session->id); -+ "eng(%s): run session %s", ctx->id, ctx->session->id); - ctx->session->user_data = ctx; - -- while (!ctx->owner->aborted) { -- if (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) { -- add_request(ctx->session, r); -- } -- -+ ctx->r_done = 0; -+ add_request(ctx->session, ctx->r); -+ -+ while (!ctx->master->aborted && !ctx->r_done) { -+ - status = h2_proxy_session_process(ctx->session); -- -- if (status == APR_SUCCESS) { -- apr_status_t s2; -- /* ongoing processing, call again */ -- if (ctx->session->remote_max_concurrent > 0 -- && ctx->session->remote_max_concurrent != ctx->capacity) { -- ctx->capacity = H2MIN((int)ctx->session->remote_max_concurrent, -- h2_proxy_fifo_capacity(ctx->requests)); -- } -- s2 = next_request(ctx, 0); -- if (s2 == APR_ECONNABORTED) { -- /* master connection gone */ -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, s2, ctx->owner, -- APLOGNO(03374) "eng(%s): pull request", -- ctx->engine_id); -- /* give notice that we're leaving and cancel all ongoing -- * streams. */ -- next_request(ctx, 1); -- h2_proxy_session_cancel_all(ctx->session); -- h2_proxy_session_process(ctx->session); -- status = ctx->r_status = APR_SUCCESS; -- break; -- } -- if ((h2_proxy_fifo_count(ctx->requests) == 0) -- && h2_proxy_ihash_empty(ctx->session->streams)) { -- break; -- } -- } -- else { -- /* end of processing, maybe error */ -+ if (status != APR_SUCCESS) { -+ /* Encountered an error during session processing */ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, - APLOGNO(03375) "eng(%s): end of session %s", -- ctx->engine_id, ctx->session->id); -- /* -- * Any open stream of that session needs to -+ ctx->id, ctx->session->id); -+ /* Any open stream of that session needs to - * a) be reopened on the new session iff safe to do so - * b) reported as done (failed) otherwise - */ - h2_proxy_session_cleanup(ctx->session, session_req_done); -- break; -+ goto out; - } - } - -- ctx->session->user_data = NULL; -- ctx->session = NULL; -- -- return status; --} -- --static apr_status_t push_request_somewhere(h2_proxy_ctx *ctx, request_rec *r) --{ -- conn_rec *c = ctx->owner; -- const char *engine_type, *hostname; -- -- hostname = (ctx->p_conn->ssl_hostname? -- ctx->p_conn->ssl_hostname : ctx->p_conn->hostname); -- engine_type = apr_psprintf(ctx->pool, "proxy_http2 %s%s", hostname, -- ctx->server_portstr); -- -- if (c->master && req_engine_push && r && is_h2 && is_h2(c)) { -- /* If we are have req_engine capabilities, push the handling of this -- * request (e.g. slave connection) to a proxy_http2 engine which -- * uses the same backend. We may be called to create an engine -- * ourself. */ -- if (req_engine_push(engine_type, r, proxy_engine_init) == APR_SUCCESS) { -- if (ctx->engine == NULL) { -- /* request has been assigned to an engine in another thread */ -- return SUSPENDED; -- } -+out: -+ if (ctx->master->aborted) { -+ /* master connection gone */ -+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, -+ APLOGNO(03374) "eng(%s): master connection gone", ctx->id); -+ /* cancel all ongoing requests */ -+ h2_proxy_session_cancel_all(ctx->session); -+ h2_proxy_session_process(ctx->session); -+ if (!ctx->master->aborted) { -+ status = ctx->r_status = APR_SUCCESS; - } - } - -- if (!ctx->engine) { -- /* No engine was available or has been initialized, handle this -- * request just by ourself. */ -- ctx->engine_id = apr_psprintf(ctx->pool, "eng-proxy-%ld", c->id); -- ctx->engine_type = engine_type; -- ctx->engine_pool = ctx->pool; -- ctx->req_buffer_size = (32*1024); -- ctx->standalone = 1; -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "h2_proxy_http2(%ld): setup standalone engine for type %s", -- c->id, engine_type); -- } -- else { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, -- "H2: hosting engine %s", ctx->engine_id); -- } -- -- return h2_proxy_fifo_push(ctx->requests, r); -+ ctx->session->user_data = NULL; -+ ctx->session = NULL; -+ return status; - } - - static int proxy_http2_handler(request_rec *r, -@@ -466,7 +291,7 @@ - const char *proxyname, - apr_port_t proxyport) - { -- const char *proxy_func; -+ const char *proxy_func, *task_id; - char *locurl = url, *u; - apr_size_t slen; - int is_ssl = 0; -@@ -498,29 +323,35 @@ - default: - return DECLINED; - } -+ -+ task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE); - - ctx = apr_pcalloc(r->pool, sizeof(*ctx)); -- ctx->owner = r->connection; -- ctx->pool = r->pool; -- ctx->rbase = r; -- ctx->server = r->server; -+ ctx->master = r->connection->master? r->connection->master : r->connection; -+ ctx->id = task_id? task_id : apr_psprintf(r->pool, "%ld", (long)ctx->master->id); -+ ctx->owner = r->connection; -+ ctx->pool = r->pool; -+ ctx->server = r->server; - ctx->proxy_func = proxy_func; -- ctx->is_ssl = is_ssl; -- ctx->worker = worker; -- ctx->conf = conf; -- ctx->flushall = apr_table_get(r->subprocess_env, "proxy-flushall")? 1 : 0; -- ctx->r_status = HTTP_SERVICE_UNAVAILABLE; -- -- h2_proxy_fifo_set_create(&ctx->requests, ctx->pool, 100); -+ ctx->is_ssl = is_ssl; -+ ctx->worker = worker; -+ ctx->conf = conf; -+ ctx->req_buffer_size = (32*1024); -+ ctx->r = r; -+ ctx->r_status = status = HTTP_SERVICE_UNAVAILABLE; -+ ctx->r_done = 0; -+ ctx->r_may_retry = 1; - - ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx); - - /* scheme says, this is for us. */ -- apr_table_setn(ctx->rbase->notes, H2_PROXY_REQ_URL_NOTE, url); -- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->rbase, -+ apr_table_setn(ctx->r->notes, H2_PROXY_REQ_URL_NOTE, url); -+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->r, - "H2: serving URL %s", url); - - run_connect: -+ if (ctx->master->aborted) goto cleanup; -+ - /* Get a proxy_conn_rec from the worker, might be a new one, might - * be one still open from another request, or it might fail if the - * worker is stopped or in error. */ -@@ -530,25 +361,11 @@ - } - - ctx->p_conn->is_ssl = ctx->is_ssl; -- if (ctx->is_ssl && ctx->p_conn->connection) { -- /* If there are some metadata on the connection (e.g. TLS alert), -- * let mod_ssl detect them, and create a new connection below. -- */ -- apr_bucket_brigade *tmp_bb; -- tmp_bb = apr_brigade_create(ctx->rbase->pool, -- ctx->rbase->connection->bucket_alloc); -- status = ap_get_brigade(ctx->p_conn->connection->input_filters, tmp_bb, -- AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 1); -- if (status != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(status)) { -- ctx->p_conn->close = 1; -- } -- apr_brigade_cleanup(tmp_bb); -- } - - /* Step One: Determine the URL to connect to (might be a proxy), - * initialize the backend accordingly and determine the server - * port string we can expect in responses. */ -- if ((status = ap_proxy_determine_connection(ctx->pool, ctx->rbase, conf, worker, -+ if ((status = ap_proxy_determine_connection(ctx->pool, ctx->r, conf, worker, - ctx->p_conn, &uri, &locurl, - proxyname, proxyport, - ctx->server_portstr, -@@ -556,17 +373,6 @@ - goto cleanup; - } - -- /* If we are not already hosting an engine, try to push the request -- * to an already existing engine or host a new engine here. */ -- if (r && !ctx->engine) { -- ctx->r_status = push_request_somewhere(ctx, r); -- r = NULL; -- if (ctx->r_status == SUSPENDED) { -- /* request was pushed to another thread, leave processing here */ -- goto cleanup; -- } -- } -- - /* Step Two: Make the Connection (or check that an already existing - * socket is still usable). On success, we have a socket connected to - * backend->hostname. */ -@@ -575,70 +381,56 @@ - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352) - "H2: failed to make connection to backend: %s", - ctx->p_conn->hostname); -- goto reconnect; -+ goto cleanup; - } - - /* Step Three: Create conn_rec for the socket we have open now. */ -- if (!ctx->p_conn->connection) { -- status = ap_proxy_connection_create_ex(ctx->proxy_func, -- ctx->p_conn, ctx->rbase); -- if (status != OK) { -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353) -- "setup new connection: is_ssl=%d %s %s %s", -- ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, -- locurl, ctx->p_conn->hostname); -- goto reconnect; -- } -- -- if (!ctx->p_conn->data) { -- /* New conection: set a note on the connection what CN is -- * requested and what protocol we want */ -- if (ctx->p_conn->ssl_hostname) { -- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, ctx->owner, -- "set SNI to %s for (%s)", -- ctx->p_conn->ssl_hostname, -- ctx->p_conn->hostname); -- apr_table_setn(ctx->p_conn->connection->notes, -- "proxy-request-hostname", ctx->p_conn->ssl_hostname); -- } -- if (ctx->is_ssl) { -- apr_table_setn(ctx->p_conn->connection->notes, -- "proxy-request-alpn-protos", "h2"); -- } -- } -+ status = ap_proxy_connection_create_ex(ctx->proxy_func, ctx->p_conn, ctx->r); -+ if (status != OK) { -+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353) -+ "setup new connection: is_ssl=%d %s %s %s", -+ ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, -+ locurl, ctx->p_conn->hostname); -+ ctx->r_status = status; -+ goto cleanup; - } -- --run_session: -- status = proxy_engine_run(ctx); -- if (status == APR_SUCCESS) { -- /* session and connection still ok */ -- if (next_request(ctx, 1) == APR_SUCCESS) { -- /* more requests, run again */ -- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03376) -- "run_session, again"); -- goto run_session; -+ -+ if (!ctx->p_conn->data && ctx->is_ssl) { -+ /* New SSL connection: set a note on the connection about what -+ * protocol we want. -+ */ -+ apr_table_setn(ctx->p_conn->connection->notes, -+ "proxy-request-alpn-protos", "h2"); -+ if (ctx->p_conn->ssl_hostname) { -+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, -+ "set SNI to %s for (%s)", -+ ctx->p_conn->ssl_hostname, -+ ctx->p_conn->hostname); -+ apr_table_setn(ctx->p_conn->connection->notes, -+ "proxy-request-hostname", ctx->p_conn->ssl_hostname); - } -- /* done */ -- ctx->engine = NULL; - } - --reconnect: -- if (next_request(ctx, 1) == APR_SUCCESS) { -- /* Still more to do, tear down old conn and start over */ -+ if (ctx->master->aborted) goto cleanup; -+ status = ctx_run(ctx); -+ -+ if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->master->aborted) { -+ /* Not successfully processed, but may retry, tear down old conn and start over */ - if (ctx->p_conn) { - ctx->p_conn->close = 1; -- /*only in trunk so far */ -- /*proxy_run_detach_backend(r, ctx->p_conn);*/ -+#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2) -+ proxy_run_detach_backend(r, ctx->p_conn); -+#endif - ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server); - ctx->p_conn = NULL; - } - ++reconnects; -- if (reconnects < 5 && !ctx->owner->aborted) { -+ if (reconnects < 5) { - goto run_connect; - } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023) -- "giving up after %d reconnects, %d requests todo", -- reconnects, h2_proxy_fifo_count(ctx->requests)); -+ "giving up after %d reconnects, request-done=%d", -+ reconnects, ctx->r_done); - } - - cleanup: -@@ -647,17 +439,13 @@ - /* close socket when errors happened or session shut down (EOF) */ - ctx->p_conn->close = 1; - } -- /*only in trunk so far */ -- /*proxy_run_detach_backend(ctx->rbase, ctx->p_conn);*/ -+#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2) -+ proxy_run_detach_backend(ctx->r, ctx->p_conn); -+#endif - ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server); - ctx->p_conn = NULL; - } - -- /* Any requests will still have need to fail */ -- while (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) { -- request_done(ctx, r, HTTP_SERVICE_UNAVAILABLE, 1); -- } -- - ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, - APLOGNO(03377) "leaving handler"); diff --git a/debian/patches/reproducible_builds.diff b/debian/patches/reproducible_builds.diff index 36f71e2..8f48922 100644 --- a/debian/patches/reproducible_builds.diff +++ b/debian/patches/reproducible_builds.diff @@ -18,7 +18,7 @@ Last-Update: 2015-08-11 -#endif +static const char server_built[] = BUILD_DATETIME; - AP_DECLARE(const char *) ap_get_server_built() + AP_DECLARE(const char *) ap_get_server_built(void) { --- a/server/Makefile.in +++ b/server/Makefile.in diff --git a/debian/patches/series b/debian/patches/series index 839511c..49ae838 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -4,60 +4,7 @@ suexec-CVE-2007-1742.patch customize_apxs.patch build_suexec-custom.patch reproducible_builds.diff +fix-macro.patch # This patch is applied manually #suexec-custom.patch -spelling-errors.patch - -CVE-2019-0196.patch -CVE-2019-0211.patch -CVE-2019-0215.patch -CVE-2019-0217.patch -CVE-2019-0220-1.patch -CVE-2019-0220-2.patch -CVE-2019-0220-3.patch -CVE-2019-0197.patch -CVE-2019-10092.patch -CVE-2019-10097.patch -CVE-2019-10098.patch -import-http2-module-from-2.4.46.patch -CVE-2020-11984.patch -CVE-2020-1927.patch -CVE-2020-1934.patch -CVE-2021-31618.patch -CVE-2021-30641.patch -CVE-2021-26691.patch -CVE-2021-26690.patch -CVE-2020-35452.patch -CVE-2021-34798.patch -CVE-2021-36160.patch -CVE-2021-39275.patch -CVE-2021-40438.patch -CVE-2021-44224-1.patch -CVE-2021-44224-2.patch -CVE-2021-44790.patch -CVE-2021-36160-2.patch -CVE-2022-22719.patch -CVE-2022-22720.patch -CVE-2022-22721.patch -CVE-2022-23943-1.patch -CVE-2022-23943-2.patch -CVE-2022-26377.patch -CVE-2022-28614.patch -CVE-2022-28615.patch -CVE-2022-29404.patch -CVE-2022-30522.patch -CVE-2022-30556.patch -CVE-2022-31813.patch -CVE-2006-20001.patch -CVE-2022-36760.patch -CVE-2022-37436.patch -CVE-2021-33193.patch -0052-CVE-2023-27522-HTTP-Response-Smuggling-mod_proxy_uws.patch -0053-CVE-2023-25690-1.patch -0054-CVE-2023-25690-2.patch -0055-CVE-2023-25690-Regression-1.patch -0056-CVE-2023-25690-Regression-2.patch -0057-CVE-2023-25690-Regression-3.patch - - diff --git a/debian/patches/spelling-errors.patch b/debian/patches/spelling-errors.patch deleted file mode 100644 index d42ec00..0000000 --- a/debian/patches/spelling-errors.patch +++ /dev/null @@ -1,196 +0,0 @@ -Description: spelling errors -Author: Xavier Guimard <yadd@debian.org> -Forwarded: https://bz.apache.org/bugzilla/show_bug.cgi?id=62960 -Last-Update: 2018-11-28 - ---- a/LICENSE -+++ b/LICENSE -@@ -516,7 +516,7 @@ - This program may be used and copied freely providing this copyright notice - is not removed. - --This software is provided "as is" and any express or implied waranties, -+This software is provided "as is" and any express or implied warranties, - including but not limited to, the implied warranties of merchantability and - fitness for a particular purpose are disclaimed. In no event shall - Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, ---- a/docs/man/httxt2dbm.1 -+++ b/docs/man/httxt2dbm.1 -@@ -50,7 +50,7 @@ - Specify the DBM type to be used for the output\&. If not specified, will use the APR Default\&. Available types are: \fBGDBM\fR for GDBM files, \fBSDBM\fR for SDBM files, \fBDB\fR for berkeley DB files, \fBNDBM\fR for NDBM files, \fBdefault\fR for the default DBM type\&. - .TP - \fB-i \fISOURCE_TXT\fR\fR --Input file from which the dbm is to be created\&. The file should be formated with one record per line, of the form: \fBkey value\fR\&. See the documentation for RewriteMap for further details of this file's format and meaning\&. -+Input file from which the dbm is to be created\&. The file should be formatted with one record per line, of the form: \fBkey value\fR\&. See the documentation for RewriteMap for further details of this file's format and meaning\&. - .TP - \fB-o \fIOUTPUT_DBM\fR\fR - Name of the output dbm files\&. ---- a/docs/manual/howto/htaccess.html.ja.utf8 -+++ b/docs/manual/howto/htaccess.html.ja.utf8 -@@ -247,7 +247,7 @@ - - <p>As discussed in the documentation on <a href="../sections.html">Configuration Sections</a>, - <code>.htaccess</code> files can override the <code class="directive"><a href="../mod/core.html#directory"><Directory></a></code> sections for -- the corresponding directory, but will be overriden by other types -+ the corresponding directory, but will be overridden by other types - of configuration sections from the main configuration files. This - fact can be used to enforce certain configurations, even in the - presence of a liberal <code class="directive"><a href="../mod/core.html#allowoverride">AllowOverride</a></code> setting. For example, to -@@ -414,4 +414,4 @@ - prettyPrint(); - } - //--><!]]></script> --</body></html> -\ No newline at end of file -+</body></html> ---- a/docs/manual/mod/core.html.es -+++ b/docs/manual/mod/core.html.es -@@ -1211,7 +1211,7 @@ - error rather than masking it. More information is available in - Microsoft Knowledge Base article <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;Q294807">Q294807</a>.</p> - -- <p>Although most error messages can be overriden, there are certain -+ <p>Although most error messages can be overridden, there are certain - circumstances where the internal messages are used regardless of the - setting of <code class="directive"><a href="#errordocument">ErrorDocument</a></code>. In - particular, if a malformed request is detected, normal request processing -@@ -4524,4 +4524,4 @@ - prettyPrint(); - } - //--><!]]></script> --</body></html> -\ No newline at end of file -+</body></html> ---- a/docs/manual/programs/httxt2dbm.html.en -+++ b/docs/manual/programs/httxt2dbm.html.en -@@ -66,7 +66,7 @@ - </dd> - - <dt><code>-i <var>SOURCE_TXT</var></code></dt> -- <dd>Input file from which the dbm is to be created. The file should be formated -+ <dd>Input file from which the dbm is to be created. The file should be formatted - with one record per line, of the form: <code>key value</code>. - See the documentation for <code class="directive"><a href="../mod/mod_rewrite.html#rewritemap">RewriteMap</a></code> for - further details of this file's format and meaning. -@@ -111,4 +111,4 @@ - prettyPrint(); - } - //--><!]]></script> --</body></html> -\ No newline at end of file -+</body></html> ---- a/modules/http/http_request.c -+++ b/modules/http/http_request.c -@@ -376,7 +376,7 @@ - - /* The EOR bucket has either been handled by an output filter (eg. - * deleted or moved to a buffered_bb => no more in bb), or an error -- * occured before that (eg. c->aborted => still in bb) and we ought -+ * occurred before that (eg. c->aborted => still in bb) and we ought - * to destroy it now. So cleanup any remaining bucket along with - * the orphan request (if any). - */ -@@ -779,7 +779,7 @@ - - AP_INTERNAL_REDIRECT(r->uri, new_uri); - -- /* ap_die was already called, if an error occured */ -+ /* ap_die was already called, if an error occurred */ - if (!new) { - return; - } -@@ -803,7 +803,7 @@ - int access_status; - request_rec *new = internal_internal_redirect(new_uri, r); - -- /* ap_die was already called, if an error occured */ -+ /* ap_die was already called, if an error occurred */ - if (!new) { - return; - } ---- a/modules/http2/h2_config.c -+++ b/modules/http2/h2_config.c -@@ -419,7 +419,7 @@ - else if (!strcasecmp("BEFORE", sdependency)) { - dependency = H2_DEPENDANT_BEFORE; - if (sweight) { -- return "dependecy 'Before' does not allow a weight"; -+ return "dependency 'Before' does not allow a weight"; - } - } - else if (!strcasecmp("INTERLEAVED", sdependency)) { ---- a/modules/http2/h2_ngn_shed.c -+++ b/modules/http2/h2_ngn_shed.c -@@ -281,7 +281,7 @@ - if (H2_REQ_ENTRIES_EMPTY(&ngn->entries)) { - if (want_shutdown) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c, -- "h2_ngn_shed(%ld): emtpy queue, shutdown engine %s", -+ "h2_ngn_shed(%ld): empty queue, shutdown engine %s", - shed->c->id, ngn->id); - ngn->shutdown = 1; - } ---- a/modules/mappers/mod_imagemap.c -+++ b/modules/mappers/mod_imagemap.c -@@ -858,7 +858,7 @@ - /* There's not much else we can do ... we've already sent the headers - * to the client. - */ -- ap_rputs("\n\n[an internal server error occured]\n", r); -+ ap_rputs("\n\n[an internal server error occurred]\n", r); - menu_footer(r); - return OK; - } ---- a/modules/md/md_acme_authz.c -+++ b/modules/md/md_acme_authz.c -@@ -239,7 +239,7 @@ - - if (md_log_is_level(p, log_level)) { - md_log_perror(MD_LOG_MARK, log_level, rv, p, "ACME server authz: %s for %s at %s. " -- "Exact repsonse was: %s", err? err : "", authz->domain, authz->location, -+ "Exact response was: %s", err? err : "", authz->domain, authz->location, - json? md_json_writep(json, p, MD_JSON_FMT_COMPACT) : "not available"); - } - ---- a/modules/metadata/mod_remoteip.c -+++ b/modules/metadata/mod_remoteip.c -@@ -393,7 +393,7 @@ - - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, new, APLOGNO(03491) - "RemoteIPProxyProtocol: previous setting for %s:%hu from virtual " -- "host {%s:%hu in %s} is being overriden by virtual host " -+ "host {%s:%hu in %s} is being overridden by virtual host " - "{%s:%hu in %s}; new setting is '%s'", - buf, prev->addr->port, prev->source->server_hostname, - prev->source->addrs->host_port, prev->source->defn_name, ---- a/modules/ssl/mod_ssl.c -+++ b/modules/ssl/mod_ssl.c -@@ -94,7 +94,7 @@ - "Enable FIPS-140 mode " - "(`on', `off')") - SSL_CMD_ALL(CipherSuite, TAKE12, -- "Colon-delimited list of permitted SSL Ciphers, optional preceeded " -+ "Colon-delimited list of permitted SSL Ciphers, optional preceded " - "by protocol identifier ('XXX:...:XXX' - see manual)") - SSL_CMD_SRV(CertificateFile, TAKE1, - "SSL Server Certificate file " -@@ -187,7 +187,7 @@ - "('[+-][" SSL_PROTOCOLS "] ...' - see manual)") - SSL_CMD_PXY(ProxyCipherSuite, TAKE12, - "SSL Proxy: colon-delimited list of permitted SSL ciphers " -- ", optionally preceeded by protocol specifier ('XXX:...:XXX' - see manual)") -+ ", optionally preceded by protocol specifier ('XXX:...:XXX' - see manual)") - SSL_CMD_PXY(ProxyVerify, TAKE1, - "SSL Proxy: whether to verify the remote certificate " - "('on' or 'off')") ---- a/support/ab.c -+++ b/support/ab.c -@@ -18,7 +18,7 @@ - ** This program is based on ZeusBench V1.0 written by Adam Twiss - ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/ - ** -- ** This software is provided "as is" and any express or implied waranties, -+ ** This software is provided "as is" and any express or implied warranties, - ** including but not limited to, the implied warranties of merchantability and - ** fitness for a particular purpose are disclaimed. In no event shall - ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, diff --git a/debian/patches/suexec-CVE-2007-1742.patch b/debian/patches/suexec-CVE-2007-1742.patch index a348cbc..159c2c9 100644 --- a/debian/patches/suexec-CVE-2007-1742.patch +++ b/debian/patches/suexec-CVE-2007-1742.patch @@ -14,7 +14,7 @@ Bug: https://issues.apache.org/bugzilla/show_bug.cgi?id=44752 #include <stdio.h> #include <stdarg.h> -@@ -281,11 +282,12 @@ +@@ -279,11 +280,12 @@ char *actual_gname; /* actual group name */ char *cmd; /* command to be executed */ char cwd[AP_MAXPATH]; /* current working directory */ diff --git a/debian/patches/suexec-custom.patch b/debian/patches/suexec-custom.patch index 9c7a732..37b761d 100644 --- a/debian/patches/suexec-custom.patch +++ b/debian/patches/suexec-custom.patch @@ -2,8 +2,6 @@ Description: the actual patch to make suexec-custom read a config file Forwarded: not-needed Author: Stefan Fritsch <sf@debian.org> Last-Update: 2018-07-17 -diff --git a/support/suexec-custom.c b/support/suexec-custom.c -index f3811a97..30bb644b 100644 --- a/support/suexec-custom.c +++ b/support/suexec-custom.c @@ -29,6 +29,7 @@ @@ -22,7 +20,7 @@ index f3811a97..30bb644b 100644 #if APR_HAVE_UNISTD_H #include <unistd.h> #endif -@@ -222,6 +224,26 @@ static void log_no_err(const char *fmt,...) +@@ -222,6 +224,26 @@ return; } @@ -48,8 +46,8 @@ index f3811a97..30bb644b 100644 + static void clean_env(void) { - char pathbuf[512]; -@@ -288,6 +310,11 @@ int main(int argc, char *argv[]) + char **cleanenv; +@@ -286,6 +308,11 @@ struct stat dir_info; /* directory info holder */ struct stat prg_info; /* program info holder */ int cwdh; /* handle to cwd */ @@ -61,7 +59,7 @@ index f3811a97..30bb644b 100644 /* * Start with a "clean" environment -@@ -317,15 +344,10 @@ int main(int argc, char *argv[]) +@@ -315,15 +342,10 @@ || (! strcmp(AP_HTTPD_USER, pw->pw_name))) #endif /* _OSD_POSIX */ ) { @@ -78,17 +76,17 @@ index f3811a97..30bb644b 100644 #if defined(AP_LOG_SYSLOG) fprintf(stderr, " -D AP_LOG_SYSLOG\n"); #elif defined(AP_LOG_EXEC) -@@ -339,9 +361,6 @@ int main(int argc, char *argv[]) - #endif +@@ -338,9 +360,6 @@ #ifdef AP_UID_MIN fprintf(stderr, " -D AP_UID_MIN=%d\n", AP_UID_MIN); --#endif + #endif -#ifdef AP_USERDIR_SUFFIX - fprintf(stderr, " -D AP_USERDIR_SUFFIX=\"%s\"\n", AP_USERDIR_SUFFIX); - #endif +-#endif exit(0); } -@@ -357,23 +376,6 @@ int main(int argc, char *argv[]) + /* +@@ -355,23 +374,6 @@ target_gname = argv[2]; cmd = argv[3]; @@ -112,11 +110,10 @@ index f3811a97..30bb644b 100644 /* * Check for a leading '/' (absolute path) in the command to be executed, -@@ -397,6 +399,59 @@ int main(int argc, char *argv[]) - userdir = 1; +@@ -396,6 +398,59 @@ } -+ /* + /* + * Check to see if the user running this program + * is the user allowed to do so as defined in + * SUEXEC_CONFIG_DIR/username @@ -169,10 +166,11 @@ index f3811a97..30bb644b 100644 + } + } + - /* ++ /* * Error out if the target username is invalid. */ -@@ -538,7 +593,7 @@ int main(int argc, char *argv[]) + if (strspn(target_uname, "1234567890") != strlen(target_uname)) { +@@ -538,7 +593,7 @@ if (userdir) { if (((chdir(target_homedir)) != 0) || @@ -181,7 +179,7 @@ index f3811a97..30bb644b 100644 ((getcwd(dwd, AP_MAXPATH)) == NULL) || ((fchdir(cwdh)) != 0)) { log_err("cannot get docroot information (%s)\n", target_homedir); -@@ -546,7 +601,7 @@ int main(int argc, char *argv[]) +@@ -546,7 +601,7 @@ } } else { |