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

Reply via email to