blob: 6c855e3f7508ea46533a3083e84a5ca94b32bd23 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed 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: ISAPI plugin for IIS/PWS *
* Author: Gal Shachor <shachor@il.ibm.com> *
* Author: Larry Isaacs <larryi@apache.org> *
* Author: Ignacio J. Ortega <nacho@apache.org> *
* Author: Mladen Turk <mturk@apache.org> *
* Version: $Revision$ *
***************************************************************************/
// This define is needed to include wincrypt,h, needed to get client certificates
#define _WIN32_WINNT 0x0400
#include <httpext.h>
#include <httpfilt.h>
#include <wininet.h>
#include "jk_global.h"
#include "jk_util.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_service.h"
#include "jk_worker.h"
#include "jk_uri_worker_map.h"
#include "jk_shm.h"
#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
#define SHM_DEF_NAME "JKISAPISHMEM"
#define DEFAULT_WORKER_NAME ("ajp13")
/*
* We use special headers to pass values from the filter to the
* extension. These values are:
*
* 1. The real URI before redirection took place
* 2. The name of the worker to be used.
* 3. The contents of the Translate header, if any
*
*/
#define URI_HEADER_NAME_BASE ("TOMCATURI")
#define QUERY_HEADER_NAME_BASE ("TOMCATQUERY")
#define WORKER_HEADER_NAME_BASE ("TOMCATWORKER")
#define TOMCAT_TRANSLATE_HEADER_NAME_BASE ("TOMCATTRANSLATE")
#define CONTENT_LENGTH ("CONTENT_LENGTH:")
/* The template used to construct our unique headers
* from the base name and module instance
*/
#define HEADER_TEMPLATE ("%s%p:")
#define HTTP_HEADER_TEMPLATE ("HTTP_%s%p")
static char URI_HEADER_NAME[_MAX_FNAME];
static char QUERY_HEADER_NAME[_MAX_FNAME];
static char WORKER_HEADER_NAME[_MAX_FNAME];
static char TOMCAT_TRANSLATE_HEADER_NAME[_MAX_FNAME];
static char HTTP_URI_HEADER_NAME[_MAX_FNAME];
static char HTTP_QUERY_HEADER_NAME[_MAX_FNAME];
static char HTTP_WORKER_HEADER_NAME[_MAX_FNAME];
#define REGISTRY_LOCATION ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
#define EXTENSION_URI_TAG ("extension_uri")
#define URI_SELECT_TAG ("uri_select")
#define URI_SELECT_PARSED_VERB ("parsed")
#define URI_SELECT_UNPARSED_VERB ("unparsed")
#define URI_SELECT_ESCAPED_VERB ("escaped")
#define URI_REWRITE_VERB ("rewrite_rule_file")
#define TRANSLATE_HEADER ("Translate:")
#define TRANSLATE_HEADER_NAME ("Translate")
#define TRANSLATE_HEADER_NAME_LC ("translate")
#define BAD_REQUEST -1
#define BAD_PATH -2
#define MAX_SERVERNAME 128
#define HTML_ERROR_400 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
"<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>" \
"<BODY><H1>Bad request!</H1><DL><DD>\n" \
"Your browser (or proxy) sent a request that " \
"this server could not understand.</DL></DD></BODY></HTML>"
#define HTML_ERROR_404 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
"<HTML><HEAD><TITLE>Object not found!</TITLE></HEAD>" \
"<BODY><H1>The requested URL was not found on this server" \
"</H1><DL><DD>\nIf you entered the URL manually please check your" \
"spelling and try again.</DL></DD></BODY></HTML>"
#define JK_TOLOWER(x) ((char)tolower((BYTE)(x)))
#define GET_SERVER_VARIABLE_VALUE(name, place) \
do { \
(place) = NULL; \
huge_buf_sz = sizeof(huge_buf); \
if (get_server_value(private_data->lpEcb, \
(name), \
huge_buf, \
huge_buf_sz)) { \
(place) = jk_pool_strdup(&private_data->p, \
huge_buf); \
} } while(0)
#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def) \
do { \
huge_buf_sz = sizeof(huge_buf); \
if (get_server_value(private_data->lpEcb, \
(name), \
huge_buf, \
huge_buf_sz)) { \
(place) = atoi(huge_buf); \
if (0 == (place)) { \
(place) = def; \
} \
} else { \
(place) = def; \
} } while(0)
static char ini_file_name[MAX_PATH];
static int using_ini_file = JK_FALSE;
static int is_inited = JK_FALSE;
static int is_mapread = JK_FALSE;
static int iis5 = -1;
static jk_uri_worker_map_t *uw_map = NULL;
static jk_map_t *workers_map = NULL;
static jk_map_t *rewrite_map = NULL;
static jk_logger_t *logger = NULL;
static char *SERVER_NAME = "SERVER_NAME";
static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
static char *CONTENT_TYPE = "Content-Type:text/html\r\n\r\n";
static char extension_uri[INTERNET_MAX_URL_LENGTH] =
"/jakarta/isapi_redirect.dll";
static char log_file[MAX_PATH * 2];
static int log_level = JK_LOG_DEF_LEVEL;
static char worker_file[MAX_PATH * 2];
static char worker_mount_file[MAX_PATH * 2] = {0};
static char rewrite_rule_file[MAX_PATH * 2] = {0};
#define URI_SELECT_OPT_PARSED 0
#define URI_SELECT_OPT_UNPARSED 1
#define URI_SELECT_OPT_ESCAPED 2
static int uri_select_option = URI_SELECT_OPT_PARSED;
static jk_worker_env_t worker_env;
typedef struct isapi_private_data_t isapi_private_data_t;
struct isapi_private_data_t
{
jk_pool_t p;
int request_started;
unsigned int bytes_read_so_far;
LPEXTENSION_CONTROL_BLOCK lpEcb;
};
typedef struct isapi_log_data_t isapi_log_data_t;
struct isapi_log_data_t {
char uri[INTERNET_MAX_URL_LENGTH];
char query[INTERNET_MAX_URL_LENGTH];
};
static int JK_METHOD start_response(jk_ws_service_t *s,
int status,
const char *reason,
const char *const *header_names,
const char *const *header_values,
unsigned int num_of_headers);
static int JK_METHOD read(jk_ws_service_t *s,
void *b, unsigned int l, unsigned int *a);
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l);
static int init_ws_service(isapi_private_data_t * private_data,
jk_ws_service_t *s, char **worker_name);
static int init_jk(char *serverName);
static int initialize_extension(void);
static int read_registry_init_data(void);
static int get_registry_config_parameter(HKEY hkey,
const char *tag, char *b, DWORD sz);
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
char *name,
char *buf, DWORD bufsz);
static int base64_encode_cert_len(int len);
static int base64_encode_cert(char *encoded,
const char *string, int len);
static char x2c(const char *what)
{
register char digit;
digit =
((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit +=
(what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return (digit);
}
static int unescape_url(char *url)
{
register int x, y, badesc, badpath;
badesc = 0;
badpath = 0;
for (x = 0, y = 0; url[y]; ++x, ++y) {
if (url[y] != '%')
url[x] = url[y];
else {
if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
badesc = 1;
url[x] = '%';
}
else {
url[x] = x2c(&url[y + 1]);
y += 2;
if (url[x] == '/' || url[x] == '\0')
badpath = 1;
}
}
}
url[x] = '\0';
if (badesc)
return BAD_REQUEST;
else if (badpath)
return BAD_PATH;
else
return 0;
}
static void getparents(char *name)
{
int l, w;
/* Four paseses, as per RFC 1808 */
/* a) remove ./ path segments */
for (l = 0, w = 0; name[l] != '\0';) {
if (name[l] == '.' && name[l + 1] == '/'
&& (l == 0 || name[l - 1] == '/'))
l += 2;
else
name[w++] = name[l++];
}
/* b) remove trailing . path, segment */
if (w == 1 && name[0] == '.')
w--;
else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
w--;
name[w] = '\0';
/* c) remove all xx/../ segments. (including leading ../ and /../) */
l = 0;
while (name[l] != '\0') {
if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
(l == 0 || name[l - 1] == '/')) {
register int m = l + 3, n;
l = l - 2;
if (l >= 0) {
while (l >= 0 && name[l] != '/')
l--;
l++;
}
else
l = 0;
n = l;
while ((name[n] = name[m]) != '\0') {
n++;
m++;
}
}
else
++l;
}
/* d) remove trailing xx/.. segment. */
if (l == 2 && name[0] == '.' && name[1] == '.')
name[0] = '\0';
else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
&& name[l - 3] == '/') {
l = l - 4;
if (l >= 0) {
while (l >= 0 && name[l] != '/')
l--;
l++;
}
else
l = 0;
name[l] = '\0';
}
}
/* Apache code to escape a URL */
#define T_OS_ESCAPE_PATH (4)
static const BYTE test_char_table[256] = {
0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 0, 7, 6, 1, 6, 1, 1, 9, 9, 1, 0, 8, 0, 0, 10,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15, 15, 8, 15, 15,
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 7, 0,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 7, 15, 1, 14,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
};
#define TEST_CHAR(c, f) (test_char_table[(unsigned int)(c)] & (f))
static const char c2x_table[] = "0123456789abcdef";
static BYTE *c2x(unsigned int what, BYTE *where)
{
*where++ = '%';
*where++ = c2x_table[what >> 4];
*where++ = c2x_table[what & 0xf];
return where;
}
static char *status_reason(int status)
{
static struct reasons {
int status;
char *reason;
} *r, reasons[] = {
{ 100, "Continue" },
{ 101, "Switching Protocols" },
{ 200, "OK" },
{ 201, "Created" },
{ 202, "Accepted" },
{ 203, "Non-Authoritative Information" },
{ 204, "No Content" },
{ 205, "Reset Content" },
{ 206, "Partial Content" },
{ 300, "Multiple Choices" },
{ 301, "Moved Permanently" },
{ 302, "Moved Temporarily" },
{ 303, "See Other" },
{ 304, "Not Modified" },
{ 305, "Use Proxy" },
{ 400, "Bad Request" },
{ 401, "Unauthorized" },
{ 402, "Payment Required" },
{ 403, "Forbidden" },
{ 404, "Not Found" },
{ 405, "Method Not Allowed" },
{ 406, "Not Acceptable" },
{ 407, "Proxy Authentication Required" },
{ 408, "Request Timeout" },
{ 409, "Conflict" },
{ 410, "Gone" },
{ 411, "Length Required" },
{ 412, "Precondition Failed" },
{ 413, "Request Entity Too Large" },
{ 414, "Request-URI Too Long" },
{ 415, "Unsupported Media Type" },
{ 500, "Internal Server Error" },
{ 501, "Not Implemented" },
{ 502, "Bad Gateway" },
{ 503, "Service Unavailable" },
{ 504, "Gateway Timeout" },
{ 505, "HTTP Version Not Supported" },
{ 000, NULL}
};
r = reasons;
while (r->status <= status)
if (r->status == status)
return r->reason;
else
r++;
return "No Reason";
}
static int escape_url(const char *path, char *dest, int destsize)
{
const BYTE *s = (const BYTE *)path;
BYTE *d = (BYTE *)dest;
BYTE *e = d + destsize - 1;
BYTE *ee = d + destsize - 3;
while (*s) {
if (TEST_CHAR(*s, T_OS_ESCAPE_PATH)) {
if (d >= ee)
return JK_FALSE;
d = c2x(*s, d);
}
else {
if (d >= e)
return JK_FALSE;
*d++ = *s;
}
++s;
}
*d = '\0';
return JK_TRUE;
}
/*
* Find the first occurrence of find in s.
*/
static char *stristr(const char *s, const char *find)
{
char c, sc;
size_t len;
if ((c = tolower((unsigned char)(*find++))) != 0) {
len = strlen(find);
do {
do {
if ((sc = tolower((unsigned char)(*s++))) == 0)
return (NULL);
} while (sc != c);
} while (strnicmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}
static int uri_is_web_inf(const char *uri)
{
if (stristr(uri, "web-inf")) {
return JK_TRUE;
}
if (stristr(uri, "meta-inf")) {
return JK_TRUE;
}
return JK_FALSE;
}
static void write_error_response(PHTTP_FILTER_CONTEXT pfc, char *status,
char *msg)
{
DWORD len = (DWORD)strlen(msg);
/* reject !!! */
pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0);
pfc->ServerSupportFunction(pfc,
SF_REQ_SEND_RESPONSE_HEADER,
status, 0, 0);
pfc->WriteClient(pfc, msg, &len, 0);
}
static int JK_METHOD start_response(jk_ws_service_t *s,
int status,
const char *reason,
const char *const *header_names,
const char *const *header_values,
unsigned int num_of_headers)
{
static char crlf[3] = { (char)13, (char)10, '\0' };
JK_TRACE_ENTER(logger);
if (status < 100 || status > 1000) {
jk_log(logger, JK_LOG_ERROR,
"invalid status %d",
status);
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
if (s && s->ws_private) {
isapi_private_data_t *p = s->ws_private;
if (!p->request_started) {
size_t len_of_status;
char *status_str;
char *headers_str;
p->request_started = JK_TRUE;
/*
* Create the status line
*/
if (!reason) {
reason = status_reason(status);
}
status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char));
sprintf(status_str, "%d %s", status, reason);
len_of_status = strlen(status_str);
/*
* Create response headers string
*/
if (num_of_headers) {
size_t i, len_of_headers;
for (i = 0, len_of_headers = 0; i < num_of_headers; i++) {
len_of_headers += strlen(header_names[i]);
len_of_headers += strlen(header_values[i]);
len_of_headers += 4; /* extra for colon, space and crlf */
}
len_of_headers += 3; /* crlf and terminating null char */
headers_str = (char *)_alloca(len_of_headers * sizeof(char));
headers_str[0] = '\0';
for (i = 0; i < num_of_headers; i++) {
strcat(headers_str, header_names[i]);
strcat(headers_str, ": ");
strcat(headers_str, header_values[i]);
strcat(headers_str, crlf);
}
strcat(headers_str, crlf);
}
else {
headers_str = crlf;
}
if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
status_str,
(LPDWORD) &len_of_status,
(LPDWORD) headers_str)) {
jk_log(logger, JK_LOG_ERROR,
"HSE_REQ_SEND_RESPONSE_HEADER failed");
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
}
JK_TRACE_EXIT(logger);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(logger);
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
static int JK_METHOD read(jk_ws_service_t *s,
void *b, unsigned int l, unsigned int *a)
{
JK_TRACE_ENTER(logger);
if (s && s->ws_private && b && a) {
isapi_private_data_t *p = s->ws_private;
*a = 0;
if (l) {
char *buf = b;
DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
if (already_read >= l) {
memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, l);
p->bytes_read_so_far += l;
*a = l;
}
else {
/*
* Try to copy what we already have
*/
if (already_read > 0) {
memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far,
already_read);
buf += already_read;
l -= already_read;
p->bytes_read_so_far = p->lpEcb->cbAvailable;
*a = already_read;
}
/*
* Now try to read from the client ...
*/
if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, (LPDWORD)&l)) {
*a += l;
}
else {
jk_log(logger, JK_LOG_ERROR,
"ReadClient failed with %08x", GetLastError());
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
}
}
JK_TRACE_EXIT(logger);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(logger);
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l)
{
JK_TRACE_ENTER(logger);
if (s && s->ws_private && b) {
isapi_private_data_t *p = s->ws_private;
if (l) {
unsigned int written = 0;
char *buf = (char *)b;
if (!p->request_started) {
start_response(s, 200, NULL, NULL, NULL, 0);
}
while (written < l) {
DWORD try_to_write = l - written;
if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
buf + written, &try_to_write, 0)) {
jk_log(logger, JK_LOG_ERROR,
"WriteClient failed with %08x", GetLastError());
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
written += try_to_write;
}
}
JK_TRACE_EXIT(logger);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(logger);
JK_TRACE_EXIT(logger);
return JK_FALSE;
}
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{
ULONG http_filter_revision = HTTP_FILTER_REVISION;
pVer->dwFilterVersion = pVer->dwServerFilterVersion;
if (pVer->dwFilterVersion > http_filter_revision) {
pVer->dwFilterVersion = http_filter_revision;
}
pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
SF_NOTIFY_SECURE_PORT |
SF_NOTIFY_NONSECURE_PORT |
SF_NOTIFY_PREPROC_HEADERS |
SF_NOTIFY_LOG |
SF_NOTIFY_AUTH_COMPLETE;
strcpy(pVer->lpszFilterDesc, VERSION_STRING);
if (!is_inited) {
return initialize_extension();
}
return TRUE;
}
static void simple_rewrite(char *uri)
{
if (rewrite_map) {
int i;
char buf[INTERNET_MAX_URL_LENGTH];
for (i = 0; i < jk_map_size(rewrite_map); i++) {
const char *src = jk_map_name_at(rewrite_map, i);
if (strncmp(uri, src, strlen(src)) == 0) {
strcpy(buf, jk_map_value_at(rewrite_map, i));
strcat(buf, uri + strlen(src));
strcpy(uri, buf);
}
}
}
}
DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
DWORD dwNotificationType, LPVOID pvNotification)
{
/* Initialise jk */
if (is_inited && !is_mapread) {
char serverName[MAX_SERVERNAME];
DWORD dwLen = sizeof(serverName);
if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) {
if (dwLen > 0)
serverName[dwLen - 1] = '\0';
if (init_jk(serverName))
is_mapread = JK_TRUE;
}
/* If we can't read the map we become dormant */
if (!is_mapread)
is_inited = JK_FALSE;
}
if (is_inited && (iis5 < 0)) {
char serverSoftware[256];
DWORD dwLen = sizeof(serverSoftware);
iis5 = 0;
if (pfc->
GetServerVariable(pfc, SERVER_SOFTWARE, serverSoftware, &dwLen)) {
iis5 = (atof(serverSoftware + 14) >= 5.0);
if (iis5) {
jk_log(logger, JK_LOG_DEBUG, "Detected IIS >= 5.0");
}
else {
jk_log(logger, JK_LOG_DEBUG, "Detected IIS < 5.0");
}
}
}
if (is_inited &&
(((SF_NOTIFY_PREPROC_HEADERS == dwNotificationType) && !iis5) ||
((SF_NOTIFY_AUTH_COMPLETE == dwNotificationType) && iis5)
)
) {
char uri[INTERNET_MAX_URL_LENGTH];
char snuri[INTERNET_MAX_URL_LENGTH] = "/";
char Host[INTERNET_MAX_URL_LENGTH] = "";
char Port[INTERNET_MAX_URL_LENGTH] = "";
char Translate[INTERNET_MAX_URL_LENGTH];
BOOL(WINAPI * GetHeader)
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
LPVOID lpvBuffer, LPDWORD lpdwSize);
BOOL(WINAPI * SetHeader)
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
LPSTR lpszValue);
BOOL(WINAPI * AddHeader)
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
LPSTR lpszValue);
char *query;
DWORD sz = sizeof(uri);
DWORD szHost = sizeof(Host);
DWORD szPort = sizeof(Port);
DWORD szTranslate = sizeof(Translate);
if (iis5) {
GetHeader =
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->GetHeader;
SetHeader =
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->SetHeader;
AddHeader =
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->AddHeader;
}
else {
GetHeader =
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->GetHeader;
SetHeader =
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->SetHeader;
AddHeader =
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->AddHeader;
}
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG, "Filter started");
/*
* Just in case somebody set these headers in the request!
*/
SetHeader(pfc, URI_HEADER_NAME, NULL);
SetHeader(pfc, QUERY_HEADER_NAME, NULL);
SetHeader(pfc, WORKER_HEADER_NAME, NULL);
SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
if (!GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz)) {
jk_log(logger, JK_LOG_ERROR,
"error while getting the url");
return SF_STATUS_REQ_ERROR;
}
if (strlen(uri)) {
int rc;
const char *worker = NULL;
query = strchr(uri, '?');
if (query) {
*query++ = '\0';
}
rc = unescape_url(uri);
if (rc == BAD_REQUEST) {
jk_log(logger, JK_LOG_ERROR,
"[%s] contains one or more invalid escape sequences.",
uri);
write_error_response(pfc, "400 Bad Request",
HTML_ERROR_400);
return SF_STATUS_REQ_FINISHED;
}
else if (rc == BAD_PATH) {
jk_log(logger, JK_LOG_EMERG,
"[%s] contains forbidden escape sequences.",
uri);
write_error_response(pfc, "404 Not Found",
HTML_ERROR_404);
return SF_STATUS_REQ_FINISHED;
}
getparents(uri);
if (pfc->
GetServerVariable(pfc, SERVER_NAME, (LPVOID) Host,
(LPDWORD) & szHost)) {
if (szHost > 0) {
Host[szHost - 1] = '\0';
}
}
Port[0] = '\0';
if (pfc->
GetServerVariable(pfc, "SERVER_PORT", (LPVOID) Port,
(LPDWORD) & szPort)) {
if (szPort > 0) {
Port[szPort - 1] = '\0';
}
}
szPort = atoi(Port);
if (szPort != 80 && szPort != 443 && szHost > 0) {
strcat(Host, ":");
strcat(Host, Port);
}
if (szHost > 0) {
strcat(snuri, Host);
strcat(snuri, uri);
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"Virtual Host redirection of %s",
snuri);
worker = map_uri_to_worker(uw_map, snuri, logger);
}
if (!worker) {
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"Default redirection of %s",
uri);
worker = map_uri_to_worker(uw_map, uri, logger);
}
/*
* Check if somebody is feading us with his own TOMCAT data headers.
* We reject such postings !
*/
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"check if [%s] is points to the web-inf directory",
uri);
if (uri_is_web_inf(uri)) {
jk_log(logger, JK_LOG_EMERG,
"[%s] points to the web-inf or meta-inf directory.\nSomebody try to hack into the site!!!",
uri);
write_error_response(pfc, "404 Not Found",
HTML_ERROR_404);
return SF_STATUS_REQ_FINISHED;
}
if (worker) {
char *forwardURI;
/* This is a servlet, should redirect ... */
jk_log(logger, JK_LOG_DEBUG,
"[%s] is a servlet url - should redirect to %s",
uri, worker);
/* get URI we should forward */
if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
/* get original unparsed URI */
GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz);
/* restore terminator for uri portion */
if (query)
*(query - 1) = '\0';
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"fowarding original URI [%s]",
uri);
forwardURI = uri;
}
else if (uri_select_option == URI_SELECT_OPT_ESCAPED) {
if (!escape_url(uri, snuri, INTERNET_MAX_URL_LENGTH)) {
jk_log(logger, JK_LOG_ERROR,
"[%s] re-encoding request exceeds maximum buffer size.",
uri);
write_error_response(pfc, "400 Bad Request",
HTML_ERROR_400);
return SF_STATUS_REQ_FINISHED;
}
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"fowarding escaped URI [%s]",
snuri);
forwardURI = snuri;
}
else {
forwardURI = uri;
}
/* Do a simple rewrite .
* Note that URI can be escaped, so thus the rule has
* to be in that case.
*
* TODO: Add more advanced regexp rewrite.
*/
simple_rewrite(forwardURI);
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"rewriten URI [%s]",
forwardURI);
if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
((query != NULL && strlen(query) > 0)
? !AddHeader(pfc, QUERY_HEADER_NAME, query) : FALSE) ||
!AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) ||
!SetHeader(pfc, "url", extension_uri)) {
jk_log(logger, JK_LOG_ERROR,
"error while adding request headers");
return SF_STATUS_REQ_ERROR;
}
/* Move Translate: header to a temporary header so
* that the extension proc will be called.
* This allows the servlet to handle 'Translate: f'.
*/
if (GetHeader
(pfc, TRANSLATE_HEADER, (LPVOID) Translate,
(LPDWORD) & szTranslate) && Translate != NULL
&& szTranslate > 0) {
if (!AddHeader
(pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
jk_log(logger, JK_LOG_ERROR,
"error while adding Tomcat-Translate headers");
return SF_STATUS_REQ_ERROR;
}
SetHeader(pfc, "Translate:", NULL);
}
if (!pfc->pFilterContext) {
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->AllocMem(pfc, sizeof(isapi_log_data_t), 0);
if (!ld) {
jk_log(logger, JK_LOG_ERROR,
"error while allocating memory");
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return SF_STATUS_REQ_ERROR;
}
memset(ld, 0, sizeof(isapi_log_data_t));
strcpy(ld->uri, forwardURI);
if (query && strlen(query) > 0)
strcpy(ld->query, query);
pfc->pFilterContext = ld;
} else {
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
memset(ld, 0, sizeof(isapi_log_data_t));
strcpy(ld->uri, forwardURI);
if (query && strlen(query) > 0)
strcpy(ld->query, query);
}
}
else {
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"[%s] is not a servlet url", uri);
}
}
}
else if (is_inited && (dwNotificationType == SF_NOTIFY_LOG)) {
if (pfc->pFilterContext) {
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
HTTP_FILTER_LOG *pl = (HTTP_FILTER_LOG *)pvNotification;
pl->pszTarget = ld->uri;
pl->pszParameters = ld->query;
}
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
{
pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
strcpy(pVer->lpszExtensionDesc, VERSION_STRING);
if (!is_inited) {
return initialize_extension();
}
return TRUE;
}
DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb)
{
DWORD rc = HSE_STATUS_ERROR;
lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
JK_TRACE_ENTER(logger);
/* Initialise jk */
if (is_inited && !is_mapread) {
char serverName[MAX_SERVERNAME];
DWORD dwLen = sizeof(serverName);
if (lpEcb->
GetServerVariable(lpEcb->ConnID, SERVER_NAME, serverName,
&dwLen)) {
if (dwLen > 0)
serverName[dwLen - 1] = '\0';
if (init_jk(serverName))
is_mapread = JK_TRUE;
}
if (!is_mapread)
is_inited = JK_FALSE;
}
if (is_inited) {
isapi_private_data_t private_data;
jk_ws_service_t s;
jk_pool_atom_t buf[SMALL_POOL_SIZE];
char *worker_name;
wc_maintain(logger);
jk_init_ws_service(&s);
jk_open_pool(&private_data.p, buf, sizeof(buf));
private_data.request_started = JK_FALSE;
private_data.bytes_read_so_far = 0;
private_data.lpEcb = lpEcb;
s.ws_private = &private_data;
s.pool = &private_data.p;
if (init_ws_service(&private_data, &s, &worker_name)) {
jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"%s a worker for name %s",
worker ? "got" : "could not get", worker_name);
if (worker) {
jk_endpoint_t *e = NULL;
/* Update retries for this worker */
s.retries = worker->retries;
if (worker->get_endpoint(worker, &e, logger)) {
int is_error = JK_HTTP_SERVER_ERROR;
if (e->service(e, &s, logger, &is_error)) {
rc = HSE_STATUS_SUCCESS;
lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
jk_log(logger, JK_LOG_DEBUG,
"service() returned OK");
}
else {
lpEcb->dwHttpStatusCode = is_error;
jk_log(logger, JK_LOG_ERROR,
"service() failed");
}
e->done(&e, logger);
}
}
else {
jk_log(logger, JK_LOG_ERROR,
"could not get a worker for name %s",
worker_name);
}
}
jk_close_pool(&private_data.p);
}
else {
jk_log(logger, JK_LOG_ERROR,
"not initialized");
}
JK_TRACE_EXIT(logger);
return rc;
}
BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
return TerminateFilter(dwFlags);
}
BOOL WINAPI TerminateFilter(DWORD dwFlags)
{
UNREFERENCED_PARAMETER(dwFlags);
if (is_inited) {
is_inited = JK_FALSE;
if (is_mapread) {
uri_worker_map_free(&uw_map, logger);
is_mapread = JK_FALSE;
}
if (workers_map) {
jk_map_free(&workers_map);
workers_map = NULL;
}
wc_close(logger);
if (logger) {
jk_close_file_logger(&logger);
}
}
return TRUE;
}
BOOL WINAPI DllMain(HINSTANCE hInst, // Instance Handle of the DLL
ULONG ulReason, // Reason why NT called this DLL
LPVOID lpReserved) // Reserved parameter for future use
{
BOOL fReturn = TRUE;
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char file_name[_MAX_PATH];
UNREFERENCED_PARAMETER(lpReserved);
switch (ulReason) {
case DLL_PROCESS_ATTACH:
if (GetModuleFileName(hInst, file_name, sizeof(file_name))) {
_splitpath(file_name, drive, dir, fname, NULL);
_makepath(ini_file_name, drive, dir, fname, ".properties");
}
else {
fReturn = JK_FALSE;
}
/* Construct redirector headers to use for this redirector instance */
sprintf(URI_HEADER_NAME, HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
sprintf(QUERY_HEADER_NAME, HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
sprintf(WORKER_HEADER_NAME, HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
sprintf(TOMCAT_TRANSLATE_HEADER_NAME, HEADER_TEMPLATE, TOMCAT_TRANSLATE_HEADER_NAME_BASE, hInst);
sprintf(HTTP_URI_HEADER_NAME, HTTP_HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
sprintf(HTTP_QUERY_HEADER_NAME, HTTP_HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
sprintf(HTTP_WORKER_HEADER_NAME, HTTP_HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
break;
case DLL_PROCESS_DETACH:
__try {
TerminateFilter(HSE_TERM_MUST_UNLOAD);
}
__except(1) {
}
break;
default:
break;
}
return fReturn;
}
static int init_jk(char *serverName)
{
int rc = JK_FALSE;
if (!jk_open_file_logger(&logger, log_file, log_level)) {
logger = NULL;
}
/* Simulate shared memory
* For now use fixed size.
*/
jk_shm_open(SHM_DEF_NAME, JK_SHM_DEF_SIZE, logger);
/* 10 is minimum supported on WINXP */
jk_set_worker_def_cache_size(10);
/* Logging the initialization type: registry or properties file in virtual dir
*/
if (JK_IS_DEBUG_LEVEL(logger)) {
if (using_ini_file) {
jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.", ini_file_name);
}
else {
jk_log(logger, JK_LOG_DEBUG, "Using registry.");
}
jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
worker_mount_file);
jk_log(logger, JK_LOG_DEBUG, "Using rewrite rule file %s.",
rewrite_rule_file);
jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option);
}
if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
if (jk_map_read_properties(rewrite_map, rewrite_rule_file, NULL)) {
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG, "Loaded rewrite rule file %s.",
rewrite_rule_file);
}
else {
jk_map_free(&rewrite_map);
}
}
}
if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
rc = JK_FALSE;
uw_map->fname = worker_mount_file;
if (worker_mount_file[0])
rc = uri_worker_map_load(uw_map, logger);
}
if (rc) {
rc = JK_FALSE;
if (jk_map_alloc(&workers_map)) {
if (jk_map_read_properties(workers_map, worker_file, NULL)) {
/* we add the URI->WORKER MAP since workers using AJP14 will feed it */
worker_env.uri_to_worker = uw_map;
worker_env.server_name = serverName;
if (wc_open(workers_map, &worker_env, logger)) {
rc = JK_TRUE;
}
}
else {
jk_log(logger, JK_LOG_EMERG,
"Unable to read worker file %s.", worker_file);
}
if (rc != JK_TRUE) {
jk_map_free(&workers_map);
workers_map = NULL;
}
}
}
return rc;
}
static int initialize_extension(void)
{
if (read_registry_init_data()) {
is_inited = JK_TRUE;
}
return is_inited;
}
int parse_uri_select(const char *uri_select)
{
if (0 == strcasecmp(uri_select, URI_SELECT_PARSED_VERB)) {
return URI_SELECT_OPT_PARSED;
}
if (0 == strcasecmp(uri_select, URI_SELECT_UNPARSED_VERB)) {
return URI_SELECT_OPT_UNPARSED;
}
if (0 == strcasecmp(uri_select, URI_SELECT_ESCAPED_VERB)) {
return URI_SELECT_OPT_ESCAPED;
}
return -1;
}
static int read_registry_init_data(void)
{
char tmpbuf[INTERNET_MAX_URL_LENGTH];
HKEY hkey;
long rc;
int ok = JK_TRUE;
const char *tmp;
jk_map_t *map;
if (jk_map_alloc(&map)) {
if (jk_map_read_properties(map, ini_file_name, NULL)) {
using_ini_file = JK_TRUE;
}
}
if (using_ini_file) {
tmp = jk_map_get_string(map, JK_LOG_FILE_TAG, NULL);
if (tmp) {
strcpy(log_file, tmp);
}
else {
ok = JK_FALSE;
}
tmp = jk_map_get_string(map, JK_LOG_LEVEL_TAG, NULL);
if (tmp) {
log_level = jk_parse_log_level(tmp);
}
tmp = jk_map_get_string(map, EXTENSION_URI_TAG, NULL);
if (tmp) {
strcpy(extension_uri, tmp);
}
else {
ok = JK_FALSE;
}
tmp = jk_map_get_string(map, JK_WORKER_FILE_TAG, NULL);
if (tmp) {
strcpy(worker_file, tmp);
}
else {
ok = JK_FALSE;
}
tmp = jk_map_get_string(map, JK_MOUNT_FILE_TAG, NULL);
if (tmp) {
strcpy(worker_mount_file, tmp);
}
else {
ok = JK_FALSE;
}
tmp = jk_map_get_string(map, URI_REWRITE_VERB, NULL);
if (tmp) {
strcpy(rewrite_rule_file, tmp);
}
tmp = jk_map_get_string(map, URI_SELECT_TAG, NULL);
if (tmp) {
int opt = parse_uri_select(tmp);
if (opt >= 0) {
uri_select_option = opt;
}
else {
ok = JK_FALSE;
}
}
}
else {
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
if (ERROR_SUCCESS != rc) {
return JK_FALSE;
}
if (get_registry_config_parameter(hkey,
JK_LOG_FILE_TAG,
tmpbuf, sizeof(log_file))) {
strcpy(log_file, tmpbuf);
}
else {
ok = JK_FALSE;
}
if (get_registry_config_parameter(hkey,
JK_LOG_LEVEL_TAG,
tmpbuf, sizeof(tmpbuf))) {
log_level = jk_parse_log_level(tmpbuf);
}
if (get_registry_config_parameter(hkey,
EXTENSION_URI_TAG,
tmpbuf, sizeof(extension_uri))) {
strcpy(extension_uri, tmpbuf);
}
else {
ok = JK_FALSE;
}
if (get_registry_config_parameter(hkey,
JK_WORKER_FILE_TAG,
tmpbuf, sizeof(worker_file))) {
strcpy(worker_file, tmpbuf);
}
else {
ok = JK_FALSE;
}
if (get_registry_config_parameter(hkey,
JK_MOUNT_FILE_TAG,
tmpbuf,
sizeof(worker_mount_file))) {
strcpy(worker_mount_file, tmpbuf);
}
else {
ok = JK_FALSE;
}
if (get_registry_config_parameter(hkey,
URI_REWRITE_VERB,
tmpbuf,
sizeof(rewrite_rule_file))) {
strcpy(rewrite_rule_file, tmpbuf);
}
if (get_registry_config_parameter(hkey,
URI_SELECT_TAG,
tmpbuf, sizeof(tmpbuf))) {
int opt = parse_uri_select(tmpbuf);
if (opt >= 0) {
uri_select_option = opt;
}
else {
ok = JK_FALSE;
}
}
RegCloseKey(hkey);
}
return ok;
}
static int get_registry_config_parameter(HKEY hkey,
const char *tag, char *b, DWORD sz)
{
DWORD type = 0;
LONG lrc;
lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
return JK_FALSE;
}
b[sz] = '\0';
return JK_TRUE;
}
static int init_ws_service(isapi_private_data_t * private_data,
jk_ws_service_t *s, char **worker_name)
{
char huge_buf[16 * 1024]; /* should be enough for all */
DWORD huge_buf_sz;
s->jvm_route = NULL;
s->start_response = start_response;
s->read = read;
s->write = write;
s->flush = NULL;
/* Clear RECO status */
s->reco_status = RECO_NONE;
GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));
GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);
GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);
if (s->req_uri == NULL) {
s->query_string = private_data->lpEcb->lpszQueryString;
*worker_name = DEFAULT_WORKER_NAME;
GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);
if (unescape_url(s->req_uri) < 0)
return JK_FALSE;
getparents(s->req_uri);
}
GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
GET_SERVER_VARIABLE_VALUE("REMOTE_USER", s->remote_user);
GET_SERVER_VARIABLE_VALUE("SERVER_PROTOCOL", s->protocol);
GET_SERVER_VARIABLE_VALUE("REMOTE_HOST", s->remote_host);
GET_SERVER_VARIABLE_VALUE("REMOTE_ADDR", s->remote_addr);
GET_SERVER_VARIABLE_VALUE(SERVER_NAME, s->server_name);
GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80);
GET_SERVER_VARIABLE_VALUE(SERVER_SOFTWARE, s->server_software);
GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
s->method = private_data->lpEcb->lpszMethod;
s->content_length = private_data->lpEcb->cbTotalBytes;
s->ssl_cert = NULL;
s->ssl_cert_len = 0;
s->ssl_cipher = NULL;
s->ssl_session = NULL;
s->ssl_key_size = -1;
s->headers_names = NULL;
s->headers_values = NULL;
s->num_headers = 0;
s->uw_map = uw_map;
/*
* Add SSL IIS environment
*/
if (s->is_ssl) {
char *ssl_env_names[9] = {
"CERT_ISSUER",
"CERT_SUBJECT",
"CERT_COOKIE",
"HTTPS_SERVER_SUBJECT",
"CERT_FLAGS",
"HTTPS_SECRETKEYSIZE",
"CERT_SERIALNUMBER",
"HTTPS_SERVER_ISSUER",
"HTTPS_KEYSIZE"
};
char *ssl_env_values[9] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
unsigned int i;
unsigned int num_of_vars = 0;
for (i = 0; i < 9; i++) {
GET_SERVER_VARIABLE_VALUE(ssl_env_names[i], ssl_env_values[i]);
if (ssl_env_values[i]) {
num_of_vars++;
}
}
if (num_of_vars) {
unsigned int j;
s->attributes_names =
jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
s->attributes_values =
jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
j = 0;
for (i = 0; i < 9; i++) {
if (ssl_env_values[i]) {
s->attributes_names[j] = ssl_env_names[i];
s->attributes_values[j] = ssl_env_values[i];
j++;
}
}
s->num_attributes = num_of_vars;
if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
CERT_CONTEXT_EX cc;
cc.cbAllocated = sizeof(huge_buf);
cc.CertContext.pbCertEncoded = (BYTE *) huge_buf;
cc.CertContext.cbCertEncoded = 0;
if (private_data->lpEcb->
ServerSupportFunction(private_data->lpEcb->ConnID,
(DWORD) HSE_REQ_GET_CERT_INFO_EX,
(LPVOID) & cc, NULL,
NULL) != FALSE) {
jk_log(logger, JK_LOG_DEBUG,
"Client Certificate encoding:%d sz:%d flags:%ld",
cc.CertContext.
dwCertEncodingType & X509_ASN_ENCODING,
cc.CertContext.cbCertEncoded,
cc.dwCertificateFlags);
s->ssl_cert =
jk_pool_alloc(&private_data->p,
base64_encode_cert_len(cc.CertContext.
cbCertEncoded));
s->ssl_cert_len = base64_encode_cert(s->ssl_cert,
huge_buf,
cc.CertContext.
cbCertEncoded) - 1;
}
}
}
}
huge_buf_sz = sizeof(huge_buf);
if (get_server_value(private_data->lpEcb,
"ALL_HTTP", huge_buf, huge_buf_sz)) {
unsigned int cnt = 0;
char *tmp;
for (tmp = huge_buf; *tmp; tmp++) {
if (*tmp == '\n') {
cnt++;
}
}
if (cnt) {
char *headers_buf = jk_pool_strdup(&private_data->p, huge_buf);
unsigned int i;
size_t len_of_http_prefix = strlen("HTTP_");
BOOL need_content_length_header = (s->content_length == 0);
cnt -= 2; /* For our two special headers:
* HTTP_TOMCATURI_XXXXXXXX
* HTTP_TOMCATWORKER_XXXXXXXX
*/
/* allocate an extra header slot in case we need to add a content-length header */
s->headers_names =
jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
s->headers_values =
jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
if (!s->headers_names || !s->headers_values || !headers_buf) {
return JK_FALSE;
}
for (i = 0, tmp = headers_buf; *tmp && i < cnt;) {
int real_header = JK_TRUE;
/* Skipp the HTTP_ prefix to the beginning of th header name */
tmp += len_of_http_prefix;
if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME))
|| !strnicmp(tmp, WORKER_HEADER_NAME,
strlen(WORKER_HEADER_NAME))) {
real_header = JK_FALSE;
}
else if (!strnicmp(tmp, QUERY_HEADER_NAME,
strlen(QUERY_HEADER_NAME))) {
/* HTTP_TOMCATQUERY_XXXXXXXX was supplied,
* remove it from the count and skip
*/
cnt--;
real_header = JK_FALSE;
}
else if (need_content_length_header &&
!strnicmp(tmp, CONTENT_LENGTH,
strlen(CONTENT_LENGTH))) {
need_content_length_header = FALSE;
s->headers_names[i] = tmp;
}
else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
s->headers_names[i] = TRANSLATE_HEADER_NAME_LC;
}
else {
s->headers_names[i] = tmp;
}
while (':' != *tmp && *tmp) {
if ('_' == *tmp) {
*tmp = '-';
}
else {
*tmp = JK_TOLOWER(*tmp);
}
tmp++;
}
*tmp = '\0';
tmp++;
/* Skip all the WS chars after the ':' to the beginning of th header value */
while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
tmp++;
}
if (real_header) {
s->headers_values[i] = tmp;
}
while (*tmp != '\n' && *tmp != '\r') {
tmp++;
}
*tmp = '\0';
tmp++;
/* skipp CR LF */
while (*tmp == '\n' || *tmp == '\r') {
tmp++;
}
if (real_header) {
i++;
}
}
/* Add a content-length = 0 header if needed.
* Ajp13 assumes an absent content-length header means an unknown,
* but non-zero length body.
*/
if (need_content_length_header) {
s->headers_names[cnt] = "Content-Length";
s->headers_values[cnt] = "0";
cnt++;
}
s->num_headers = cnt;
}
else {
/* We must have our two headers */
return JK_FALSE;
}
}
else {
return JK_FALSE;
}
return JK_TRUE;
}
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
char *name, char *buf, DWORD bufsz)
{
DWORD sz = bufsz;
buf[0] = '\0';
if (!lpEcb->GetServerVariable(lpEcb->ConnID, name,
buf, (LPDWORD) &sz))
return JK_FALSE;
if (sz <= bufsz)
buf[sz-1] = '\0';
return JK_TRUE;
}
static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int base64_encode_cert_len(int len)
{
int n = ((len + 2) / 3 * 4) + 1; /* base64 encoded size */
n += (n + 63 / 64) * 2; /* add CRLF's */
n += sizeof(begin_cert) + sizeof(end_cert) - 2; /* add enclosing strings. */
return n;
}
static int base64_encode_cert(char *encoded,
const char *string, int len)
{
int i, c;
char *p;
const char *t;
p = encoded;
t = begin_cert;
while (*t != '\0')
*p++ = *t++;
c = 0;
for (i = 0; i < len - 2; i += 3) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int)(string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
((int)(string[i + 2] & 0xC0) >> 6)];
*p++ = basis_64[string[i + 2] & 0x3F];
c += 4;
if (c >= 64) {
*p++ = '\r';
*p++ = '\n';
c = 0;
}
}
if (i < len) {
*p++ = basis_64[(string[i] >> 2) & 0x3F];
if (i == (len - 1)) {
*p++ = basis_64[((string[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = basis_64[((string[i] & 0x3) << 4) |
((int)(string[i + 1] & 0xF0) >> 4)];
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
}
*p++ = '=';
c++;
}
if (c != 0) {
*p++ = '\r';
*p++ = '\n';
}
t = end_cert;
while (*t != '\0')
*p++ = *t++;
*p++ = '\0';
return (int)(p - encoded);
}