On 1/7/21 2:19 PM, yla...@apache.org wrote:
> Author: ylavic
> Date: Thu Jan 7 13:19:08 2021
> New Revision: 1885239
>
> URL: http://svn.apache.org/viewvc?rev=1885239&view=rev
> Log:
> mod_proxy_wstunnel: leave Upgrade requests handling to mod_proxy_http.
>
> Let mod_proxy_http's canon and scheme handlers accept "ws[s]:" schemes so that
> mod_proxy_wstunnel can decline requests when mod_proxy_http is loaded.
>
> * modules/proxy/{mod_proxy.h,proxy_util.c} (ap_proxy_worker_can_upgrade):
> Add a "dflt" argument to ap_proxy_worker_can_upgrade() which, if not NULL,
> is matched when no worker upgrade= parameter is configured. This allows to
> handle the default "Upgrade: websocket" case for "ws[s]:" schemes.
>
> * modules/proxy/mod_proxy_http.c (proxy_http_canon, proxy_http_handler):
> Add and use the new get_url_scheme() helper to parse URL schemes handled by
> mod_proxy_http and use it in canon and scheme handlers. This helper now
> accepts ws[s] schemes.
>
> * modules/proxy/mod_proxy_wstunnel.c (proxy_wstunnel_post_config):
> New post_config hook to detect whether mod_proxy_http is loaded and set
> global fallback_to_mod_proxy_http flag in this case.
>
> * modules/proxy/mod_proxy_wstunnel.c (proxy_wstunnel_check_trans,
> proxy_wstunnel_canon,
> proxy_wstunnel_handler):
> These hooks now early return DECLINED if fallback_to_mod_proxy_http is set.
>
> Added:
> httpd/httpd/trunk/changes-entries/proxy_wstunnel_to_http.txt
> Modified:
> httpd/httpd/trunk/docs/log-message-tags/next-number
> httpd/httpd/trunk/modules/proxy/mod_proxy.h
> httpd/httpd/trunk/modules/proxy/mod_proxy_http.c
> httpd/httpd/trunk/modules/proxy/mod_proxy_wstunnel.c
> httpd/httpd/trunk/modules/proxy/proxy_util.c
>
> Added: httpd/httpd/trunk/changes-entries/proxy_wstunnel_to_http.txt
> URL:
> http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/proxy_wstunnel_to_http.txt?rev=1885239&view=auto
> ==============================================================================
> --- httpd/httpd/trunk/changes-entries/proxy_wstunnel_to_http.txt (added)
> +++ httpd/httpd/trunk/changes-entries/proxy_wstunnel_to_http.txt Thu Jan 7
> 13:19:08 2021
> @@ -0,0 +1,4 @@
> + *) mod_proxy_wstunnel: Leave Upgrade requests handling to mod_proxy_http,
> + allowing for (non-)Upgrade negotiation with the origin server.
> + [Yann Ylavic]
> +
>
> Modified: httpd/httpd/trunk/docs/log-message-tags/next-number
> URL:
> http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/log-message-tags/next-number?rev=1885239&r1=1885238&r2=1885239&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/docs/log-message-tags/next-number (original)
> +++ httpd/httpd/trunk/docs/log-message-tags/next-number Thu Jan 7 13:19:08
> 2021
> @@ -1 +1 @@
> -10262
> +10263
>
> Modified: httpd/httpd/trunk/modules/proxy/mod_proxy.h
> URL:
> http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/mod_proxy.h?rev=1885239&r1=1885238&r2=1885239&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/proxy/mod_proxy.h (original)
> +++ httpd/httpd/trunk/modules/proxy/mod_proxy.h Thu Jan 7 13:19:08 2021
> @@ -755,11 +755,13 @@ PROXY_DECLARE(char *) ap_proxy_worker_na
> * @param p memory pool used for displaying worker name
> * @param worker the worker
> * @param upgrade the Upgrade header to match
> + * @param dflt default protocol (NULL for none)
> * @return 1 (true) or 0 (false)
> */
> PROXY_DECLARE(int) ap_proxy_worker_can_upgrade(apr_pool_t *p,
> const proxy_worker *worker,
> - const char *upgrade);
> + const char *upgrade,
> + const char *dflt);
>
> /**
> * Get the worker from proxy configuration
>
> Modified: httpd/httpd/trunk/modules/proxy/mod_proxy_http.c
> URL:
> http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/mod_proxy_http.c?rev=1885239&r1=1885238&r2=1885239&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/proxy/mod_proxy_http.c (original)
> +++ httpd/httpd/trunk/modules/proxy/mod_proxy_http.c Thu Jan 7 13:19:08 2021
> @@ -28,37 +28,71 @@ static int (*ap_proxy_clear_connection_f
> static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n,
> request_rec *r, int flags, int *read);
>
> +static const char *get_url_scheme(const char **url, int *is_ssl)
> +{
> + const char *u = *url;
> +
> + switch (u[0]) {
> + case 'h':
> + case 'H':
> + if (strncasecmp(u + 1, "ttp", 3) == 0) {
> + if (u[4] == ':') {
> + *is_ssl = 0;
> + *url = u + 5;
> + return "http";
> + }
> + if (apr_tolower(u[4]) == 's' && u[5] == ':') {
> + *is_ssl = 1;
> + *url = u + 6;
> + return "https";
> + }
> + }
> + break;
> +
> + case 'w':
> + case 'W':
> + if (apr_tolower(u[1]) == 's') {
> + if (u[2] == ':') {
> + *is_ssl = 0;
> + *url = u + 3;
> + return "ws";
> + }
> + if (apr_tolower(u[2]) == 's' && u[3] == ':') {
> + *is_ssl = 0;
> + *url = u + 4;
> + return "wss";
> + }
> + }
> + break;
> + }
> +
> + *is_ssl = 0;
> + return NULL;
> +}
>
> /*
> * Canonicalise http-like URLs.
> * scheme is the scheme for the URL
> * url is the URL starting with the first '/'
> - * def_port is the default port for this scheme.
> */
> static int proxy_http_canon(request_rec *r, char *url)
> {
> + const char *base_url = url;
> char *host, *path, sport[7];
> char *search = NULL;
> const char *err;
> const char *scheme;
> apr_port_t port, def_port;
> + int is_ssl = 0;
>
> - /* ap_port_of_scheme() */
> - if (ap_cstr_casecmpn(url, "http:", 5) == 0) {
> - url += 5;
> - scheme = "http";
> - }
> - else if (ap_cstr_casecmpn(url, "https:", 6) == 0) {
> - url += 6;
> - scheme = "https";
> - }
> - else {
> + scheme = get_url_scheme((const char **)&url, &is_ssl);
> + if (!scheme) {
> return DECLINED;
> }
> - port = def_port = ap_proxy_port_of_scheme(scheme);
> + port = def_port = (is_ssl) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;
>
> ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
> - "HTTP: canonicalising URL %s", url);
> + "HTTP: canonicalising URL %s", base_url);
>
> /* do syntatic check.
> * We break the URL into host, port, path, search
> @@ -66,7 +100,7 @@ static int proxy_http_canon(request_rec
> err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
> if (err) {
> ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01083)
> - "error parsing URL %s: %s", url, err);
> + "error parsing URL %s: %s", base_url, err);
> return HTTP_BAD_REQUEST;
> }
>
> @@ -106,8 +140,9 @@ static int proxy_http_canon(request_rec
> if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
> host = apr_pstrcat(r->pool, "[", host, "]", NULL);
> }
> +
> r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
> - "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
> + "/", path, (search) ? "?" : "", search, NULL);
> return OK;
> }
>
> @@ -1568,7 +1603,9 @@ int ap_proxy_http_process_response(proxy
> * may be an HTTP_UPGRADE_REQUIRED response or some other status
> where
> * Upgrade makes sense to negotiate the protocol by other means.
> */
> - if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade)) {
> + if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade,
> + (*req->proto == 'w')
> + ? "WebSocket" : NULL)) {
> apr_table_setn(r->headers_out, "Connection", "Upgrade");
> apr_table_setn(r->headers_out, "Upgrade", apr_pstrdup(p,
> upgrade));
> }
> @@ -1840,9 +1877,8 @@ static int proxy_http_handler(request_re
> apr_port_t proxyport)
> {
> int status;
> - char *scheme;
> - const char *proxy_function;
> - const char *u;
> + const char *scheme;
> + const char *u = url;
> proxy_http_req_t *req = NULL;
> proxy_conn_rec *backend = NULL;
> apr_bucket_brigade *input_brigade = NULL;
> @@ -1860,41 +1896,31 @@ static int proxy_http_handler(request_re
> apr_pool_t *p = r->pool;
> apr_uri_t *uri;
>
> - /* find the scheme */
> - u = strchr(url, ':');
> - if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
> + scheme = get_url_scheme(&u, &is_ssl);
> + if (!scheme && proxyname && strncasecmp(url, "ftp:", 4) == 0) {
> + u = url + 4;
> + scheme = "ftp";
> + is_ssl = 0;
> + }
> + if (!scheme || u[0] != '/' || u[1] != '/' || u[2] == '\0') {
> + if (!scheme && (u = strchr(url, ':')) && (u - url) > 14) {
> + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10262)
> + "overlong proxy URL scheme in %s", url);
> + return HTTP_BAD_REQUEST;
> + }
This breaks forward proxies with the CONNECT method.
For CONNECT somwhere123456789.com:443 schema is NULL and u[0] is 's' and hence
!= /.
The following patches fixes this:
Index: mod_proxy_http.c
===================================================================
--- mod_proxy_http.c (revision 1886120)
+++ mod_proxy_http.c (working copy)
@@ -1903,15 +1903,15 @@
is_ssl = 0;
}
if (!scheme || u[0] != '/' || u[1] != '/' || u[2] == '\0') {
- if (!scheme && (u = strchr(url, ':')) && (u - url) > 14) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10262)
- "overlong proxy URL scheme in %s", url);
- return HTTP_BAD_REQUEST;
- }
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01113)
"HTTP: declining URL %s", url);
return DECLINED; /* only interested in HTTP, WS or FTP via proxy */
}
+ if (!scheme && (u = strchr(url, ':')) && (u - url) > 14) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10262)
+ "overlong proxy URL scheme in %s", url);
+ return HTTP_BAD_REQUEST;
+ }
if (is_ssl && !ap_proxy_ssl_enable(NULL)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01112)
"HTTP: declining URL %s (mod_ssl not configured?)", url);
Unfortunately this has been already backported in r1885605 and hence 2.4.x is
now broken as well.
Regards
RĂ¼diger