2017-03-09 22:47 GMT+01:00 Luca Toscano <toscano.l...@gmail.com>: > > > 2017-02-24 23:27 GMT+01:00 Luca Toscano <toscano.l...@gmail.com>: > >> Hi everybody, >> >> the following users@ question is interesting in my opinion: >> >> 2017-02-20 18:17 GMT+01:00 Mike Schlottman <mschl...@spe.org>: >> >>> >>> The problem comes when I combine these 2 so that all users except those >>> coming from 127.*.*.* or 192.168.*.* see the nice error page. >>> >>> <If "! %{HTTP:X-Real-IP} -ipmatch '127.0.0.0/8' "> >>> >>> <If "! %{HTTP:X-Real-IP} -ipmatch '192.168.0.0/16' "> >>> >>> ErrorDocument 404 /errors/404 >>> >>> </If> >>> >>> </If> >>> >>> The user from 172.28.1.84 does not get the nice 404 page, but the >>> default 404 page. The IP does not match either of the ranges as observed >>> when using the ranges individually, but when combined in this way it does >>> not work as expected. >>> >>> >>> >>> Any ideas why this is? >>> >>> >>> >> >> He ended up using a single if with an expression composed by the two >> conditions in && to solve the problem, but I started to wonder why this >> configuration does not work. I tested the "nested ifs" config with trace8 >> logging and it seem that only the outermost <If> expression gets evaluated. >> >> Is there a specific reason why this happens? I'd expect two possible >> outcomes from this configuration, namely either a syntax error while >> parsing the config (preferred imho) or a context merge, but not a no-op. >> >> Any pointers/suggestions about where to look? >> >> > I tried to investigate a bit the problem to write a good explanation in > the docs (and also to better understand the httpd internals for fun) but I > am still far from a satisfactory result. > > I tried the following config (inside other containers like Location too): > > <If "true"> > ErrorDocument 503 "This is a custom 503 inside the outer IF!" > <If "true"> > ErrorDocument 503 "This is a custom 503 inside the inner IF!" > </If> > </If> > > Each time that 503 is generated, I get the "outer IF" 503 response, never > the inner one. > > With gdb it is easy to see that when ap_if_walk is executed, > dconf->sec_if->nelts is 1, so only one of the Ifs is evaluated. > > So I checked how the if sections are parsed and built > with > ap_process_config_tree->ap_walk_config->ap_walk_config_sub->invoke_cmd->ifsection > and eventually ap_add_if, but I only got that the parent/child relationship > is set up correctly. > > My only goal is to figure out if nested Ifs are not evaluated on purpose > and if so why they are allowed by the syntax checker. Eventually it would > be really great to update the docs (regular user docs and also > https://httpd.apache.org/docs/trunk/developer/request.html). > > This time (hopefully) I think I found what I was looking for, namely why nested <If> blocks are definitely not going to work.
The ap_if_walk (request.c) function gets the core_dir_config settings via ap_get_core_module_config(r->per_dir_config), retrieving all the <If> containers using the sec_if field, and then iterating through them to evaluate their (apr_expr) conditions. In order to be able to use nested <If> blocks ap_if_walk would need to check, for each sec_if->elts element of the core's r->per_dir_config, if it contains a sec_if blocks too (and restart the process of checking its condition etc..). Something like: AP_DECLARE(int) ap_if_walk(request_rec *r) { [..] /* Go through the if entries, and check for matches */ for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) { core_dir_config *entry_core; entry_core = ap_get_core_module_config(sec_ent[sec_idx]); if(entry_core->sec_if->nelts > 0) { .. /* handling nested <If>s */ .. } [..] In this way we would allow arbitrary nesting of <If> blocks in the httpd config. I am not advocating for this solution but we should either allow nested <If> blocks or explicitly warn the users when httpd checks its config that a nested configuration will be silently ignored (emitting an explicit configuration error while parsing the config would be even better but probably too invasive for 2.4.x releases). Thoughts? Luca