blob: 9bb74559ce44d9bd63e8038c07673ebeafbefae0 [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: 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"
#include "pcre.h"
#ifndef POSIX_MALLOC_THRESHOLD
#define POSIX_MALLOC_THRESHOLD (10)
#endif
#include <strsafe.h>
#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
#define SHM_DEF_NAME "JKISAPISHMEM"
#define DEFAULT_WORKER_NAME ("ajp13")
/*
* This is default value found inside httpd.conf
* for MaxClients
*/
#define DEFAULT_WORKER_THREADS 250
/*
* 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_PATH];
static char QUERY_HEADER_NAME[MAX_PATH];
static char WORKER_HEADER_NAME[MAX_PATH];
static char TOMCAT_TRANSLATE_HEADER_NAME[MAX_PATH];
static char HTTP_URI_HEADER_NAME[MAX_PATH];
static char HTTP_QUERY_HEADER_NAME[MAX_PATH];
static char HTTP_WORKER_HEADER_NAME[MAX_PATH];
#define REGISTRY_LOCATION ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
#define W3SVC_REGISTRY_KEY ("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters")
#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_TAG ("rewrite_rule_file")
#define SHM_SIZE_TAG ("shm_size")
#define WORKER_MOUNT_RELOAD_TAG ("worker_mount_reload")
#define STRIP_SESSION_TAG ("strip_session")
#define AUTH_COMPLETE_TAG ("auth_complete")
#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
char HTML_ERROR_400[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
"<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>\n"
"<BODY><H1>Bad request!</H1>\n<P>"
"Your browser (or proxy) sent a request that "
"this server could not understand.</P></BODY></HTML>";
char HTML_ERROR_404[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
"<HTML><HEAD><TITLE>Object not found!</TITLE></HEAD>\n"
"<BODY><H1>The requested URL was not found on this server"
"</H1>\n<P>If you entered the URL manually please check your"
"spelling and try again.</P></BODY></HTML>";
char HTML_ERROR_500[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
"<HTML><HEAD><TITLE>Server error!</TITLE></HEAD>\n"
"<BODY><H1>Internal server error!</H1>\n<P>"
"The server encountered an internal error and was "
"unable to complete your request.</P></BODY></HTML>";
char HTML_ERROR_503[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
"<HTML><HEAD><TITLE>Service unavailable!</TITLE></HEAD>\n"
"<BODY><H1>Service temporary unavailable!</H1>\n<P>"
"The server is temporarily unable to service your "
"request due to maintenance downtime or capacity problems. "
"Please try again later.</P></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 jk_uri_worker_map_t *uw_map = NULL;
static jk_map_t *workers_map = NULL;
static jk_map_t *rewrite_map = NULL;
static jk_map_t *rregexp_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 int worker_mount_reload = JK_URIMAP_DEF_RELOAD;
static char rewrite_rule_file[MAX_PATH * 2] = {0};
static int shm_config_size = JK_SHM_DEF_SIZE;
static int strip_session = 0;
static DWORD auth_notification_flags = 0;
static int use_auth_notification_flags = 1;
#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_config_parameter(LPVOID src, const char *tag,
char *val, DWORD sz);
static int get_config_bool(LPVOID src, const char *tag, int def);
static int get_config_int(LPVOID src, const char *tag, int def);
static int get_registry_config_parameter(HKEY hkey,
const char *tag, char *b, DWORD sz);
static int get_registry_config_number(HKEY hkey, const char *tag,
int *val);
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 int get_auth_flags();
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 void write_error_message(LPEXTENSION_CONTROL_BLOCK lpEcb, int err)
{
DWORD len;
if (err = 500) {
lpEcb->ServerSupportFunction(lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
"500 Internal Server Error",
0,
(LPDWORD)CONTENT_TYPE);
len = (DWORD)(sizeof(HTML_ERROR_500) - 1);
lpEcb->WriteClient(lpEcb->ConnID,
HTML_ERROR_503, &len, 0);
}
else if (err == 503) {
lpEcb->ServerSupportFunction(lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
"503 Service Unavailable",
0,
(LPDWORD)CONTENT_TYPE);
len = (DWORD)(sizeof(HTML_ERROR_503) - 1);
lpEcb->WriteClient(lpEcb->ConnID,
HTML_ERROR_503, &len, 0);
}
else {
return;
}
}
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) {
int rv = JK_TRUE;
isapi_private_data_t *p = s->ws_private;
if (!p->request_started) {
char *status_str;
DWORD status_str_len;
char *headers_str = NULL;
BOOL keep_alive = FALSE;
p->request_started = JK_TRUE;
/*
* Create the status line
*/
if (!reason) {
reason = status_reason(status);
}
status_str = (char *)malloc((6 + strlen(reason)));
StringCbPrintf(status_str, 6 + strlen(reason), "%d %s", status, reason);
status_str_len = (DWORD)strlen(status_str);
/*
* Create response headers string
*/
if (num_of_headers) {
size_t i, len_of_headers = 0;
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 *)malloc(len_of_headers);
headers_str[0] = '\0';
for (i = 0; i < num_of_headers; i++) {
StringCbCat(headers_str, len_of_headers, header_names[i]);
StringCbCat(headers_str, len_of_headers, ": ");
StringCbCat(headers_str, len_of_headers, header_values[i]);
StringCbCat(headers_str, len_of_headers, crlf);
}
StringCbCat(headers_str, len_of_headers, crlf);
}
else {
headers_str = crlf;
}
if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
status_str,
&status_str_len,
(LPDWORD)headers_str)) {
jk_log(logger, JK_LOG_ERROR,
"HSE_REQ_SEND_RESPONSE_HEADER failed with error=%08x",
GetLastError());
rv = JK_FALSE;
}
if (headers_str)
free(headers_str);
if (status_str)
free(status_str);
}
JK_TRACE_EXIT(logger);
return rv;
}
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;
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG,
"Preparing to read %d bytes. "
"ECB reports %d bytes total, with %d available.",
l, p->lpEcb->cbTotalBytes, p->lpEcb->cbAvailable);
}
*a = 0;
if (l) {
char *buf = b;
DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
if (already_read >= l) {
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG,
"Already read %d bytes - supplying %d bytes from buffer",
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) {
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG,
"Supplying %d bytes from buffer",
already_read);
}
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 (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG,
"Attempting to read %d bytes from client", l);
}
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)
{
BOOL rv = TRUE;
ULONG http_filter_revision = HTTP_FILTER_REVISION;
pVer->dwFilterVersion = pVer->dwServerFilterVersion;
if (pVer->dwFilterVersion > http_filter_revision) {
pVer->dwFilterVersion = http_filter_revision;
}
if (!is_inited) {
rv = initialize_extension();
}
if (auth_notification_flags == SF_NOTIFY_AUTH_COMPLETE) {
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;
}
else {
pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
SF_NOTIFY_SECURE_PORT |
SF_NOTIFY_NONSECURE_PORT |
SF_NOTIFY_PREPROC_HEADERS;
}
StringCbCopy(pVer->lpszFilterDesc, SF_MAX_FILTER_DESC_LEN, VERSION_STRING);
return rv;
}
#define AP_REG_ICASE 0x01 /** use a case-insensitive match */
#define AP_REG_NEWLINE 0x02 /** don't match newlines against '.' etc */
#define AP_REG_NOTBOL 0x04 /** ^ will not match against start-of-string */
#define AP_REG_NOTEOL 0x08 /** $ will not match against end-of-string */
#define AP_REG_EXTENDED (0) /** unused */
#define AP_REG_NOSUB (0) /** unused */
/** The max number of regex captures that can be expanded by ap_pregsub */
#define AP_MAX_REG_MATCH 10
/* Error values: */
enum {
AP_REG_ASSERT = 1, /** internal error ? */
AP_REG_ESPACE, /** failed to get memory */
AP_REG_INVARG, /** invalid argument */
AP_REG_NOMATCH /** match failed */
};
/* The structure representing a compiled regular expression. */
typedef struct {
void *re_pcre;
size_t re_nsub;
size_t re_erroffset;
const char *real;
const char *fake;
} ap_regex_t;
/* The structure in which a captured offset is returned. */
typedef struct {
int rm_so;
int rm_eo;
} ap_regmatch_t;
/* Table of error strings corresponding to POSIX error codes; must be
* kept in synch with include/ap_regex.h's AP_REG_E* definitions. */
static const char *const pstring[] = {
"", /* Dummy for value 0 */
"internal error", /* AP_REG_ASSERT */
"failed to get memory", /* AP_REG_ESPACE */
"bad argument", /* AP_REG_INVARG */
"match failed" /* AP_REG_NOMATCH */
};
static size_t ap_regerror(int errcode, const ap_regex_t *preg,
char *errbuf, size_t errbuf_size)
{
const char *message, *addmessage;
size_t length, addlength;
message = (errcode >= (int)(sizeof(pstring)/sizeof(char *))) ?
"unknown error code" : pstring[errcode];
length = strlen(message) + 1;
addmessage = " at offset ";
addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
strlen(addmessage) + 6 : 0;
if (errbuf_size > 0) {
if (addlength > 0 && errbuf_size >= length + addlength)
StringCbPrintf(errbuf, sizeof(errbuf), "%s%s%-6d",
message, addmessage,
(int)preg->re_erroffset);
else {
strncpy(errbuf, message, errbuf_size - 1);
errbuf[errbuf_size-1] = 0;
}
}
return length + addlength;
}
/*************************************************
* Free store held by a regex *
*************************************************/
static void ap_regfree(ap_regex_t *preg)
{
(pcre_free)(preg->re_pcre);
}
/*************************************************
* Compile a regular expression *
*************************************************/
/*
Arguments:
preg points to a structure for recording the compiled expression
pattern the pattern to compile
cflags compilation flags
Returns: 0 on success
various non-zero codes on failure
*/
static int ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags)
{
const char *errorptr;
int erroffset;
int options = 0;
if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS;
if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
preg->re_erroffset = erroffset;
if (preg->re_pcre == NULL) return AP_REG_INVARG;
preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
return 0;
}
/*************************************************
* Match a regular expression *
*************************************************/
/* Unfortunately, PCRE requires 3 ints of working space for each captured
substring, so we have to get and release working store instead of just using
the POSIX structures as was done in earlier releases when PCRE needed only 2
ints. However, if the number of possible capturing brackets is small, use a
block of store on the stack, to reduce the use of malloc/free. The threshold is
in a macro that can be changed at configure time. */
static int ap_regexec(const ap_regex_t *preg, const char *string,
int nmatch, ap_regmatch_t pmatch[],
int eflags)
{
int rc;
int options = 0;
int *ovector = NULL;
int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
int allocated_ovector = 0;
if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
((ap_regex_t *)preg)->re_erroffset = (size_t)(-1); /* Only has meaning after compile */
if (nmatch > 0) {
if (nmatch <= POSIX_MALLOC_THRESHOLD) {
ovector = &(small_ovector[0]);
}
else {
ovector = (int *)malloc(sizeof(int) * nmatch * 3);
if (ovector == NULL)
return AP_REG_ESPACE;
allocated_ovector = 1;
}
}
rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string,
(int)strlen(string),
0, options, ovector, nmatch * 3);
if (rc == 0)
rc = nmatch; /* All captured slots were filled in */
if (rc >= 0) {
int i;
for (i = 0; i < rc; i++) {
pmatch[i].rm_so = ovector[i*2];
pmatch[i].rm_eo = ovector[i*2+1];
}
if (allocated_ovector)
free(ovector);
for (; i < nmatch; i++)
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
return 0;
}
else {
if (allocated_ovector)
free(ovector);
switch(rc) {
case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH;
case PCRE_ERROR_NULL: return AP_REG_INVARG;
case PCRE_ERROR_BADOPTION: return AP_REG_INVARG;
case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG;
case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT;
case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE;
#ifdef PCRE_ERROR_MATCHLIMIT
case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE;
#endif
#ifdef PCRE_ERROR_BADUTF8
case PCRE_ERROR_BADUTF8: return AP_REG_INVARG;
#endif
#ifdef PCRE_ERROR_BADUTF8_OFFSET
case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG;
#endif
default: return AP_REG_ASSERT;
}
}
}
/* This function substitutes for $0-$9, filling in regular expression
* submatches. Pass it the same nmatch and pmatch arguments that you
* passed ap_regexec(). pmatch should not be greater than the maximum number
* of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
*
* input should be the string with the $-expressions, source should be the
* string that was matched against.
*
* It returns the substituted string, or NULL on error.
*
* Parts of this code are based on Henry Spencer's regsub(), from his
* AT&T V8 regexp package.
*/
static char *ap_pregsub(const char *input,
const char *source, size_t nmatch,
ap_regmatch_t pmatch[])
{
const char *src = input;
char *dest, *dst;
char c;
size_t no;
int len;
if (!source)
return NULL;
if (!nmatch)
return strdup(src);
/* First pass, find the size */
len = 0;
while ((c = *src++) != '\0') {
if (c == '&')
no = 0;
else if (c == '$' && isdigit((unsigned char)*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
c = *src++;
len++;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
len += pmatch[no].rm_eo - pmatch[no].rm_so;
}
}
dest = dst = calloc(1, len + 1);
/* Now actually fill in the string */
src = input;
while ((c = *src++) != '\0') {
if (c == '&')
no = 0;
else if (c == '$' && isdigit((unsigned char)*src))
no = *src++ - '0';
else
no = 10;
if (no > 9) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
c = *src++;
*dst++ = c;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
len = pmatch[no].rm_eo - pmatch[no].rm_so;
memcpy(dst, source + pmatch[no].rm_so, len);
dst += len;
}
}
*dst = '\0';
return dest;
}
static int 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 (*src == '~')
continue; /* Skip regexp rewrites */
if (strncmp(uri, src, strlen(src)) == 0) {
StringCbCopy(buf, INTERNET_MAX_URL_LENGTH, jk_map_value_at(rewrite_map, i));
StringCbCat(buf, INTERNET_MAX_URL_LENGTH, uri + strlen(src));
StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, buf);
return 1;
}
}
}
return 0;
}
static int rregex_rewrite(char *uri)
{
ap_regmatch_t regm[AP_MAX_REG_MATCH];
if (rregexp_map) {
int i;
for (i = 0; i < jk_map_size(rregexp_map); i++) {
ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
if (!ap_regexec(regexp, uri, AP_MAX_REG_MATCH, regm, 0)) {
char *subs = ap_pregsub(regexp->fake, uri,
AP_MAX_REG_MATCH, regm);
if (subs) {
char buf[INTERNET_MAX_URL_LENGTH];
size_t diffsz = strlen(subs) - (regm[0].rm_eo - regm[0].rm_so);
memcpy(&buf[0], uri, regm[0].rm_so);
StringCbCopy(&buf[regm[0].rm_so], INTERNET_MAX_URL_LENGTH - regm[0].rm_so, subs);
StringCbCat(&buf[0], INTERNET_MAX_URL_LENGTH, uri + regm[0].rm_eo);
StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, &buf[0]);
free(subs);
return 1;
}
}
}
}
return 0;
}
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 (auth_notification_flags == dwNotificationType) {
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];
char squery[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 (auth_notification_flags == SF_NOTIFY_AUTH_COMPLETE) {
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';
StringCbCopy(squery, INTERNET_MAX_URL_LENGTH, query);
}
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) {
StringCbCat(Host, INTERNET_MAX_URL_LENGTH, ":");
StringCbCat(Host, INTERNET_MAX_URL_LENGTH, Port);
}
if (szHost > 0) {
StringCbCat(snuri, INTERNET_MAX_URL_LENGTH, Host);
StringCbCat(snuri, INTERNET_MAX_URL_LENGTH, 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 (worker) {
char *forwardURI;
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. "
"Somebody try to hack into the site!!!",
uri);
write_error_response(pfc, "404 Not Found",
HTML_ERROR_404);
return SF_STATUS_REQ_FINISHED;
}
/* This is a servlet, should redirect ... */
if (JK_IS_DEBUG_LEVEL(logger))
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.
*/
if (JK_IS_DEBUG_LEVEL(logger)) {
char duri[INTERNET_MAX_URL_LENGTH];
StringCbCopy(duri, INTERNET_MAX_URL_LENGTH, forwardURI);
if (simple_rewrite(forwardURI)) {
jk_log(logger, JK_LOG_DEBUG,
"rewriten URI [%s]->[%s]",
duri, forwardURI);
}
else if (rregex_rewrite(forwardURI)) {
jk_log(logger, JK_LOG_DEBUG,
"rewriten URI [%s]->[%s]",
duri, forwardURI);
}
}
else {
if (!simple_rewrite(forwardURI))
rregex_rewrite(forwardURI);
}
if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
((strlen(squery) > 0)
? !AddHeader(pfc, QUERY_HEADER_NAME, squery) : 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));
StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
pfc->pFilterContext = ld;
} else {
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
memset(ld, 0, sizeof(isapi_log_data_t));
StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
}
}
else {
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"[%s] is not a servlet url", uri);
if (strip_session) {
char *jsessionid = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
if (jsessionid) {
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"removing session identifier [%s] for non servlet url [%s]",
jsessionid, uri);
*jsessionid = '\0';
SetHeader(pfc, "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);
StringCbCopy(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, 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] = { 0 };
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;
if (JK_IS_DEBUG_LEVEL(logger))
jk_log(logger, JK_LOG_DEBUG,
"service() returned OK");
}
else {
jk_log(logger, JK_LOG_ERROR,
"service() failed with http error %d", is_error);
lpEcb->dwHttpStatusCode = is_error;
write_error_message(lpEcb, is_error);
}
e->done(&e, logger);
}
else {
jk_log(logger, JK_LOG_ERROR,
"Failed to obtain an endpoint to service request - "
"your connection_pool_size is probably less than the threads in your web server!");
}
}
else {
jk_log(logger, JK_LOG_ERROR,
"could not get a worker for name %s",
worker_name);
}
}
else {
jk_log(logger, JK_LOG_ERROR,
"failed to init service for request.");
}
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);
}
if (rewrite_map) {
jk_map_free(&rewrite_map);
}
if (rregexp_map) {
int i;
for (i = 0; i < jk_map_size(rregexp_map); i++) {
ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
if (regexp) {
ap_regfree(regexp);
free(regexp);
}
}
jk_map_free(&rregexp_map);
}
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_PATH];
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 */
StringCbPrintf(URI_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
StringCbPrintf(QUERY_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
StringCbPrintf(WORKER_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
StringCbPrintf(TOMCAT_TRANSLATE_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, TOMCAT_TRANSLATE_HEADER_NAME_BASE, hInst);
StringCbPrintf(HTTP_URI_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
StringCbPrintf(HTTP_QUERY_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
StringCbPrintf(HTTP_WORKER_HEADER_NAME, MAX_PATH, 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)
{
char shm_name[MAX_PATH];
int rc = JK_FALSE;
if (!jk_open_file_logger(&logger, log_file, log_level)) {
logger = NULL;
}
StringCbCopy(shm_name, MAX_PATH, SHM_DEF_NAME);
if (*serverName) {
size_t i;
StringCbCat(shm_name, MAX_PATH, "_");
StringCbCat(shm_name, MAX_PATH, serverName);
for(i = 0; i < strlen(shm_name); i++) {
shm_name[i] = toupper(shm_name[i]);
if (!isalnum(shm_name[i]))
shm_name[i] = '_';
}
}
/*
* Create named shared memory for each server
*/
jk_shm_open(shm_name, shm_config_size, logger);
jk_set_worker_def_cache_size(DEFAULT_WORKER_THREADS);
/* 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_load_properties(rewrite_map, rewrite_rule_file, NULL, logger)) {
int i;
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG, "Loaded rewrite rule file %s.",
rewrite_rule_file);
}
jk_map_alloc(&rregexp_map);
for (i = 0; i < jk_map_size(rewrite_map); i++) {
const char *src = jk_map_name_at(rewrite_map, i);
if (*src == '~') {
ap_regex_t *regexp = malloc(sizeof(ap_regex_t));
const char *val = jk_map_value_at(rewrite_map, i);
/* Skip leading tilde */
regexp->real = src + 1;
regexp->fake = val;
if (!ap_regcomp(regexp, regexp->real, AP_REG_EXTENDED)) {
jk_map_add(rregexp_map, regexp->real, regexp);
if (JK_IS_DEBUG_LEVEL(logger)) {
jk_log(logger, JK_LOG_DEBUG,
"Added regular expression rule %s -> %s",
regexp->real, regexp->fake);
}
}
else {
jk_log(logger, JK_LOG_ERROR,
"Unable to compile regular expression %s",
regexp->real);
free(regexp);
}
}
}
}
else {
jk_map_free(&rewrite_map);
rewrite_map = NULL;
}
}
if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
rc = JK_FALSE;
uw_map->fname = worker_mount_file;
uw_map->reload = worker_mount_reload;
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, 1, logger)) {
/* we add the URI->WORKER MAP since workers using AJP14 will feed it */
if (jk_map_resolve_references(workers_map, "worker.", 1, 1, logger) == JK_FALSE) {
jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
}
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;
}
}
}
if (rc) {
jk_log(logger, JK_LOG_INFO,
"isapi_redirect/%s initialized",
JK_VERSTRING);
}
return rc;
}
static int initialize_extension(void)
{
if (read_registry_init_data()) {
auth_notification_flags = get_auth_flags();
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[MAX_PATH];
int ok = JK_TRUE;
LPVOID src;
HKEY hkey;
jk_map_t *map = NULL;
if (jk_map_alloc(&map)) {
if (jk_map_read_properties(map, ini_file_name, NULL, 1, logger)) {
using_ini_file = JK_TRUE;
src = map;
}
else {
jk_map_free(&map);
}
}
if (!using_ini_file) {
long rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION,
(DWORD)0, KEY_READ, &hkey);
if (ERROR_SUCCESS != rc) {
return JK_FALSE;
}
else {
src = &hkey;
}
}
ok = ok && get_config_parameter(src, JK_LOG_FILE_TAG, log_file, sizeof(log_file));
if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf, sizeof(tmpbuf))) {
log_level = jk_parse_log_level(tmpbuf);
}
ok = ok && get_config_parameter(src, EXTENSION_URI_TAG, extension_uri, sizeof(extension_uri));
ok = ok && get_config_parameter(src, JK_WORKER_FILE_TAG, worker_file, sizeof(worker_file));
ok = ok && get_config_parameter(src, JK_MOUNT_FILE_TAG, worker_mount_file, sizeof(worker_mount_file));
get_config_parameter(src, URI_REWRITE_TAG, rewrite_rule_file, sizeof(rewrite_rule_file));
if (get_config_parameter(src, URI_SELECT_TAG, tmpbuf, sizeof(tmpbuf))) {
int opt = parse_uri_select(tmpbuf);
if (opt >= 0) {
uri_select_option = opt;
}
else {
ok = JK_FALSE;
}
}
shm_config_size = get_config_int(src, SHM_SIZE_TAG, JK_SHM_DEF_SIZE);
worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG, JK_URIMAP_DEF_RELOAD);
strip_session = get_config_bool(src, STRIP_SESSION_TAG, JK_FALSE);
use_auth_notification_flags = get_config_int(src, AUTH_COMPLETE_TAG, 1);
if (using_ini_file) {
jk_map_free(&map);
}
else {
RegCloseKey(hkey);
}
return ok;
}
static int get_config_parameter(LPVOID src, const char *tag,
char *val, DWORD sz)
{
const char *tmp = NULL;
if (using_ini_file) {
tmp = jk_map_get_string((jk_map_t*)src, tag, NULL);
if (tmp && (strlen(tmp) < sz)) {
StringCbCopy(val, sz, tmp);
return JK_TRUE;
}
else {
return JK_FALSE;
}
} else {
return get_registry_config_parameter(*((HKEY*)src), tag, val, sz);
}
}
static int get_config_int(LPVOID src, const char *tag, int def)
{
if (using_ini_file) {
return jk_map_get_int((jk_map_t*)src, tag, def);
} else {
int val;
if (get_registry_config_number(*((HKEY*)src), tag, &val) ) {
return val;
}
else {
return def;
}
}
}
static int get_config_bool(LPVOID src, const char *tag, int def)
{
if (using_ini_file) {
return jk_map_get_bool((jk_map_t*)src, tag, def);
} else {
char tmpbuf[128];
if (get_registry_config_parameter(*((HKEY*)src), tag,
tmpbuf, sizeof(tmpbuf))) {
return jk_get_bool_code(tmpbuf, def);
}
else {
return def;
}
}
}
static int get_registry_config_parameter(HKEY hkey,
const char *tag, char *b, DWORD sz)
{
DWORD type = 0;
LONG lrc;
sz = sz - 1; /* Reserve space for RegQueryValueEx to add null terminator */
b[sz] = '\0'; /* Null terminate in case RegQueryValueEx doesn't */
lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
return JK_FALSE;
}
return JK_TRUE;
}
static int get_registry_config_number(HKEY hkey,
const char *tag, int *val)
{
DWORD type = 0;
DWORD data = 0;
DWORD sz = sizeof(DWORD);
LONG lrc;
lrc = RegQueryValueEx(hkey, tag, (LPDWORD)0, &type, (LPBYTE)&data, &sz);
if ((ERROR_SUCCESS != lrc) || (type != REG_DWORD)) {
return JK_FALSE;
}
*val = data;
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->route = NULL;
s->start_response = start_response;
s->read = read;
s->write = write;
/* Yes we do want to reuse AJP connections */
s->disable_reuse = JK_FALSE;
s->flush = NULL;
s->flush_packets = JK_FALSE;
s->flush_header = JK_FALSE;
/* 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);
}
static int get_auth_flags()
{
HKEY hkey;
long rc;
int maj, sz;
int rv = SF_NOTIFY_PREPROC_HEADERS;
int use_auth = JK_FALSE;
/* Retreive the IIS version Major */
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
W3SVC_REGISTRY_KEY, (DWORD) 0, KEY_READ, &hkey);
if (ERROR_SUCCESS != rc) {
return rv;
}
sz = sizeof(int);
rc = RegQueryValueEx(hkey, "MajorVersion", NULL, NULL,
(LPBYTE) & maj, &sz);
if (ERROR_SUCCESS != rc) {
CloseHandle(hkey);
return rv;
}
CloseHandle(hkey);
if (use_auth_notification_flags && maj > 4)
rv = SF_NOTIFY_AUTH_COMPLETE;
return rv;
}