blob: 118c6150f9ea493388f9dc9953e057fde24c1db5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***************************************************************************
* Description: URI to worker map object. *
* *
* Author: Gal Shachor <shachor@il.ibm.com> *
* Author: Mladen Turk <mturk@apache.org> *
***************************************************************************/
#include "jk_pool.h"
#include "jk_util.h"
#include "jk_map.h"
#include "jk_mt.h"
#include "jk_uri_worker_map.h"
#include "jk_worker.h"
#include "jk_lb_worker.h"
#ifdef WIN32
#define JK_STRCMP strcasecmp
#define JK_STRNCMP strnicmp
#else
#define JK_STRCMP strcmp
#define JK_STRNCMP strncmp
#endif
#define JK_UWMAP_EXTENSION_REPLY_TIMEOUT "reply_timeout="
#define JK_UWMAP_EXTENSION_STICKY_IGNORE "sticky_ignore="
#define JK_UWMAP_EXTENSION_STATELESS "stateless="
#define JK_UWMAP_EXTENSION_ACTIVE "active="
#define JK_UWMAP_EXTENSION_DISABLED "disabled="
#define JK_UWMAP_EXTENSION_STOPPED "stopped="
#define JK_UWMAP_EXTENSION_FAIL_ON_STATUS "fail_on_status="
#define JK_UWMAP_EXTENSION_USE_SRV_ERRORS "use_server_errors="
#define JK_UWMAP_EXTENSION_SESSION_COOKIE "session_cookie="
#define JK_UWMAP_EXTENSION_SESSION_PATH "session_path="
#define JK_UWMAP_EXTENSION_SET_SESSION_COOKIE "set_session_cookie="
#define JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH "session_cookie_path="
#define IND_SWITCH(x) (((x)+1) % 2)
#define IND_THIS(x) ((x)[uw_map->index])
#define IND_NEXT(x) ((x)[IND_SWITCH(uw_map->index)])
#define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
static volatile int map_id_counter = 0;
static const char *uri_worker_map_source_type[] = {
"unknown",
SOURCE_TYPE_TEXT_WORKERDEF,
SOURCE_TYPE_TEXT_JKMOUNT,
SOURCE_TYPE_TEXT_URIMAP,
SOURCE_TYPE_TEXT_DISCOVER,
NULL
};
/* Return the string representation of the uwr source */
const char *uri_worker_map_get_source(uri_worker_record_t *uwr)
{
return uri_worker_map_source_type[uwr->source_type];
}
/* Return the string representation of the uwr match type */
char *uri_worker_map_get_match(uri_worker_record_t *uwr, char *buf)
{
unsigned int match;
buf[0] = '\0';
match = uwr->match_type;
if (match & MATCH_TYPE_DISABLED)
strcat(buf, "Disabled ");
/* deprecated
if (match & MATCH_TYPE_STOPPED)
strcat(buf, "Stopped ");
*/
if (match & MATCH_TYPE_NO_MATCH)
strcat(buf, "Unmount ");
if (match & MATCH_TYPE_EXACT)
strcat(buf, "Exact");
else if (match & MATCH_TYPE_WILDCHAR_PATH)
strcat(buf, "Wildchar");
/* deprecated
else if (match & MATCH_TYPE_CONTEXT)
strcat(buf, "Context");
else if (match & MATCH_TYPE_CONTEXT_PATH)
strcat(buf, "Context Path");
else if (match & MATCH_TYPE_SUFFIX)
strcat(buf, "Suffix");
else if (match & MATCH_TYPE_GENERAL_SUFFIX)
return "General Suffix";
*/
else
strcat(buf, "Unknown");
return buf;
}
/*
* Given context uri, count the number of path tokens.
*
* Servlet specification 2.4, SRV.11.1 says
* The container will recursively try tomatch the longest
* path-prefix. This is done by stepping down the path tree a
* directory at a time, using the / character as a path
* separator. The longest match determines the servlet selected.
*
* The implication seems to be `most uri path elements is most exact'.
* This is a little helper function to count uri tokens, so we can
* keep the worker map sorted with most specific first.
*/
static int worker_count_context_uri_tokens(const char * context)
{
const char * c = context;
int count = 0;
while (c && *c) {
if ('/' == *c++)
count++;
}
return count;
}
static int worker_compare(const void *elem1, const void *elem2)
{
uri_worker_record_t *e1 = *(uri_worker_record_t **)elem1;
uri_worker_record_t *e2 = *(uri_worker_record_t **)elem2;
int e1_tokens = 0;
int e2_tokens = 0;
e1_tokens = worker_count_context_uri_tokens(e1->context);
e2_tokens = worker_count_context_uri_tokens(e2->context);
if (e1_tokens != e2_tokens) {
return (e2_tokens - e1_tokens);
}
/* given the same number of URI tokens, use character
* length as a tie breaker
*/
if(e2->context_len != e1->context_len)
return ((int)e2->context_len - (int)e1->context_len);
return ((int)e2->source_type - (int)e1->source_type);
}
static void worker_qsort(jk_uri_worker_map_t *uw_map)
{
/* Sort remaining args using Quicksort algorithm: */
qsort((void *)IND_NEXT(uw_map->maps), IND_NEXT(uw_map->size),
sizeof(uri_worker_record_t *), worker_compare);
}
/* Dump the map contents - only call if debug log is active. */
static void uri_worker_map_dump(jk_uri_worker_map_t *uw_map,
const char *reason, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (uw_map) {
int i, off;
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG, "uri map dump %s: id=%d, index=%d file='%s' reject_unsafe=%d "
"reload=%d modified=%d checked=%d",
reason, uw_map->id, uw_map->index, STRNULL_FOR_NULL(uw_map->fname),
uw_map->reject_unsafe, uw_map->reload, uw_map->modified, uw_map->checked);
}
for (i = 0; i <= 1; i++) {
jk_log(l, JK_LOG_DEBUG, "generation %d: size=%d nosize=%d capacity=%d",
i, uw_map->size[i], uw_map->nosize[i], uw_map->capacity[i], uw_map->maps[i]);
}
off = uw_map->index;
for (i = 0; i <= 1; i++) {
char buf[32];
int k;
unsigned int j;
k = (i + off) % 2;
for (j = 0; j < uw_map->size[k]; j++) {
uri_worker_record_t *uwr = uw_map->maps[k][j];
if (uwr && JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG, "%s (%d) map #%d: uri=%s worker=%s context=%s "
"source=%s type=%s len=%d",
i ? "NEXT" : "THIS", i, j,
STRNULL_FOR_NULL(uwr->uri), STRNULL_FOR_NULL(uwr->worker_name),
STRNULL_FOR_NULL(uwr->context), STRNULL_FOR_NULL(uri_worker_map_get_source(uwr)),
STRNULL_FOR_NULL(uri_worker_map_get_match(uwr, buf)), uwr->context_len);
}
}
}
}
JK_TRACE_EXIT(l);
}
int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map_p,
jk_map_t *init_data, jk_logger_t *l)
{
int i;
JK_TRACE_ENTER(l);
if (uw_map_p) {
int rc;
jk_uri_worker_map_t *uw_map;
*uw_map_p = (jk_uri_worker_map_t *)calloc(1, sizeof(jk_uri_worker_map_t));
uw_map = *uw_map_p;
JK_INIT_CS(&(uw_map->cs), rc);
if (rc == JK_FALSE) {
jk_log(l, JK_LOG_ERROR,
"creating thread lock (errno=%d)",
errno);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
jk_open_pool(&(uw_map->p),
uw_map->buf, sizeof(jk_pool_atom_t) * BIG_POOL_SIZE);
for (i = 0; i <= 1; i++) {
jk_open_pool(&(uw_map->p_dyn[i]),
uw_map->buf_dyn[i], sizeof(jk_pool_atom_t) * BIG_POOL_SIZE);
uw_map->size[i] = 0;
uw_map->nosize[i] = 0;
uw_map->capacity[i] = 0;
uw_map->maps[i] = NULL;
}
uw_map->id = 0;
uw_map->index = 0;
uw_map->fname = NULL;
uw_map->reject_unsafe = 0;
uw_map->reload = JK_URIMAP_DEF_RELOAD;
uw_map->modified = 0;
uw_map->checked = 0;
if (init_data)
rc = uri_worker_map_open(uw_map, init_data, l);
if (rc == JK_TRUE)
uw_map->id = ++map_id_counter;
JK_TRACE_EXIT(l);
return rc;
}
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
static int uri_worker_map_close(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (uw_map) {
JK_DELETE_CS(&uw_map->cs);
jk_close_pool(&uw_map->p_dyn[0]);
jk_close_pool(&uw_map->p_dyn[1]);
jk_close_pool(&uw_map->p);
JK_TRACE_EXIT(l);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
int uri_worker_map_free(jk_uri_worker_map_t **uw_map, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (uw_map && *uw_map) {
uri_worker_map_close(*uw_map, l);
free(*uw_map);
*uw_map = NULL;
JK_TRACE_EXIT(l);
return JK_TRUE;
}
else
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
/*
* Ensure there will be memory in context info to store Context Bases
*/
#define UW_INC_SIZE 4 /* 4 URI->WORKER STEP */
static int uri_worker_map_realloc(jk_uri_worker_map_t *uw_map)
{
if (IND_NEXT(uw_map->size) == IND_NEXT(uw_map->capacity)) {
uri_worker_record_t **uwr;
int capacity = IND_NEXT(uw_map->capacity) + UW_INC_SIZE;
uwr =
(uri_worker_record_t **) jk_pool_alloc(&IND_NEXT(uw_map->p_dyn),
sizeof(uri_worker_record_t
*) * capacity);
if (!uwr)
return JK_FALSE;
if (IND_NEXT(uw_map->capacity) && IND_NEXT(uw_map->maps))
memcpy(uwr, IND_NEXT(uw_map->maps),
sizeof(uri_worker_record_t *) * IND_NEXT(uw_map->capacity));
IND_NEXT(uw_map->maps) = uwr;
IND_NEXT(uw_map->capacity) = capacity;
}
return JK_TRUE;
}
/*
* Delete all entries of a given source type
*/
static int uri_worker_map_clear(jk_uri_worker_map_t *uw_map,
jk_logger_t *l)
{
uri_worker_record_t *uwr = NULL;
unsigned int i;
unsigned int new_size = 0;
unsigned int new_nosize = 0;
JK_TRACE_ENTER(l);
IND_NEXT(uw_map->maps) =
(uri_worker_record_t **) jk_pool_alloc(&(IND_NEXT(uw_map->p_dyn)),
sizeof(uri_worker_record_t
*) * IND_THIS(uw_map->size));
IND_NEXT(uw_map->capacity) = IND_THIS(uw_map->size);
IND_NEXT(uw_map->size) = 0;
IND_NEXT(uw_map->nosize) = 0;
for (i = 0; i < IND_THIS(uw_map->size); i++) {
uwr = IND_THIS(uw_map->maps)[i];
if (uwr->source_type == SOURCE_TYPE_URIMAP) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"deleting map rule '%s=%s' source '%s'",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
}
else {
IND_NEXT(uw_map->maps)[new_size] = uwr;
new_size++;
if (uwr->match_type & MATCH_TYPE_NO_MATCH)
new_nosize++;
}
}
IND_NEXT(uw_map->size) = new_size;
IND_NEXT(uw_map->nosize) = new_nosize;
JK_TRACE_EXIT(l);
return JK_TRUE;
}
static void extract_activation(jk_pool_t *p,
lb_worker_t *lb,
int *activations,
char *workers,
int activation,
jk_logger_t *l)
{
unsigned int i;
char *worker;
#ifdef _MT_CODE_PTHREAD
char *lasts;
#endif
JK_TRACE_ENTER(l);
worker = jk_pool_strdup(p, workers);
#ifdef _MT_CODE_PTHREAD
for (worker = strtok_r(worker, ", ", &lasts);
worker; worker = strtok_r(NULL, ", ", &lasts)) {
#else
for (worker = strtok(worker, ", "); worker; worker = strtok(NULL, ", ")) {
#endif
for (i = 0; i < lb->num_of_workers; i++) {
if (!strcmp(worker, lb->lb_workers[i].name)) {
if (activations[i] != JK_LB_ACTIVATION_UNSET)
jk_log(l, JK_LOG_WARNING,
"inconsistent activation overwrite for member %s "
"of load balancer %s: '%s' replaced by '%s'",
worker, lb->name,
jk_lb_get_activation_direct(activations[i], l),
jk_lb_get_activation_direct(activation, l));
activations[i] = activation;
break;
}
}
if (i >= lb->num_of_workers)
jk_log(l, JK_LOG_WARNING,
"could not find member %s of load balancer %s",
worker, lb->name);
}
JK_TRACE_EXIT(l);
}
static void extension_fix_fail_on_status(jk_pool_t *p,
const char *name,
rule_extension_t *extensions,
jk_logger_t *l)
{
unsigned int i;
int j;
int cnt = 1;
size_t status_len;
char *status;
#ifdef _MT_CODE_PTHREAD
char *lasts;
#endif
JK_TRACE_ENTER(l);
status_len = strlen(extensions->fail_on_status_str);
for (i = 0; i < status_len; i++) {
if (extensions->fail_on_status_str[i] == ',' ||
extensions->fail_on_status_str[i] == ' ')
cnt++;
}
extensions->fail_on_status_size = cnt;
status = jk_pool_strdup(p, extensions->fail_on_status_str);
extensions->fail_on_status = (int *)jk_pool_alloc(p,
extensions->fail_on_status_size * sizeof(int));
if (!extensions->fail_on_status) {
jk_log(l, JK_LOG_ERROR,
"can't alloc extensions fail_on_status list for worker (%s)",
name);
JK_TRACE_EXIT(l);
return;
} else if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Allocated fail_on_status array of size %d for worker (%s)",
extensions->fail_on_status_size, name);
for (j=0; j<extensions->fail_on_status_size; j++) {
extensions->fail_on_status[j] = 0;
}
cnt = 0;
#ifdef _MT_CODE_PTHREAD
for (status = strtok_r(status, ", ", &lasts);
status; status = strtok_r(NULL, ", ", &lasts)) {
#else
for (status = strtok(status, ", "); status; status = strtok(NULL, ", ")) {
#endif
extensions->fail_on_status[cnt] = atoi(status);
cnt++;
}
JK_TRACE_EXIT(l);
}
static int extension_fix_activation(jk_pool_t *p, const char *name, jk_worker_t *jw,
rule_extension_t *extensions, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Checking extension for worker %s of type %s (%d)",
name, wc_get_name_for_type(jw->type,l), jw->type);
if (jw->type == JK_LB_WORKER_TYPE &&
(extensions->active || extensions->disabled || extensions->stopped)) {
int j;
lb_worker_t *lb = (lb_worker_t *)jw->worker_private;
if (!extensions->activation) {
extensions->activation_size = lb->num_of_workers;
extensions->activation = (int *)jk_pool_alloc(p,
extensions->activation_size * sizeof(int));
if (!extensions->activation) {
jk_log(l, JK_LOG_ERROR,
"can't alloc extensions activation list");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
else if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Allocated activations array of size %d for lb worker %s",
extensions->activation_size, name);
for (j=0; j<extensions->activation_size; j++) {
extensions->activation[j] = JK_LB_ACTIVATION_UNSET;
}
}
if (extensions->active)
extract_activation(p, lb, extensions->activation,
extensions->active, JK_LB_ACTIVATION_ACTIVE, l);
if (extensions->disabled)
extract_activation(p, lb, extensions->activation,
extensions->disabled, JK_LB_ACTIVATION_DISABLED, l);
if (extensions->stopped)
extract_activation(p, lb, extensions->activation,
extensions->stopped, JK_LB_ACTIVATION_STOPPED, l);
}
else if (extensions->active) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, activation extension "
JK_UWMAP_EXTENSION_ACTIVE " for %s ignored",
name, extensions->active);
}
else if (extensions->disabled) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, activation extension "
JK_UWMAP_EXTENSION_DISABLED " for %s ignored",
name, extensions->disabled);
}
else if (extensions->stopped) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, activation extension "
JK_UWMAP_EXTENSION_STOPPED " for %s ignored",
name, extensions->stopped);
}
JK_TRACE_EXIT(l);
return JK_TRUE;
}
static void extension_fix_session(jk_pool_t *p, const char *name, jk_worker_t *jw,
rule_extension_t *extensions, jk_logger_t *l)
{
if (jw->type != JK_LB_WORKER_TYPE && extensions->session_cookie) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, extension "
JK_UWMAP_EXTENSION_SESSION_COOKIE " for %s ignored",
name, extensions->session_cookie);
}
if (jw->type != JK_LB_WORKER_TYPE && extensions->session_path) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, extension "
JK_UWMAP_EXTENSION_SESSION_PATH " for %s ignored",
name, extensions->session_path);
}
if (jw->type != JK_LB_WORKER_TYPE && extensions->set_session_cookie) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, extension "
JK_UWMAP_EXTENSION_SET_SESSION_COOKIE " for %s ignored",
name, extensions->set_session_cookie ? "'true'" : "'false'");
}
if (jw->type != JK_LB_WORKER_TYPE && extensions->session_cookie_path) {
jk_log(l, JK_LOG_WARNING,
"Worker %s is not of type lb, extension "
JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH " for %s ignored",
name, extensions->session_cookie_path);
}
}
void extension_fix(jk_pool_t *p, const char *name,
rule_extension_t *extensions, jk_logger_t *l)
{
jk_worker_t *jw = wc_get_worker_for_name(name, l);
if(!jw) {
jk_log(l, JK_LOG_ERROR,
"Could not find worker with name '%s' in uri map post processing.",
name);
return;
}
if (!extension_fix_activation(p, name, jw, extensions, l))
return;
if (extensions->fail_on_status_str) {
extension_fix_fail_on_status(p, name, extensions, l);
}
extension_fix_session(p, name, jw, extensions, l);
}
void uri_worker_map_switch(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
{
int new_index;
JK_TRACE_ENTER(l);
if (uw_map) {
new_index = IND_SWITCH(uw_map->index);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Switching uri worker map from index %d to index %d",
uw_map->index, new_index);
uw_map->index = new_index;
jk_reset_pool(&(IND_NEXT(uw_map->p_dyn)));
}
JK_TRACE_EXIT(l);
}
void uri_worker_map_ext(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
{
unsigned int i;
JK_TRACE_ENTER(l);
for (i = 0; i < IND_NEXT(uw_map->size); i++) {
uri_worker_record_t *uwr = IND_NEXT(uw_map->maps)[i];
jk_pool_t *p;
if(uwr->match_type & MATCH_TYPE_NO_MATCH)
continue;
if (uwr->source_type == SOURCE_TYPE_URIMAP)
p = &IND_NEXT(uw_map->p_dyn);
else
p = &uw_map->p;
extension_fix(p, uwr->worker_name, &uwr->extensions, l);
}
if (JK_IS_DEBUG_LEVEL(l))
uri_worker_map_dump(uw_map, "after extension stripping", l);
JK_TRACE_EXIT(l);
return;
}
/* Parse rule extensions */
void parse_rule_extensions(char *rule, rule_extension_t *extensions,
jk_logger_t *l)
{
char *param;
#ifdef _MT_CODE_PTHREAD
char *lasts = NULL;
#endif
extensions->reply_timeout = -1;
extensions->sticky_ignore = JK_FALSE;
extensions->stateless = JK_FALSE;
extensions->active = NULL;
extensions->disabled = NULL;
extensions->stopped = NULL;
extensions->activation_size = 0;
extensions->activation = NULL;
extensions->fail_on_status_size = 0;
extensions->fail_on_status = NULL;
extensions->fail_on_status_str = NULL;
extensions->use_server_error_pages = 0;
extensions->session_cookie = NULL;
extensions->session_path = NULL;
extensions->set_session_cookie = JK_FALSE;
extensions->session_cookie_path = NULL;
#ifdef _MT_CODE_PTHREAD
param = strtok_r(rule, ";", &lasts);
#else
param = strtok(rule, ";");
#endif
if (param) {
#ifdef _MT_CODE_PTHREAD
for (param = strtok_r(NULL, ";", &lasts); param; param = strtok_r(NULL, ";", &lasts)) {
#else
for (param = strtok(NULL, ";"); param; param = strtok(NULL, ";")) {
#endif
if (!strncmp(param, JK_UWMAP_EXTENSION_REPLY_TIMEOUT, strlen(JK_UWMAP_EXTENSION_REPLY_TIMEOUT))) {
extensions->reply_timeout = atoi(param + strlen(JK_UWMAP_EXTENSION_REPLY_TIMEOUT));
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_STICKY_IGNORE, strlen(JK_UWMAP_EXTENSION_STICKY_IGNORE))) {
int val = atoi(param + strlen(JK_UWMAP_EXTENSION_STICKY_IGNORE));
if (val) {
extensions->sticky_ignore = JK_TRUE;
}
else {
extensions->sticky_ignore = JK_FALSE;
}
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_STATELESS, strlen(JK_UWMAP_EXTENSION_STATELESS))) {
int val = atoi(param + strlen(JK_UWMAP_EXTENSION_STATELESS));
if (val) {
extensions->stateless = JK_TRUE;
}
else {
extensions->stateless = JK_FALSE;
}
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_USE_SRV_ERRORS, strlen(JK_UWMAP_EXTENSION_USE_SRV_ERRORS))) {
extensions->use_server_error_pages = atoi(param + strlen(JK_UWMAP_EXTENSION_USE_SRV_ERRORS));
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_ACTIVE, strlen(JK_UWMAP_EXTENSION_ACTIVE))) {
if (extensions->active)
jk_log(l, JK_LOG_WARNING,
"rule extension '" JK_UWMAP_EXTENSION_ACTIVE "' only allowed once");
else
extensions->active = param + strlen(JK_UWMAP_EXTENSION_ACTIVE);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_DISABLED, strlen(JK_UWMAP_EXTENSION_DISABLED))) {
if (extensions->disabled)
jk_log(l, JK_LOG_WARNING,
"rule extension '" JK_UWMAP_EXTENSION_DISABLED "' only allowed once");
else
extensions->disabled = param + strlen(JK_UWMAP_EXTENSION_DISABLED);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_STOPPED, strlen(JK_UWMAP_EXTENSION_STOPPED))) {
if (extensions->stopped)
jk_log(l, JK_LOG_WARNING,
"rule extension '" JK_UWMAP_EXTENSION_STOPPED "' only allowed once");
else
extensions->stopped = param + strlen(JK_UWMAP_EXTENSION_STOPPED);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_FAIL_ON_STATUS, strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS))) {
if (extensions->fail_on_status_str)
jk_log(l, JK_LOG_WARNING,
"rule extension '" JK_UWMAP_EXTENSION_FAIL_ON_STATUS "' only allowed once");
else
extensions->fail_on_status_str = param + strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_COOKIE, strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE))) {
if (extensions->session_cookie)
jk_log(l, JK_LOG_WARNING,
"extension '" JK_UWMAP_EXTENSION_SESSION_COOKIE "' in uri worker map only allowed once");
else
extensions->session_cookie = param + strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE);
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_PATH, strlen(JK_UWMAP_EXTENSION_SESSION_PATH))) {
if (extensions->session_path)
jk_log(l, JK_LOG_WARNING,
"extension '" JK_UWMAP_EXTENSION_SESSION_PATH "' in uri worker map only allowed once");
else {
// Check if the session identifier starts with semicolon.
if (!strcmp(param, JK_UWMAP_EXTENSION_SESSION_PATH)) {
#ifdef _MT_CODE_PTHREAD
param = strtok_r(NULL, ";", &lasts);
#else
param = strtok(NULL, ";");
#endif
extensions->session_path = param;
} else
extensions->session_path = param + strlen(JK_UWMAP_EXTENSION_SESSION_PATH);
}
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SET_SESSION_COOKIE, strlen(JK_UWMAP_EXTENSION_SET_SESSION_COOKIE))) {
if (extensions->set_session_cookie)
jk_log(l, JK_LOG_WARNING,
"extension '" JK_UWMAP_EXTENSION_SET_SESSION_COOKIE "' in uri worker map only allowed once");
else {
int val = atoi(param + strlen(JK_UWMAP_EXTENSION_SET_SESSION_COOKIE));
if (val) {
extensions->set_session_cookie = JK_TRUE;
}
else {
extensions->set_session_cookie = JK_FALSE;
}
}
}
else if (!strncmp(param, JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH, strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH))) {
if (extensions->session_cookie_path)
jk_log(l, JK_LOG_WARNING,
"extension '" JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH "' in uri worker map only allowed once");
else
extensions->session_cookie_path = param + strlen(JK_UWMAP_EXTENSION_SESSION_COOKIE_PATH);
}
else {
jk_log(l, JK_LOG_WARNING,
"unknown rule extension '%s'",
param);
}
}
}
}
/* Add new entry to NEXT generation */
int uri_worker_map_add(jk_uri_worker_map_t *uw_map,
const char *puri, const char *worker,
unsigned int source_type, jk_logger_t *l)
{
uri_worker_record_t *uwr = NULL;
char *uri;
jk_pool_t *p;
unsigned int match_type = 0;
JK_TRACE_ENTER(l);
if (*puri == '-') {
/* Disable urimap.
* This way you can disable already mounted
* context.
*/
match_type = MATCH_TYPE_DISABLED;
puri++;
}
if (*puri == '!') {
match_type |= MATCH_TYPE_NO_MATCH;
puri++;
}
if (uri_worker_map_realloc(uw_map) == JK_FALSE) {
JK_TRACE_EXIT(l);
return JK_FALSE;
}
if (source_type == SOURCE_TYPE_URIMAP)
p = &IND_NEXT(uw_map->p_dyn);
else
p = &uw_map->p;
uwr = (uri_worker_record_t *)jk_pool_alloc(p, sizeof(uri_worker_record_t));
if (!uwr) {
jk_log(l, JK_LOG_ERROR,
"can't alloc map entry");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
uri = jk_pool_strdup(p, puri);
if (!uri || !worker) {
jk_log(l, JK_LOG_ERROR,
"can't alloc uri/worker strings");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
if (*uri == '/') {
char *w = jk_pool_strdup(p, worker);
parse_rule_extensions(w, &uwr->extensions, l);
uwr->source_type = source_type;
uwr->worker_name = w;
uwr->uri = uri;
uwr->context = uri;
uwr->context_len = strlen(uwr->context);
if (strchr(uri, '*') ||
strchr(uri, '?')) {
/* Something like
* /context/ * /user/ *
* /context/ *.suffix
*/
match_type |= MATCH_TYPE_WILDCHAR_PATH;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"wildchar rule '%s=%s' source '%s' was added",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
}
else {
/* Something like: JkMount /login/j_security_check ajp13 */
match_type |= MATCH_TYPE_EXACT;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"exact rule '%s=%s' source '%s' was added",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
}
}
else {
/*
* JFC: please check...
* Not sure what to do, but I try to prevent problems.
* I have fixed jk_mount_context() in apaches/mod_jk.c so we should
* not arrive here when using Apache.
*/
jk_log(l, JK_LOG_ERROR,
"invalid context '%s': does not begin with '/'",
uri);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
uwr->match_type = match_type;
IND_NEXT(uw_map->maps)[IND_NEXT(uw_map->size)] = uwr;
IND_NEXT(uw_map->size)++;
if (match_type & MATCH_TYPE_NO_MATCH) {
/* If we split the mappings this one will be calculated */
IND_NEXT(uw_map->nosize)++;
}
worker_qsort(uw_map);
JK_TRACE_EXIT(l);
return JK_TRUE;
}
int uri_worker_map_open(jk_uri_worker_map_t *uw_map,
jk_map_t *init_data, jk_logger_t *l)
{
int rc = JK_TRUE;
JK_TRACE_ENTER(l);
if (uw_map) {
int sz = jk_map_size(init_data);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"rule map size is %d",
sz);
if (sz > 0) {
int i;
for (i = 0; i < sz; i++) {
const char *u = jk_map_name_at(init_data, i);
const char *w = jk_map_value_at(init_data, i);
/* Multiple mappings like :
* /servlets-examples|/ *
* will create two mappings:
* /servlets-examples
* and:
* /servlets-examples/ *
*/
if (strchr(u, '|')) {
char *s, *r = strdup(u);
s = strchr(r, '|');
*(s++) = '\0';
/* Add first mapping */
if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s", r, w);
rc = JK_FALSE;
}
for (; *s; s++)
*(s - 1) = *s;
*(s - 1) = '\0';
/* add second mapping */
if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s", r, w);
rc = JK_FALSE;
}
free(r);
}
else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_JKMOUNT, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s",
u, w);
rc = JK_FALSE;
break;
}
if (rc == JK_FALSE)
break;
}
}
if (rc == JK_FALSE) {
jk_log(l, JK_LOG_ERROR,
"there was an error, freeing buf");
jk_close_pool(&uw_map->p_dyn[0]);
jk_close_pool(&uw_map->p_dyn[1]);
jk_close_pool(&uw_map->p);
}
else if (JK_IS_DEBUG_LEVEL(l))
uri_worker_map_dump(uw_map, "after map open", l);
}
JK_TRACE_EXIT(l);
return rc;
}
static int find_match(jk_uri_worker_map_t *uw_map,
const char *url, jk_logger_t *l)
{
unsigned int i;
JK_TRACE_ENTER(l);
for (i = 0; i < IND_THIS(uw_map->size); i++) {
uri_worker_record_t *uwr = IND_THIS(uw_map->maps)[i];
/* Check for match types */
if ((uwr->match_type & MATCH_TYPE_DISABLED) ||
(uwr->match_type & MATCH_TYPE_NO_MATCH))
continue;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Attempting to map context URI '%s=%s' source '%s'",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
/* Map is already sorted by context_len */
if (jk_wildchar_match(url, uwr->context,
#ifdef WIN32
0
#else
0
#endif
) == 0) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Found a wildchar match '%s=%s'",
uwr->context, uwr->worker_name);
JK_TRACE_EXIT(l);
return i;
}
}
else if (JK_STRNCMP(uwr->context, url, uwr->context_len) == 0) {
if (strlen(url) == uwr->context_len) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Found an exact match '%s=%s'",
uwr->context, uwr->worker_name);
JK_TRACE_EXIT(l);
return i;
}
}
}
JK_TRACE_EXIT(l);
return -1;
}
static int is_nomatch(jk_uri_worker_map_t *uw_map,
const char *uri, int match,
jk_logger_t *l)
{
unsigned int i;
const char *worker = IND_THIS(uw_map->maps)[match]->worker_name;
JK_TRACE_ENTER(l);
for (i = 0; i < IND_THIS(uw_map->size); i++) {
uri_worker_record_t *uwr = IND_THIS(uw_map->maps)[i];
/* Check only nomatch mappings */
if (!(uwr->match_type & MATCH_TYPE_NO_MATCH) ||
(uwr->match_type & MATCH_TYPE_DISABLED))
continue;
/* Check only matching workers */
if (strcmp(uwr->worker_name, worker) &&
strcmp(uwr->worker_name, "*"))
continue;
if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
/* Map is already sorted by context_len */
if (jk_wildchar_match(uri, uwr->context,
#ifdef WIN32
0
#else
0
#endif
) == 0) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Found a wildchar no match '%s=%s' source '%s'",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
JK_TRACE_EXIT(l);
return JK_TRUE;
}
}
else if (JK_STRNCMP(uwr->context, uri, uwr->context_len) == 0) {
if (strlen(uri) == uwr->context_len) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Found an exact no match '%s=%s' source '%s'",
uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr));
JK_TRACE_EXIT(l);
return JK_TRUE;
}
}
}
JK_TRACE_EXIT(l);
return JK_FALSE;
}
const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
const char *uri, const char *vhost,
rule_extension_t **extensions,
int *index, jk_logger_t *l)
{
unsigned int i;
unsigned int vhost_len;
int reject_unsafe;
size_t uri_len;
size_t remain;
int rv = -1;
char url[JK_MAX_URI_LEN+1];
JK_TRACE_ENTER(l);
if (!uw_map || !uri || !extensions) {
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return NULL;
}
*extensions = NULL;
if (index)
*index = -1;
if (*uri != '/') {
if (*uri == '*' && *(uri+1) == '\0') {
/* Most likely an "OPTIONS *" request */
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG,
"Uri %s can't be mapped.", uri);
}
}
else {
jk_log(l, JK_LOG_WARNING,
"Uri %s is invalid. Uri must start with /", uri);
}
JK_TRACE_EXIT(l);
return NULL;
}
if (uw_map->fname) {
uri_worker_map_update(uw_map, 0, l);
if (!IND_THIS(uw_map->size)) {
jk_log(l, JK_LOG_INFO,
"No worker maps defined for %s.",
uw_map->fname);
JK_TRACE_EXIT(l);
return NULL;
}
}
reject_unsafe = uw_map->reject_unsafe;
vhost_len = 0;
/*
* In case we got a vhost, we prepend a slash
* and the vhost to the url in order to enable
* vhost mapping rules especially for IIS.
*/
if (vhost) {
int off = 0;
/* Add leading '/' if necessary */
if (vhost[0] != '/') {
url[0] = '/';
off = 1;
}
/* Size including leading slash. */
vhost_len = (unsigned int)strlen(vhost);
if (vhost_len + off >= JK_MAX_URI_LEN) {
vhost_len = 0;
jk_log(l, JK_LOG_WARNING,
"Host prefix %s for URI %s is invalid and will be ignored."
" It must be smaller than %d chars",
vhost, JK_MAX_URI_LEN - off);
}
else {
strncpy(&url[off], vhost, vhost_len + 1);
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG, "Prefixing mapping uri with vhost '%s'", vhost);
}
}
vhost_len += off;
}
/* Make the copy of the provided uri, check length
* and look for potentially unsafe constructs
*/
uri_len = strlen(uri);
remain = JK_MAX_URI_LEN - vhost_len;
for (i = 0; i < uri_len; i++) {
if (i == remain) {
jk_log(l, JK_LOG_WARNING,
"URI %s is invalid. URI must be smaller than %d chars",
uri, remain);
JK_TRACE_EXIT(l);
return NULL;
}
url[i + vhost_len] = uri[i];
if (reject_unsafe && (uri[i] == '%' || uri[i] == '\\')) {
jk_log(l, JK_LOG_INFO, "Potentially unsafe request url '%s' rejected", uri);
JK_TRACE_EXIT(l);
return NULL;
}
}
url[i + vhost_len] = '\0';
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Attempting to map URI '%s' from %d maps",
url, IND_THIS(uw_map->size));
rv = find_match(uw_map, url, l);
/* If this doesn't find a match, try without the vhost. */
if (rv < 0 && vhost_len) {
rv = find_match(uw_map, &url[vhost_len], l);
}
/* In case we found a match, check for the unmounts. */
if (rv >= 0 && IND_THIS(uw_map->nosize)) {
int rc;
/* Again first including vhost. */
rc = is_nomatch(uw_map, url, rv, l);
/* If no unmount was found, try without vhost. */
if (!rc && vhost_len)
rc = is_nomatch(uw_map, &url[vhost_len], rv, l);
if (rc) {
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG,
"Denying match for worker %s by nomatch rule",
IND_THIS(uw_map->maps)[rv]->worker_name);
}
rv = -1;
}
}
if (rv >= 0) {
*extensions = &(IND_THIS(uw_map->maps)[rv]->extensions);
if (index)
*index = rv;
JK_TRACE_EXIT(l);
return IND_THIS(uw_map->maps)[rv]->worker_name;
}
JK_TRACE_EXIT(l);
return NULL;
}
rule_extension_t *get_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
int index)
{
if (index >= 0) {
return &(IND_THIS(uw_map->maps)[index]->extensions);
}
else {
return NULL;
}
}
int uri_worker_map_load(jk_uri_worker_map_t *uw_map,
jk_logger_t *l)
{
int rc = JK_FALSE;
jk_map_t *map;
jk_map_alloc(&map);
if (jk_map_read_properties(map, NULL, uw_map->fname, &uw_map->modified,
JK_MAP_HANDLE_NORMAL, l)) {
int i;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Loading urimaps from %s with reload check interval %d seconds",
uw_map->fname, uw_map->reload);
uri_worker_map_clear(uw_map, l);
for (i = 0; i < jk_map_size(map); i++) {
const char *u = jk_map_name_at(map, i);
const char *w = jk_map_value_at(map, i);
/* Multiple mappings like :
* /servlets-examples|/ *
* will create two mappings:
* /servlets-examples
* and:
* /servlets-examples/ *
*/
if (strchr(u, '|')) {
char *s, *r = strdup(u);
s = strchr(r, '|');
*(s++) = '\0';
/* Add first mapping */
if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s", r, w);
}
for (; *s; s++)
*(s - 1) = *s;
*(s - 1) = '\0';
/* add second mapping */
if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s", r, w);
}
free(r);
}
else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_URIMAP, l)) {
jk_log(l, JK_LOG_ERROR,
"invalid mapping rule %s->%s",
u, w);
}
}
uw_map->checked = time(NULL);
if (JK_IS_DEBUG_LEVEL(l))
uri_worker_map_dump(uw_map, "after file load", l);
rc = JK_TRUE;
}
else {
jk_log(l, JK_LOG_ERROR, "Failed to load uri_worker_map file %s (errno=%d, err=%s).", uw_map->fname, errno, strerror(errno));
}
jk_map_free(&map);
return rc;
}
int uri_worker_map_update(jk_uri_worker_map_t *uw_map,
int force, jk_logger_t *l)
{
int rc = JK_TRUE;
time_t now = time(NULL);
if (force || (uw_map->reload > 0 && difftime(now, uw_map->checked) >
uw_map->reload)) {
struct stat statbuf;
uw_map->checked = now;
if ((rc = jk_stat(uw_map->fname, &statbuf)) == -1) {
jk_log(l, JK_LOG_ERROR,
"Unable to stat the %s (errno=%d)",
uw_map->fname, errno);
return JK_FALSE;
}
if (statbuf.st_mtime == uw_map->modified) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"File %s is not modified",
uw_map->fname);
return JK_TRUE;
}
JK_ENTER_CS(&uw_map->cs);
/* Check if some other thread updated status */
if (statbuf.st_mtime == uw_map->modified) {
JK_LEAVE_CS(&uw_map->cs);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"File %s is not modified",
uw_map->fname);
return JK_TRUE;
}
rc = uri_worker_map_load(uw_map, l);
uri_worker_map_ext(uw_map, l);
uri_worker_map_switch(uw_map, l);
JK_LEAVE_CS(&uw_map->cs);
jk_log(l, JK_LOG_INFO,
"Reloaded urimaps from %s", uw_map->fname);
}
return JK_TRUE;
}