summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2021-08-10 08:27:18 +0000
committerStefan Eissing <icing@apache.org>2021-08-10 08:27:18 +0000
commit6a5d3e006b8dc8aca1a267a8607864e1c3607f61 (patch)
treee83ad495a2a46d6f08ee77859d95eb3a9432ddc7
parent8dc8b50fe67111c2f18a3842fc25d0183061638c (diff)
downloadhttpd-6a5d3e006b8dc8aca1a267a8607864e1c3607f61.tar.gz
Merged /httpd/httpd/trunk:r1879074-1879080,1879094-1879095,1879110-1879112,1879114,1879116-1879117,1879137,1879144-1879145,1879147,1879149,1879235,1879360
back port the mapping=servlet proxy logic. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1892161 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--docs/manual/developer/modguide.xml1
-rw-r--r--docs/manual/mod/mod_log_debug.xml1
-rw-r--r--docs/manual/mod/mod_lua.xml28
-rw-r--r--include/ap_mmn.h6
-rw-r--r--include/http_request.h12
-rw-r--r--include/httpd.h15
-rw-r--r--modules/dav/main/util.c8
-rw-r--r--modules/examples/mod_example_hooks.c17
-rw-r--r--modules/generators/mod_autoindex.c5
-rw-r--r--modules/generators/mod_info.c1
-rw-r--r--modules/loggers/mod_log_debug.c8
-rw-r--r--modules/lua/mod_lua.c31
-rw-r--r--modules/proxy/mod_proxy.c334
-rw-r--r--modules/proxy/mod_proxy.h10
-rw-r--r--server/request.c129
-rw-r--r--server/util.c158
16 files changed, 633 insertions, 131 deletions
diff --git a/docs/manual/developer/modguide.xml b/docs/manual/developer/modguide.xml
index 63f3d0a9d3..4ebd7fa79b 100644
--- a/docs/manual/developer/modguide.xml
+++ b/docs/manual/developer/modguide.xml
@@ -237,6 +237,7 @@ can create. Some other ways of hooking are:
<li><code>ap_hook_child_init</code>: Place a hook that executes when a child process is spawned (commonly used for initializing modules after the server has forked)</li>
<li><code>ap_hook_pre_config</code>: Place a hook that executes before any configuration data has been read (very early hook)</li>
<li><code>ap_hook_post_config</code>: Place a hook that executes after configuration has been parsed, but before the server has forked</li>
+<li><code>ap_hook_pre_translate_name</code>: Place a hook that executes when a URI needs to be translated into a filename on the server, before decoding</li>
<li><code>ap_hook_translate_name</code>: Place a hook that executes when a URI needs to be translated into a filename on the server (think <code>mod_rewrite</code>)</li>
<li><code>ap_hook_quick_handler</code>: Similar to <code>ap_hook_handler</code>, except it is run before any other request hooks (translation, auth, fixups etc)</li>
<li><code>ap_hook_log_transaction</code>: Place a hook that executes when the server is about to add a log entry of the current request</li>
diff --git a/docs/manual/mod/mod_log_debug.xml b/docs/manual/mod/mod_log_debug.xml
index e992533461..c2280b4d0a 100644
--- a/docs/manual/mod/mod_log_debug.xml
+++ b/docs/manual/mod/mod_log_debug.xml
@@ -103,6 +103,7 @@
<table border="1" style="zebra">
<columnspec><column width="1"/></columnspec>
<tr><th>Name</th></tr>
+ <tr><td><code>pre_translate_name</code></td></tr>
<tr><td><code>translate_name</code></td></tr>
<tr><td><code>type_checker</code></td></tr>
<tr><td><code>quick_handler</code></td></tr>
diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml
index 3d853f9101..8c1195f8de 100644
--- a/docs/manual/mod/mod_lua.xml
+++ b/docs/manual/mod/mod_lua.xml
@@ -216,6 +216,13 @@ performing access control, or setting mime types:</p>
been mapped to a host or virtual host</td>
</tr>
<tr>
+ <td>Pre-Translate name</td>
+ <td><directive module="mod_lua">LuaHookPreTranslateName</directive></td>
+ <td>This phase translates the requested URI into a filename on the
+ system, before decoding occurs. Modules such as <module>mod_proxy</module>
+ can operate in this phase.</td>
+ </tr>
+ <tr>
<td>Translate name</td>
<td><directive module="mod_lua">LuaHookTranslateName</directive></td>
<td>This phase translates the requested URI into a filename on the
@@ -439,7 +446,7 @@ end
<td>string</td>
<td>yes</td>
<td>The file name that the request maps to, f.x. /www/example.com/foo.txt. This can be
- changed in the translate-name or map-to-storage phases of a request to allow the
+ changed in the pre-translate-name, translate-name or map-to-storage phases of a request to allow the
default handler (or script handlers) to serve a different file than what was requested.</td>
</tr>
<tr>
@@ -538,7 +545,7 @@ end
<td>string</td>
<td>yes</td>
<td>Denotes whether this is a proxy request or not. This value is generally set in
- the post_read_request/translate_name phase of a request.</td>
+ the post_read_request/pre_translate_name/translate_name phase of a request.</td>
</tr>
<tr>
<td><code>range</code></td>
@@ -1495,6 +1502,23 @@ end
</directivesynopsis>
<directivesynopsis>
+<name>LuaHookPreTranslate</name>
+<description>Provide a hook for the pre_translate phase of a request
+processing</description>
+<syntax>LuaHookPreTranslate /path/to/lua/script.lua hook_function_name</syntax>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>All</override>
+<usage>
+<p>
+ Just like LuaHookTranslateName, but executed at the pre_translate phase,
+ where the URI-path is not percent decoded.
+</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
<name>LuaHookFixups</name>
<description>Provide a hook for the fixups phase of a request
processing</description>
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 239273762f..a156e4041c 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -563,6 +563,10 @@
* 20120211.107 (2.4.49-dev) Add ap_parse_request_line() and
* ap_check_request_header()
* 20120211.108 (2.4.49-dev) Add ajp_handle_cping_cpong
+ * 20120211.109 (2.4.49-dev) Add ap_normalize_path(),
+ * pre_translate_name hook and
+ * Add map_encoded_one and map_encoded_all bits to
+ * proxy_server_conf.
*/
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
@@ -570,7 +574,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20120211
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 108 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 109 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
diff --git a/include/http_request.h b/include/http_request.h
index 5f05668177..7e8bfad122 100644
--- a/include/http_request.h
+++ b/include/http_request.h
@@ -364,6 +364,18 @@ AP_DECLARE_HOOK(int,create_request,(request_rec *r))
/**
* This hook allow modules an opportunity to translate the URI into an
+ * actual filename, before URL decoding happens.
+ * @param r The current request
+ * @return DECLINED to let other modules handle the pre-translation,
+ * OK if it was handled and no other module should process it,
+ * DONE if no further transformation should happen on the URI,
+ * HTTP_... in case of error.
+ * @ingroup hooks
+ */
+AP_DECLARE_HOOK(int,pre_translate_name,(request_rec *r))
+
+/**
+ * This hook allow modules an opportunity to translate the URI into an
* actual filename. If no modules do anything special, the server's default
* rules will be followed.
* @param r The current request
diff --git a/include/httpd.h b/include/httpd.h
index 14f15b5c9b..02cde256ed 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -1762,6 +1762,21 @@ AP_DECLARE(void) ap_no2slash(char *name);
*/
AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path);
+#define AP_NORMALIZE_ALLOW_RELATIVE (1u << 0)
+#define AP_NORMALIZE_NOT_ABOVE_ROOT (1u << 1)
+#define AP_NORMALIZE_DECODE_UNRESERVED (1u << 2)
+#define AP_NORMALIZE_MERGE_SLASHES (1u << 3)
+#define AP_NORMALIZE_DROP_PARAMETERS (1u << 4)
+
+/**
+ * Remove all ////, /./ and /xx/../ substrings from a path, and more
+ * depending on passed in flags.
+ * @param path The path to normalize
+ * @param flags bitmask of AP_NORMALIZE_* flags
+ * @return non-zero on success
+ */
+AP_DECLARE(int) ap_normalize_path(char *path, unsigned int flags);
+
/**
* Remove all ./ and xx/../ substrings from a file name. Also remove
* any leading ../ or /../ substrings.
diff --git a/modules/dav/main/util.c b/modules/dav/main/util.c
index e21f626068..08ebe2764e 100644
--- a/modules/dav/main/util.c
+++ b/modules/dav/main/util.c
@@ -664,7 +664,13 @@ static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
/* note that parsed_uri.path is allocated; we can trash it */
/* clean up the URI a bit */
- ap_getparents(parsed_uri.path);
+ if (!ap_normalize_path(parsed_uri.path,
+ AP_NORMALIZE_NOT_ABOVE_ROOT |
+ AP_NORMALIZE_DECODE_UNRESERVED)) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_TAGGED, rv,
+ "Invalid URI path tagged If-header.");
+ }
/* the resources we will compare to have unencoded paths */
if (ap_unescape_url(parsed_uri.path) != OK) {
diff --git a/modules/examples/mod_example_hooks.c b/modules/examples/mod_example_hooks.c
index 2ab945eb3a..f7ef5a5ccf 100644
--- a/modules/examples/mod_example_hooks.c
+++ b/modules/examples/mod_example_hooks.c
@@ -1175,6 +1175,22 @@ static int x_post_read_request(request_rec *r)
/*
* This routine gives our module an opportunity to translate the URI into an
+ * actual filename, before URL decoding happens.
+ *
+ * This is a RUN_FIRST hook.
+ */
+static int x_pre_translate_name(request_rec *r)
+{
+ /*
+ * We don't actually *do* anything here, except note the fact that we were
+ * called.
+ */
+ trace_request(r, "x_pre_translate_name()");
+ return DECLINED;
+}
+
+/*
+ * This routine gives our module an opportunity to translate the URI into an
* actual filename. If we don't do anything special, the server's default
* rules (Alias directives and the like) will continue to be followed.
*
@@ -1467,6 +1483,7 @@ static void x_register_hooks(apr_pool_t *p)
ap_hook_log_transaction(x_log_transaction, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_http_scheme(x_http_scheme, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_default_port(x_default_port, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_pre_translate_name(x_pre_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_translate_name(x_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_map_to_storage(x_map_to_storage, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_header_parser(x_header_parser, NULL, NULL, APR_HOOK_MIDDLE);
diff --git a/modules/generators/mod_autoindex.c b/modules/generators/mod_autoindex.c
index 28ed85a8f3..cb4460357c 100644
--- a/modules/generators/mod_autoindex.c
+++ b/modules/generators/mod_autoindex.c
@@ -1266,8 +1266,9 @@ static struct ent *make_parent_entry(apr_int32_t autoindex_opts,
if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) {
return (NULL);
}
- ap_getparents(p->name);
- if (!*p->name) {
+ if (!ap_normalize_path(p->name, AP_NORMALIZE_ALLOW_RELATIVE |
+ AP_NORMALIZE_NOT_ABOVE_ROOT)
+ || p->name[0] == '\0') {
return (NULL);
}
diff --git a/modules/generators/mod_info.c b/modules/generators/mod_info.c
index cf1a1fd8c5..b044273062 100644
--- a/modules/generators/mod_info.c
+++ b/modules/generators/mod_info.c
@@ -322,6 +322,7 @@ static const hook_lookup_t request_hooks[] = {
{"HTTP Scheme", ap_hook_get_http_scheme},
{"Default Port", ap_hook_get_default_port},
{"Quick Handler", ap_hook_get_quick_handler},
+ {"Pre-Translate Name", ap_hook_get_pre_translate_name},
{"Translate Name", ap_hook_get_translate_name},
{"Map to Storage", ap_hook_get_map_to_storage},
{"Check Access", ap_hook_get_access_checker_ex},
diff --git a/modules/loggers/mod_log_debug.c b/modules/loggers/mod_log_debug.c
index 8a6c1244f5..3f27a958de 100644
--- a/modules/loggers/mod_log_debug.c
+++ b/modules/loggers/mod_log_debug.c
@@ -49,6 +49,7 @@ static const char * const hooks[] = {
"check_authn", /* 9 */
"check_authz", /* 10 */
"insert_filter", /* 11 */
+ "pre_translate_name", /* 12 */
NULL
};
@@ -109,6 +110,12 @@ static int log_debug_handler(request_rec *r)
return DECLINED;
}
+static int log_debug_pre_translate_name(request_rec *r)
+{
+ do_debug_log(r, hooks[12]);
+ return DECLINED;
+}
+
static int log_debug_translate_name(request_rec *r)
{
do_debug_log(r, hooks[3]);
@@ -263,6 +270,7 @@ static void register_hooks(apr_pool_t *p)
ap_hook_log_transaction(log_debug_log_transaction, NULL, NULL, APR_HOOK_FIRST);
ap_hook_quick_handler(log_debug_quick_handler, NULL, NULL, APR_HOOK_FIRST);
ap_hook_handler(log_debug_handler, NULL, NULL, APR_HOOK_FIRST);
+ ap_hook_pre_translate_name(log_debug_pre_translate_name, NULL, NULL, APR_HOOK_FIRST);
ap_hook_translate_name(log_debug_translate_name, NULL, NULL, APR_HOOK_FIRST);
ap_hook_map_to_storage(log_debug_map_to_storage, NULL, NULL, APR_HOOK_FIRST);
ap_hook_fixups(log_debug_fixups, NULL, NULL, APR_HOOK_FIRST);
diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c
index 23114304b3..665e2cddc7 100644
--- a/modules/lua/mod_lua.c
+++ b/modules/lua/mod_lua.c
@@ -1202,6 +1202,11 @@ static int lua_check_user_id_harness_last(request_rec *r)
}
*/
+static int lua_pre_trans_name_harness(request_rec *r)
+{
+ return lua_request_rec_hook_harness(r, "pre_translate_name", APR_HOOK_MIDDLE);
+}
+
static int lua_translate_name_harness_first(request_rec *r)
{
return lua_request_rec_hook_harness(r, "translate_name", AP_LUA_HOOK_FIRST);
@@ -1274,6 +1279,21 @@ static int lua_quick_harness(request_rec *r, int lookup)
return lua_request_rec_hook_harness(r, "quick", APR_HOOK_MIDDLE);
}
+static const char *register_pre_trans_name_hook(cmd_parms *cmd, void *_cfg,
+ const char *file,
+ const char *function)
+{
+ return register_named_file_function_hook("pre_translate_name", cmd, _cfg, file,
+ function, APR_HOOK_MIDDLE);
+}
+
+static const char *register_pre_trans_name_block(cmd_parms *cmd, void *_cfg,
+ const char *line)
+{
+ return register_named_block_function_hook("pre_translate_name", cmd, _cfg,
+ line);
+}
+
static const char *register_translate_name_hook(cmd_parms *cmd, void *_cfg,
const char *file,
const char *function,
@@ -1842,6 +1862,14 @@ static const command_rec lua_commands[] = {
AP_INIT_TAKE3("LuaAuthzProvider", register_authz_provider, NULL, RSRC_CONF|EXEC_ON_READ,
"Provide an authorization provider"),
+ AP_INIT_TAKE2("LuaHookPreTranslateName", register_pre_trans_name_hook, NULL,
+ OR_ALL,
+ "Provide a hook for the pre_translate name phase of request processing"),
+
+ AP_INIT_RAW_ARGS("<LuaHookPreTranslateName", register_pre_trans_name_block, NULL,
+ EXEC_ON_READ | OR_ALL,
+ "Provide a hook for the pre_translate name phase of request processing"),
+
AP_INIT_TAKE23("LuaHookTranslateName", register_translate_name_hook, NULL,
OR_ALL,
"Provide a hook for the translate name phase of request processing"),
@@ -2092,6 +2120,9 @@ static void lua_register_hooks(apr_pool_t *p)
APR_HOOK_MIDDLE);
/* http_request.h hooks */
+ ap_hook_pre_translate_name(lua_pre_trans_name_harness, NULL, NULL,
+ APR_HOOK_MIDDLE);
+
ap_hook_translate_name(lua_translate_name_harness_first, NULL, NULL,
AP_LUA_HOOK_FIRST);
ap_hook_translate_name(lua_translate_name_harness, NULL, NULL,
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index 0f787a6b45..e8b0f173b5 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -17,6 +17,7 @@
#include "mod_proxy.h"
#include "mod_core.h"
#include "apr_optional.h"
+#include "apr_strings.h"
#include "scoreboard.h"
#include "mod_status.h"
#include "proxy_util.h"
@@ -560,6 +561,201 @@ static int alias_match(const char *uri, const char *alias_fakename)
return urip - uri;
}
+/*
+ * Inspired by mod_jk's jk_servlet_normalize().
+ */
+static int alias_match_servlet(apr_pool_t *p,
+ const char **urip,
+ const char *alias)
+{
+ char *map;
+ const char *uri = *urip;
+ apr_array_header_t *stack;
+ int map_pos, uri_pos, alias_pos, first_pos;
+ int alias_depth = 0, depth;
+
+ /* Both uri and alias should start with '/' */
+ if (uri[0] != '/' || alias[0] != '/') {
+ return 0;
+ }
+
+ stack = apr_array_make(p, 5, sizeof(int));
+ map = apr_palloc(p, strlen(uri) + 1);
+ map[0] = '/';
+ map[1] = '\0';
+
+ map_pos = uri_pos = alias_pos = first_pos = 1;
+ while (uri[uri_pos] != '\0') {
+ /* Remove path parameters ;foo=bar/ from any path segment */
+ if (uri[uri_pos] == ';') {
+ do {
+ uri_pos++;
+ } while (uri[uri_pos] != '/' && uri[uri_pos] != '\0');
+ continue;
+ }
+
+ if (map[map_pos - 1] == '/') {
+ /* Collapse ///// sequences to / */
+ if (uri[uri_pos] == '/') {
+ do {
+ uri_pos++;
+ } while (uri[uri_pos] == '/');
+ continue;
+ }
+
+ if (uri[uri_pos] == '.') {
+ /* Remove /./ segments */
+ if (uri[uri_pos + 1] == '/'
+ || uri[uri_pos + 1] == ';'
+ || uri[uri_pos + 1] == '\0') {
+ uri_pos++;
+ if (uri[uri_pos] == '/') {
+ uri_pos++;
+ }
+ continue;
+ }
+
+ /* Remove /xx/../ segments */
+ if (uri[uri_pos + 1] == '.'
+ && (uri[uri_pos + 2] == '/'
+ || uri[uri_pos + 2] == ';'
+ || uri[uri_pos + 2] == '\0')) {
+ /* Wind map segment back the previous one */
+ if (map_pos == 1) {
+ /* Above root */
+ return 0;
+ }
+ do {
+ map_pos--;
+ } while (map[map_pos - 1] != '/');
+ map[map_pos] = '\0';
+
+ /* Wind alias segment back, unless in deeper segment */
+ if (alias_depth == stack->nelts) {
+ if (alias[alias_pos] == '\0') {
+ alias_pos--;
+ }
+ while (alias_pos > 0 && alias[alias_pos] == '/') {
+ alias_pos--;
+ }
+ while (alias_pos > 0 && alias[alias_pos - 1] != '/') {
+ alias_pos--;
+ }
+ AP_DEBUG_ASSERT(alias_pos > 0);
+ alias_depth--;
+ }
+ apr_array_pop(stack);
+
+ /* Move uri forward to the next segment */
+ uri_pos += 2;
+ if (uri[uri_pos] == '/') {
+ uri_pos++;
+ }
+ first_pos = 0;
+ continue;
+ }
+ }
+ if (first_pos) {
+ while (uri[first_pos] == '/') {
+ first_pos++;
+ }
+ }
+
+ /* New segment */
+ APR_ARRAY_PUSH(stack, int) = first_pos ? first_pos : uri_pos;
+ if (alias[alias_pos] != '\0') {
+ if (alias[alias_pos - 1] != '/') {
+ /* Remain in pair with uri segments */
+ do {
+ alias_pos++;
+ } while (alias[alias_pos - 1] != '/' && alias[alias_pos]);
+ }
+ while (alias[alias_pos] == '/') {
+ alias_pos++;
+ }
+ if (alias[alias_pos] != '\0') {
+ alias_depth++;
+ }
+ }
+ }
+
+ if (alias[alias_pos] != '\0') {
+ int *match = &APR_ARRAY_IDX(stack, alias_depth - 1, int);
+ if (*match) {
+ if (alias[alias_pos] != uri[uri_pos]) {
+ /* Current segment does not match */
+ *match = 0;
+ }
+ else if (alias[alias_pos + 1] == '\0'
+ && alias[alias_pos] != '/') {
+ if (uri[uri_pos + 1] == ';') {
+ /* We'll preserve the parameters of the last
+ * segment if it does not end with '/', so mark
+ * the match as negative for below handling.
+ */
+ *match = -(uri_pos + 1);
+ }
+ else if (uri[uri_pos + 1] != '/'
+ && uri[uri_pos + 1] != '\0') {
+ /* Last segment does not match all the way */
+ *match = 0;
+ }
+ }
+ }
+ /* Don't go past the segment if the uri isn't there yet */
+ if (alias[alias_pos] != '/' || uri[uri_pos] == '/') {
+ alias_pos++;
+ }
+ }
+
+ if (uri[uri_pos] == '/') {
+ first_pos = uri_pos + 1;
+ }
+ map[map_pos++] = uri[uri_pos++];
+ map[map_pos] = '\0';
+ }
+
+ /* Can't reach the end of uri before the end of the alias,
+ * for example if uri is "/" and alias is "/examples"
+ */
+ if (alias[alias_pos] != '\0') {
+ return 0;
+ }
+
+ /* Check whether each alias segment matched */
+ for (depth = 0; depth < alias_depth; ++depth) {
+ if (!APR_ARRAY_IDX(stack, depth, int)) {
+ return 0;
+ }
+ }
+
+ /* If alias_depth == stack->nelts we have a full match, i.e.
+ * uri == alias so we can return uri_pos as is (the end of uri)
+ */
+ if (alias_depth < stack->nelts) {
+ /* Return the segment following the alias */
+ uri_pos = APR_ARRAY_IDX(stack, alias_depth, int);
+ if (alias_depth) {
+ /* But if the last segment of the alias does not end with '/'
+ * and the corresponding segment of the uri has parameters,
+ * we want to forward those parameters (see above for the
+ * negative pos trick/mark).
+ */
+ int pos = APR_ARRAY_IDX(stack, alias_depth - 1, int);
+ if (pos < 0) {
+ uri_pos = -pos;
+ }
+ }
+ }
+ /* If the alias lacks a trailing slash, take it from the uri (if any) */
+ if (alias[alias_pos - 1] != '/' && uri[uri_pos - 1] == '/') {
+ uri_pos--;
+ }
+
+ *urip = map;
+ return uri_pos;
+}
+
/* Detect if an absoluteURI should be proxied or not. Note that we
* have to do this during this phase because later phases are
* "short-circuiting"... i.e. translate_names will end when the first
@@ -670,6 +866,7 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
int mismatch = 0;
unsigned int nocanon = ent->flags & PROXYPASS_NOCANON;
const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
+ const char *servlet_uri = NULL;
if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) {
fake = proxy_interpolate(r, ent->fake);
@@ -730,7 +927,14 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
}
}
else {
- len = alias_match(r->uri, fake);
+ if ((ent->flags & PROXYPASS_MAP_SERVLET) == PROXYPASS_MAP_SERVLET) {
+ servlet_uri = r->uri;
+ len = alias_match_servlet(r->pool, &servlet_uri, fake);
+ nocanon = 0; /* ignored since servlet's normalization applies */
+ }
+ else {
+ len = alias_match(r->uri, fake);
+ }
if (len != 0) {
if ((real[0] == '!') && (real[1] == '\0')) {
@@ -761,7 +965,7 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
*/
int rc = proxy_run_check_trans(r, found + 6);
if (rc != OK && rc != DECLINED) {
- return DONE;
+ return HTTP_CONTINUE;
}
r->filename = found;
@@ -775,28 +979,63 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
apr_table_setn(r->notes, "proxy-noquery", "1");
}
+ if (servlet_uri) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10248)
+ "Servlet path '%s' (%s) matches proxy handler '%s'",
+ r->uri, servlet_uri, found);
+ /* Apply servlet normalization to r->uri so that <Location> or any
+ * directory context match does not have to handle path parameters.
+ * We change r->uri in-place so that r->parsed_uri.path is updated
+ * too. Since normalized servlet_uri is necessarily shorter than
+ * the original r->uri, strcpy() is fine.
+ */
+ AP_DEBUG_ASSERT(strlen(r->uri) >= strlen(servlet_uri));
+ strcpy(r->uri, servlet_uri);
+ return DONE;
+ }
+
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03464)
"URI path '%s' matches proxy handler '%s'", r->uri,
found);
-
return OK;
}
- return DONE;
+ return HTTP_CONTINUE;
}
-static int proxy_trans(request_rec *r)
+static int proxy_trans(request_rec *r, int pre_trans)
{
- int i;
+ int i, enc;
struct proxy_alias *ent;
proxy_dir_conf *dconf;
proxy_server_conf *conf;
if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves
- * in proxy_detect
+ * in proxy_detect (DONE will prevent further decoding of r->uri,
+ * only if proxyreq is set before pre_trans already).
*/
- return OK;
+ return pre_trans ? DONE : OK;
+ }
+
+ /* In early pre_trans hook, r->uri was not manipulated yet so we are
+ * compliant with RFC1945 at this point. Otherwise, it probably isn't
+ * an issue because this is a hybrid proxy/origin server.
+ */
+
+ dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
+ conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config,
+ &proxy_module);
+
+ /* Always and only do PROXY_MAP_ENCODED mapping in pre_trans, when
+ * r->uri is still encoded, or we might consider for instance that
+ * a decoded sub-delim is now a delimiter (e.g. "%3B" => ';' for
+ * path parameters), which it's not.
+ */
+ if ((pre_trans && !conf->map_encoded_one)
+ || (!pre_trans && conf->map_encoded_all)) {
+ /* Fast path, nothing at this stage */
+ return DECLINED;
}
if ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0')
@@ -808,37 +1047,42 @@ static int proxy_trans(request_rec *r)
return DECLINED;
}
- /* XXX: since r->uri has been manipulated already we're not really
- * compliant with RFC1945 at this point. But this probably isn't
- * an issue because this is a hybrid proxy/origin server.
- */
-
- dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
-
/* short way - this location is reverse proxied? */
if (dconf->alias) {
- int rv = ap_proxy_trans_match(r, dconf->alias, dconf);
- if (DONE != rv) {
- return rv;
+ enc = (dconf->alias->flags & PROXYPASS_MAP_ENCODED) != 0;
+ if (!(pre_trans ^ enc)) {
+ int rv = ap_proxy_trans_match(r, dconf->alias, dconf);
+ if (rv != HTTP_CONTINUE) {
+ return rv;
+ }
}
}
- conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config,
- &proxy_module);
-
/* long way - walk the list of aliases, find a match */
- if (conf->aliases->nelts) {
- ent = (struct proxy_alias *) conf->aliases->elts;
- for (i = 0; i < conf->aliases->nelts; i++) {
- int rv = ap_proxy_trans_match(r, &ent[i], dconf);
- if (DONE != rv) {
+ for (i = 0; i < conf->aliases->nelts; i++) {
+ ent = &((struct proxy_alias *)conf->aliases->elts)[i];
+ enc = (ent->flags & PROXYPASS_MAP_ENCODED) != 0;
+ if (!(pre_trans ^ enc)) {
+ int rv = ap_proxy_trans_match(r, ent, dconf);
+ if (rv != HTTP_CONTINUE) {
return rv;
}
}
}
+
return DECLINED;
}
+static int proxy_pre_translate_name(request_rec *r)
+{
+ return proxy_trans(r, 1);
+}
+
+static int proxy_translate_name(request_rec *r)
+{
+ return proxy_trans(r, 0);
+}
+
static int proxy_walk(request_rec *r)
{
proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
@@ -1359,6 +1603,8 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s)
ps->forward = NULL;
ps->reverse = NULL;
ps->domain = NULL;
+ ps->map_encoded_one = 0;
+ ps->map_encoded_all = 1;
ps->id = apr_psprintf(p, "p%x", 1); /* simply for storage size */
ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
ps->viaopt_set = 0; /* 0 means default */
@@ -1516,6 +1762,9 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
ps->forward = overrides->forward ? overrides->forward : base->forward;
ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
+ ps->map_encoded_one = overrides->map_encoded_one || base->map_encoded_one;
+ ps->map_encoded_all = overrides->map_encoded_all && base->map_encoded_all;
+
ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
ps->id = (overrides->id == NULL) ? base->id : overrides->id;
ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
@@ -1640,7 +1889,7 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
: add->forward_100_continue;
new->forward_100_continue_set = add->forward_100_continue_set
|| base->forward_100_continue_set;
-
+
return new;
}
@@ -1814,11 +2063,31 @@ static const char *
"in the form 'key=value'.";
}
}
- else
+ else {
*val++ = '\0';
- apr_table_setn(params, word, val);
+ }
+ if (!strcasecmp(word, "mapping")) {
+ if (!strcasecmp(val, "encoded")) {
+ flags |= PROXYPASS_MAP_ENCODED;
+ }
+ else if (!strcasecmp(val, "servlet")) {
+ flags |= PROXYPASS_MAP_SERVLET;
+ }
+ else {
+ return "unknown mapping";
+ }
+ }
+ else {
+ apr_table_setn(params, word, val);
+ }
}
- };
+ }
+ if (flags & PROXYPASS_MAP_ENCODED) {
+ conf->map_encoded_one = 1;
+ }
+ else {
+ conf->map_encoded_all = 0;
+ }
if (r == NULL) {
return "ProxyPass|ProxyPassMatch needs a path when not defined in a location";
@@ -3142,7 +3411,10 @@ static void register_hooks(apr_pool_t *p)
/* handler */
ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
/* filename-to-URI translation */
- ap_hook_translate_name(proxy_trans, aszSucc, NULL, APR_HOOK_FIRST);
+ ap_hook_pre_translate_name(proxy_pre_translate_name, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ ap_hook_translate_name(proxy_translate_name, aszSucc, NULL,
+ APR_HOOK_FIRST);
/* walk <Proxy > entries and suppress default TRACE behavior */
ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
/* fixups */
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index c1c61068bc..bf6bbd0fb3 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -124,6 +124,8 @@ struct proxy_remote {
#define PROXYPASS_NOCANON 0x01
#define PROXYPASS_INTERPOLATE 0x02
#define PROXYPASS_NOQUERY 0x04
+#define PROXYPASS_MAP_ENCODED 0x08
+#define PROXYPASS_MAP_SERVLET 0x18 /* + MAP_ENCODED */
struct proxy_alias {
const char *real;
const char *fake;
@@ -200,6 +202,8 @@ typedef struct {
unsigned int inherit_set:1;
unsigned int ppinherit:1;
unsigned int ppinherit_set:1;
+ unsigned int map_encoded_one:1;
+ unsigned int map_encoded_all:1;
} proxy_server_conf;
typedef struct {
@@ -1171,7 +1175,11 @@ PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b,
* @param r request
* @param ent proxy_alias record
* @param dconf per-dir config or NULL
- * @return DECLINED, DONE or OK if matched
+ * @return OK if the alias matched,
+ * DONE if the alias matched and r->uri was normalized so
+ * no further transformation should happen on it,
+ * DECLINED if proxying is disabled for this alias,
+ * HTTP_CONTINUE if the alias did not match
*/
PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r,
struct proxy_alias *ent,
diff --git a/server/request.c b/server/request.c
index 299eae04d3..eb30f6b478 100644
--- a/server/request.c
+++ b/server/request.c
@@ -59,6 +59,7 @@
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
APR_HOOK_STRUCT(
+ APR_HOOK_LINK(pre_translate_name)
APR_HOOK_LINK(translate_name)
APR_HOOK_LINK(map_to_storage)
APR_HOOK_LINK(check_user_id)
@@ -74,6 +75,8 @@ APR_HOOK_STRUCT(
APR_HOOK_LINK(force_authn)
)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,pre_translate_name,
+ (request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
(request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int,map_to_storage,
@@ -157,6 +160,25 @@ AP_DECLARE(int) ap_some_authn_required(request_rec *r)
return rv;
}
+static int walk_location_and_if(request_rec *r)
+{
+ int access_status;
+ core_dir_config *d;
+
+ if ((access_status = ap_location_walk(r))) {
+ return access_status;
+ }
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
+
+ d = ap_get_core_module_config(r->per_dir_config);
+ if (d->log)
+ r->log = d->log;
+
+ return OK;
+}
+
/* This is the master logic for processing requests. Do NOT duplicate
* this logic elsewhere, or the security model will be broken by future
* API changes. Each phase must be individually optimized to pick up
@@ -164,17 +186,65 @@ AP_DECLARE(int) ap_some_authn_required(request_rec *r)
*/
AP_DECLARE(int) ap_process_request_internal(request_rec *r)
{
+ int access_status = DECLINED;
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);
+ unsigned int normalize_flags;
+
+ normalize_flags = AP_NORMALIZE_NOT_ABOVE_ROOT;
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ normalize_flags |= AP_NORMALIZE_MERGE_SLASHES;
+ }
+ if (file_req) {
+ /* File subrequests can have a relative path. */
+ normalize_flags |= AP_NORMALIZE_ALLOW_RELATIVE;
+ }
+
+ if (r->parsed_uri.path) {
+ /* Normalize: remove /./ and shrink /../ segments, plus
+ * decode unreserved chars (first time only to avoid
+ * double decoding after ap_unescape_url() below).
+ */
+ if (!ap_normalize_path(r->parsed_uri.path,
+ normalize_flags |
+ AP_NORMALIZE_DECODE_UNRESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10244)
+ "invalid URI path (%s)", r->unparsed_uri);
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
+ /* All file subrequests are a huge pain... they cannot bubble through the
+ * next several steps. Only file subrequests are allowed an empty uri,
+ * otherwise let (pre_)translate_name kill the request.
+ */
+ if (!file_req) {
+ ap_conf_vector_t *per_dir_config = r->per_dir_config;
+
+ if ((access_status = walk_location_and_if(r))) {
+ return access_status;
+ }
+
+ /* Let pre_translate_name hooks work with non-decoded URIs, and
+ * eventually prevent further URI transformations (return DONE).
+ */
+ access_status = ap_run_pre_translate_name(r);
+ if (ap_is_HTTP_ERROR(access_status)) {
+ return access_status;
+ }
+
+ /* Throw away pre_trans only merging */
+ r->per_dir_config = per_dir_config;
+ }
+
+ /* Ignore URL unescaping for translated URIs already */
+ if (access_status != DONE && r->parsed_uri.path) {
+ core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
- /* Ignore embedded %2F's in path for proxy requests */
- if (!r->proxyreq && r->parsed_uri.path) {
- d = ap_get_core_module_config(r->per_dir_config);
if (d->allow_encoded_slashes) {
- access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes);
+ access_status = ap_unescape_url_keep2f(r->parsed_uri.path,
+ d->decode_encoded_slashes);
}
else {
access_status = ap_unescape_url(r->parsed_uri.path);
@@ -183,40 +253,27 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if (access_status == HTTP_NOT_FOUND) {
if (! d->allow_encoded_slashes) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
- "found %%2f (encoded '/') in URI "
- "(decoded='%s'), returning 404",
- r->parsed_uri.path);
+ "found %%2f (encoded '/') in URI path (%s), "
+ "returning 404", r->unparsed_uri);
}
}
return access_status;
}
- }
- ap_getparents(r->uri); /* OK --- shrinking transformations... */
- if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
- ap_no2slash(r->uri);
- if (r->parsed_uri.path) {
- ap_no2slash(r->parsed_uri.path);
+ if (d->allow_encoded_slashes && d->decode_encoded_slashes) {
+ /* Decoding slashes might have created new // or /./ or /../
+ * segments (e.g. "/.%2F/"), so re-normalize.
+ */
+ ap_normalize_path(r->parsed_uri.path, normalize_flags);
}
- }
+ }
- /* All file subrequests are a huge pain... they cannot bubble through the
- * next several steps. Only file subrequests are allowed an empty uri,
- * otherwise let translate_name kill the request.
- */
+ /* Same, translate_name is not suited for file subrequests */
if (!file_req) {
- if ((access_status = ap_location_walk(r))) {
- return access_status;
- }
- if ((access_status = ap_if_walk(r))) {
+ if ((access_status = walk_location_and_if(r))) {
return access_status;
}
- d = ap_get_core_module_config(r->per_dir_config);
- if (d->log) {
- r->log = d->log;
- }
-
if ((access_status = ap_run_translate_name(r))) {
return decl_die(access_status, "translate", r);
}
@@ -233,17 +290,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
/* Rerun the location walk, which overrides any map_to_storage config.
*/
- if ((access_status = ap_location_walk(r))) {
+ if ((access_status = walk_location_and_if(r))) {
return access_status;
}
- if ((access_status = ap_if_walk(r))) {
- return access_status;
- }
-
- d = ap_get_core_module_config(r->per_dir_config);
- if (d->log) {
- r->log = d->log;
- }
if ((access_status = ap_run_post_perdir_config(r))) {
return access_status;
@@ -1370,7 +1419,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
r->canonical_filename = r->filename;
if (r->finfo.filetype == APR_DIR) {
- cache->cached = r->filename;
+ cache->cached = apr_pstrdup(r->pool, r->filename);
}
else {
cache->cached = ap_make_dirstr_parent(r->pool, r->filename);
@@ -1467,7 +1516,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
apr_pool_t *rxpool = NULL;
cached &= auth_internal_per_conf;
- cache->cached = entry_uri;
+ cache->cached = apr_pstrdup(r->pool, entry_uri);
/* Go through the location entries, and check for matches.
* We apply the directive sections in given order, we should
diff --git a/server/util.c b/server/util.c
index 90ee58190d..143c8fc69a 100644
--- a/server/util.c
+++ b/server/util.c
@@ -76,7 +76,7 @@
#include "test_char.h"
/* Win32/NetWare/OS2 need to check for both forward and back slashes
- * in ap_getparents() and ap_escape_url.
+ * in ap_normalize_path() and ap_escape_url().
*/
#ifdef CASE_BLIND_FILESYSTEM
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
@@ -491,75 +491,127 @@ AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
return rc;
}
+/* Forward declare */
+static char x2c(const char *what);
+
+#define IS_SLASH_OR_NUL(s) (s == '\0' || IS_SLASH(s))
+
/*
- * Parse .. so we don't compromise security
+ * Inspired by mod_jk's jk_servlet_normalize().
*/
-AP_DECLARE(void) ap_getparents(char *name)
+AP_DECLARE(int) ap_normalize_path(char *path, unsigned int flags)
{
- char *next;
- int l, w, first_dot;
+ int ret = 1;
+ apr_size_t l = 1, w = 1;
- /* Four paseses, as per RFC 1808 */
- /* a) remove ./ path segments */
- for (next = name; *next && (*next != '.'); next++) {
- }
+ if (!IS_SLASH(path[0])) {
+ /* Besides "OPTIONS *", a request-target should start with '/'
+ * per RFC 7230 section 5.3, so anything else is invalid.
+ */
+ if (path[0] == '*' && path[1] == '\0') {
+ return 1;
+ }
+ /* However, AP_NORMALIZE_ALLOW_RELATIVE can be used to bypass
+ * this restriction (e.g. for subrequest file lookups).
+ */
+ if (!(flags & AP_NORMALIZE_ALLOW_RELATIVE) || path[0] == '\0') {
+ return 0;
+ }
- l = w = first_dot = next - name;
- while (name[l] != '\0') {
- if (name[l] == '.' && IS_SLASH(name[l + 1])
- && (l == 0 || IS_SLASH(name[l - 1])))
- l += 2;
- else
- name[w++] = name[l++];
+ l = w = 0;
}
- /* b) remove trailing . path, segment */
- if (w == 1 && name[0] == '.')
- w--;
- else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
- w--;
- name[w] = '\0';
+ while (path[l] != '\0') {
+ /* RFC-3986 section 2.3:
+ * For consistency, percent-encoded octets in the ranges of
+ * ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
+ * period (%2E), underscore (%5F), or tilde (%7E) should [...]
+ * be decoded to their corresponding unreserved characters by
+ * URI normalizers.
+ */
+ if ((flags & AP_NORMALIZE_DECODE_UNRESERVED)
+ && path[l] == '%' && apr_isxdigit(path[l + 1])
+ && apr_isxdigit(path[l + 2])) {
+ const char c = x2c(&path[l + 1]);
+ if (apr_isalnum(c) || (c && strchr("-._~", c))) {
+ /* Replace last char and fall through as the current
+ * read position */
+ l += 2;
+ path[l] = c;
+ }
+ }
- /* c) remove all xx/../ segments. (including leading ../ and /../) */
- l = first_dot;
+ if ((flags & AP_NORMALIZE_DROP_PARAMETERS) && path[l] == ';') {
+ do {
+ l++;
+ } while (!IS_SLASH_OR_NUL(path[l]));
+ continue;
+ }
- while (name[l] != '\0') {
- if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
- && (l == 0 || IS_SLASH(name[l - 1]))) {
- int m = l + 3, n;
+ if (w == 0 || IS_SLASH(path[w - 1])) {
+ /* Collapse ///// sequences to / */
+ if ((flags & AP_NORMALIZE_MERGE_SLASHES) && IS_SLASH(path[l])) {
+ do {
+ l++;
+ } while (IS_SLASH(path[l]));
+ continue;
+ }
- l = l - 2;
- if (l >= 0) {
- while (l >= 0 && !IS_SLASH(name[l]))
- l--;
- l++;
+ if (path[l] == '.') {
+ /* Remove /./ segments */
+ if (IS_SLASH_OR_NUL(path[l + 1])) {
+ l++;
+ if (path[l]) {
+ l++;
+ }
+ continue;
+ }
+
+ /* Remove /xx/../ segments */
+ if (path[l + 1] == '.' && IS_SLASH_OR_NUL(path[l + 2])) {
+ /* Wind w back to remove the previous segment */
+ if (w > 1) {
+ do {
+ w--;
+ } while (w && !IS_SLASH(path[w - 1]));
+ }
+ else {
+ /* Already at root, ignore and return a failure
+ * if asked to.
+ */
+ if (flags & AP_NORMALIZE_NOT_ABOVE_ROOT) {
+ ret = 0;
+ }
+ }
+
+ /* Move l forward to the next segment */
+ l += 2;
+ if (path[l]) {
+ l++;
+ }
+ continue;
+ }
}
- else
- l = 0;
- n = l;
- while ((name[n] = name[m]))
- (++n, ++m);
}
- else
- ++l;
+
+ path[w++] = path[l++];
}
+ path[w] = '\0';
+
+ return ret;
+}
- /* d) remove trailing xx/.. segment. */
- if (l == 2 && name[0] == '.' && name[1] == '.')
+/*
+ * Parse .. so we don't compromise security
+ */
+AP_DECLARE(void) ap_getparents(char *name)
+{
+ if (!ap_normalize_path(name, AP_NORMALIZE_NOT_ABOVE_ROOT |
+ AP_NORMALIZE_ALLOW_RELATIVE)) {
name[0] = '\0';
- else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
- && IS_SLASH(name[l - 3])) {
- l = l - 4;
- if (l >= 0) {
- while (l >= 0 && !IS_SLASH(name[l]))
- l--;
- l++;
- }
- else
- l = 0;
- name[l] = '\0';
}
}
+
AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
{