diff options
Diffstat (limited to 'rubbos/app/tomcat-connectors-1.2.32-src/native/iis/jk_isapi_plugin.c')
-rw-r--r-- | rubbos/app/tomcat-connectors-1.2.32-src/native/iis/jk_isapi_plugin.c | 3495 |
1 files changed, 0 insertions, 3495 deletions
diff --git a/rubbos/app/tomcat-connectors-1.2.32-src/native/iis/jk_isapi_plugin.c b/rubbos/app/tomcat-connectors-1.2.32-src/native/iis/jk_isapi_plugin.c deleted file mode 100644 index 7c9f67be..00000000 --- a/rubbos/app/tomcat-connectors-1.2.32-src/native/iis/jk_isapi_plugin.c +++ /dev/null @@ -1,3495 +0,0 @@ -/* - * 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: 1129429 $ * - ***************************************************************************/ - -// This define is needed to include wincrypt,h, needed to get client certificates -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif -#include <winsock2.h> -#include <httpext.h> -#include <httpfilt.h> -#include <wininet.h> - -#include "jk_global.h" -#include "jk_url.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 "jk_ajp13.h" -#include "pcre.h" - -#ifndef POSIX_MALLOC_THRESHOLD -#define POSIX_MALLOC_THRESHOLD (10) -#endif - -#include <strsafe.h> - -#define VERSION_STRING "Jakarta/ISAPI/" JK_EXPOSED_VERSION -#define FULL_VERSION_STRING "Jakarta/ISAPI/" JK_FULL_EXPOSED_VERSION -#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 WORKER_HEADER_INDEX_BASE ("TOMCATWORKERIDX") -#define TOMCAT_TRANSLATE_HEADER_NAME_BASE ("TOMCATTRANSLATE") -#ifndef USE_CGI_HEADERS -#define CONTENT_LENGTH ("CONTENT-LENGTH:") -#else -#define CONTENT_LENGTH ("CONTENT_LENGTH:") -#endif - -/* The HTTP_ form of the header for use in ExtensionProc */ -#define HTTP_HEADER_PREFIX "HTTP_" -#ifdef USE_CGI_HEADERS -#define HTTP_HEADER_PREFIX_LEN 5 -#endif - -/* 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_HEADER_PREFIX "%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 WORKER_HEADER_INDEX[MAX_PATH]; - -/* The variants of the special headers after IIS adds - * "HTTP_" to the front of them - */ -static char HTTP_URI_HEADER_NAME[MAX_PATH]; -static char HTTP_QUERY_HEADER_NAME[MAX_PATH]; -static char HTTP_WORKER_HEADER_NAME[MAX_PATH]; -static char HTTP_WORKER_HEADER_INDEX[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_SELECT_PROXY_VERB ("proxy") -#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") -#ifndef AUTOMATIC_AUTH_NOTIFICATION -#define AUTH_COMPLETE_TAG ("auth_complete") -#endif -#define REJECT_UNSAFE_TAG ("reject_unsafe") -#define WATCHDOG_INTERVAL_TAG ("watchdog_interval") -#define ENABLE_CHUNKED_ENCODING_TAG ("enable_chunked_encoding") -#define ERROR_PAGE_TAG ("error_page") - -#define LOG_ROTATION_TIME_TAG ("log_rotationtime") -#define LOG_FILESIZE_TAG ("log_filesize") - -/* HTTP standard headers */ -#define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE ("Transfer-Encoding: chunked") -#define TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN (26) -#define TRANSFER_ENCODING_HEADER_NAME ("Transfer-Encoding") -#define TRANSFER_ENCODING_HEADER_NAME_LEN (17) -#define TRANSFER_ENCODING_IDENTITY_VALUE ("identity") -#define TRANSFER_ENCODING_CHUNKED_VALUE ("chunked") -#define TRANSFER_ENCODING_CHUNKED_VALUE_LEN (7) - -#define CONTENT_LENGTH_HEADER_NAME ("Content-Length") -#define CONTENT_LENGTH_HEADER_NAME_LEN (14) - -#define CONNECTION_HEADER_NAME ("Connection") -#define CONNECTION_CLOSE_VALUE ("Close") - -#define TRANSLATE_HEADER ("Translate:") -#define TRANSLATE_HEADER_NAME ("Translate") -#define TRANSLATE_HEADER_NAME_LC ("translate") - -/* HTTP protocol CRLF */ -#define CRLF ("\r\n") -#define CRLF_LEN (2) - -/* Transfer-Encoding: chunked content trailer */ -#define CHUNKED_ENCODING_TRAILER ("0\r\n\r\n") -#define CHUNKED_ENCODING_TRAILER_LEN (5) - -/* Hex of chunk length (one char per byte) + CRLF + terminator. */ -#define CHUNK_HEADER_BUFFER_SIZE (sizeof(unsigned int)*2+CRLF_LEN+1) - -#define BAD_REQUEST -1 -#define BAD_PATH -2 -#define MAX_SERVERNAME 128 -#define MAX_INSTANCEID 32 -#define MAX_PACKET_SIZE 65536 - -char HTML_ERROR_HEAD[] = "<!--\n" - " Licensed to the Apache Software Foundation (ASF) under one or more\n" - " contributor license agreements. See the NOTICE file distributed with\n" - " this work for additional information regarding copyright ownership.\n" - " The ASF licenses this file to You under the Apache License, Version 2.0\n" - " (the \"License\"); you may not use this file except in compliance with\n" - " the License. You may obtain a copy of the License at\n\n" - " http://www.apache.org/licenses/LICENSE-2.0\n\n" - " Unless required by applicable law or agreed to in writing, software\n" - " distributed under the License is distributed on an \"AS IS\" BASIS,\n" - " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" - " See the License for the specific language governing permissions and\n" - " limitations under the License.\n" - " -->\n" - "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"\n" - "\"http://www.w3c.org/TR/REC-html40/loose.dtd\">\n" - "<HTML>\n<HEAD>\n" - "<META http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n" - "<STYLE TYPE=\"text/css\">\n" - " body {\n" - " color: #000000;\n" - " background-color: #FFFFFF;\n" - " font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;\n" - " font-size: 9pt;\n" - " margin: 10px 10px;\n" - " }\n" - " p#footer {\n" - " text-align: right;\n" - " font-size: 80%;\n" - " }\n" - "</STYLE>\n"; - -#define HTML_ERROR_BODY_FMT "<TITLE>%s!</TITLE>\n</HEAD>\n<BODY>\n<H1>%s!</H1>\n<P>\n%s\n</P>\n" - -char HTML_ERROR_TAIL[] = "<P>\n<BR/> <BR/> <BR/> <BR/> \n" - FULL_VERSION_STRING "\n" - "<BR/> \n" - "<HR/>\n" - "<P id=\"footer\">\n" - "Copyright © 1999-2011 Apache Software Foundation<BR/>\n" - "All Rights Reserved\n" - "</P>\n</BODY>\n</HTML>\n"; - -static struct error_reasons { - int status; - const char *reason; - const char *title; - const char *description; -} error_reasons[] = { - { 100, - "Continue", - NULL, - NULL - }, - { 101, - "Switching Protocols", - NULL, - NULL - }, - { 200, - "OK", - NULL, - NULL - }, - { 201, - "Created", - NULL, - NULL - }, - { 202, - "Accepted", - NULL, - NULL - }, - { 203, - "Non-Authoritative Information", - NULL, - NULL - }, - { 204, - "No Content", - NULL, - NULL - }, - { 205, - "Reset Content", - NULL, - NULL - }, - { 206, - "Partial Content", - NULL, - NULL - }, - { 300, - "Multiple Choices", - NULL, - NULL - }, - { 301, - "Moved Permanently", - NULL, - NULL - }, - { 302, - "Moved Temporarily", - NULL, - NULL - }, - { 303, - "See Other", - NULL, - NULL - }, - { 304, - "Not Modified", - NULL, - NULL - }, - { 305, - "Use Proxy", - NULL, - NULL - }, - { 400, - "Bad Request", - "Bad Request", - "Your browser (or proxy) sent a request that " - "this server could not understand." - }, - { 401, - "Unauthorized", - "Access is denied due to invalid credentials", - "You do not have permission to view this directory or " - "page using the credentials that you supplied." - }, - { 402, - "Payment Required", - NULL, - NULL - }, - { 403, - "Forbidden", - "Access is denied", - "You do not have permission to view this directory or page " - "using the credentials that you supplied." - }, - { 404, - "Not Found", - "The requested URL was not found on this server", - "If you entered the URL manually please check your" - "spelling and try again." - }, - { 405, - "Method Not Allowed", - "HTTP method used to access this page is not allowed", - "The page you are looking for cannot be displayed because an " - "invalid method (HTTP method) was used to attempt access." - }, - { 406, - "Not Acceptable", - "Client browser does not accept the MIME type of the requested page", - "The page you are looking for cannot be opened by your browser " - "because it has a file name extension that your browser " - "does not accept." - }, - { 407, - "Proxy Authentication Required", - NULL, - "The client must first authenticate itself with the proxy" - }, - { 408, - "Request Timeout", - NULL, - "The client did not produce a request within the time " - "that the server was prepared to wait." - }, - { 409, - "Conflict", - NULL, - "The request could not be completed due to a conflict with " - "the current state of the resource." - }, - { 410, - "Gone", - NULL, - "The requested resource is no longer available at the " - "server and no forwarding address is known." - }, - { 411, - "Length Required", - NULL, - "The server refuses to accept the request without a " - "defined Content-Length." - }, - { 412, - "Precondition Failed", - NULL, - "The precondition given in one or more of the request " - "header fields evaluated to false when it was tested on the server." - }, - { 413, - "Request Entity Too Large", - NULL, - "The HTTP method does not allow the data transmitted, " - "or the data volume exceeds the capacity limit." - }, - { 414, - "Request-URI Too Long", - "Submitted URI too large", - "The length of the requested URL exceeds the capacity limit " - "for this server. The request cannot be processed." - }, - { 415, - "Unsupported Media Type", - NULL, - "The server is refusing to service the request because the " - "entity of the request is in a format not supported by the " - "requested resource for the requested method." - }, - { 500, - "Internal Server Error", - NULL, - "The server encountered an internal error and was " - "unable to complete your request." - }, - { 501, - "Not Implemented", - NULL, - "The server does not support the functionality required " - "to fulfill the request." - }, - { 502, - "Bad Gateway", - NULL, - "There is a problem with the page you are looking for, " - "and it cannot be displayed. When the Web server (while " - "acting as a gateway or proxy) contacted the upstream content " - "server, it received an invalid response from the content server." - }, - { 503, - "Service Unavailable", - "Service Temporary Unavailable", - "The server is temporarily unable to service your " - "request due to maintenance downtime or capacity problems. " - "Please try again later." - }, - { 504, - "Gateway Timeout", - NULL, - "The server, while acting as a gateway or proxy, " - "did not receive a timely response from the upstream server" - }, - { 505, - "HTTP Version Not Supported", - NULL, - "The server does not support, or refuses to support, the " - "HTTP protocol version that was used in the request message." - }, - { 0, - NULL, - NULL, - NULL - } -}; - - - -#define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)") -#ifdef USE_CGI_HEADERS -#define JK_TOLOWER(x) ((char)tolower((BYTE)(x))) -#endif - -#define GET_SERVER_VARIABLE_VALUE(name, place) \ - do { \ - (place) = NULL; \ - huge_buf_sz = MAX_PACKET_SIZE; \ - 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 = MAX_PACKET_SIZE; \ - if (get_server_value(private_data->lpEcb, \ - (name), \ - huge_buf, \ - huge_buf_sz)) { \ - (place) = atoi(huge_buf); \ - if (((place) == 0) && (errno == EINVAL || \ - errno == ERANGE)) { \ - (place) = def; \ - } \ - } else { \ - (place) = def; \ - } } while(0) - -static char dll_file_path[MAX_PATH]; -static char ini_file_name[MAX_PATH]; -static int using_ini_file = JK_FALSE; -static JK_CRIT_SEC init_cs; -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_map_t *jk_environment_map = NULL; - -static jk_logger_t *logger = NULL; -static JK_CRIT_SEC log_cs; -static char *SERVER_NAME = "SERVER_NAME"; -static char *SERVER_SOFTWARE = "SERVER_SOFTWARE"; -static char *INSTANCE_ID = "INSTANCE_ID"; -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 char log_file_effective[MAX_PATH * 2]; -static int log_level = JK_LOG_DEF_LEVEL; -static long log_rotationtime = 0; -static time_t log_next_rotate_time = 0; -static ULONGLONG log_filesize = 0; - -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 size_t shm_config_size = 0; -static int strip_session = 0; -#ifndef AUTOMATIC_AUTH_NOTIFICATION -static int use_auth_notification_flags = 1; -#endif -static int chunked_encoding_enabled = JK_FALSE; -static int reject_unsafe = 0; -static int watchdog_interval = 0; -static HANDLE watchdog_handle = NULL; -static char error_page_buf[INTERNET_MAX_URL_LENGTH] = {0}; -static char *error_page = NULL; - -#define URI_SELECT_OPT_PARSED 0 -#define URI_SELECT_OPT_UNPARSED 1 -#define URI_SELECT_OPT_ESCAPED 2 -#define URI_SELECT_OPT_PROXY 3 - -static int uri_select_option = URI_SELECT_OPT_PROXY; - -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; - - unsigned int bytes_read_so_far; - int chunk_content; /* Whether we're responding with Transfer-Encoding: chunked content */ - 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]; - int request_matched; /* Whether this request (within a multi-request connection) - was handled and needs the log values adjusted */ -}; - -typedef struct iis_info_t iis_info_t; -struct iis_info_t { - int major; /* The major version */ - int minor; /* The minor version */ - DWORD filter_notify_event; /* The primary filter SF_NOTIFY_* event */ -}; - -static iis_info_t iis_info; - -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 iis_read(jk_ws_service_t *s, - void *b, unsigned int l, unsigned int *a); - -static int JK_METHOD iis_write(jk_ws_service_t *s, const void *b, unsigned int l); - -static int JK_METHOD iis_done(jk_ws_service_t *s); - -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 JK_METHOD iis_log_to_file(jk_logger_t *l, int level, - int used, char *what); - -static BOOL 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_iis_info(iis_info_t *info); - -static int isapi_write_client(isapi_private_data_t *p, const char *buf, unsigned int write_length); - -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 const char *status_reason(int status) -{ - struct error_reasons *r; - - r = error_reasons; - while (r->status <= status) { - if (r->status == status) - return r->reason; - else - r++; - } - return "No Reason"; -} - -static const char *status_title(int status) -{ - struct error_reasons *r; - - r = error_reasons; - while (r->status <= status) { - if (r->status == status) { - if (r->title) - return r->title; - else - return r->reason; - } - else - r++; - } - return "Unknown Error"; -} - -static const char *status_description(int status) -{ - struct error_reasons *r; - - r = error_reasons; - while (r->status <= status) { - if (r->status == status) { - if (r->description) - return r->description; - else - return r->reason; - } - else - r++; - } - return "Unknown Error"; -} - -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, int err) -{ - char status[MAX_PATH]; - char body[8192] = ""; - DWORD len; - - /* reject !!! */ - pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0); - StringCbPrintf(status, MAX_PATH, "%d %s", err, status_reason(err)); - pfc->ServerSupportFunction(pfc, - SF_REQ_SEND_RESPONSE_HEADER, - status, 0, 0); - len = (DWORD)(sizeof(HTML_ERROR_HEAD) - 1); - pfc->WriteClient(pfc, HTML_ERROR_HEAD, &len, - HSE_IO_SYNC); - StringCbPrintf(body, sizeof(body), HTML_ERROR_BODY_FMT, - status_reason(err), status_title(err), - status_description(err)); - len = (DWORD)(strlen(body)); - pfc->WriteClient(pfc, body, &len, - HSE_IO_SYNC); - len = (DWORD)(sizeof(HTML_ERROR_TAIL) - 1); - pfc->WriteClient(pfc, HTML_ERROR_TAIL, &len, - HSE_IO_SYNC); -} - -static void write_error_message(LPEXTENSION_CONTROL_BLOCK lpEcb, int err) -{ - DWORD len; - char status[MAX_PATH]; - char body[8192] = ""; - - if (error_page) { - char error_page_url[INTERNET_MAX_URL_LENGTH] = ""; - int len_of_error_page; - StringCbPrintf(error_page_url, INTERNET_MAX_URL_LENGTH, - (LPCSTR)error_page, err); - len_of_error_page = (int)strlen(error_page_url); - if (!lpEcb->ServerSupportFunction(lpEcb->ConnID, - HSE_REQ_SEND_URL_REDIRECT_RESP, - error_page_url, - (LPDWORD)&len_of_error_page, - (LPDWORD)NULL)) { - lpEcb->dwHttpStatusCode = err; - } - else { - return; - } - } - lpEcb->dwHttpStatusCode = err; - - StringCbPrintf(status, MAX_PATH, "%d %s", err, status_reason(err)); - lpEcb->ServerSupportFunction(lpEcb->ConnID, - HSE_REQ_SEND_RESPONSE_HEADER, - status, - 0, - (LPDWORD)CONTENT_TYPE); - /* First write the HEAD */ - len = (DWORD)(sizeof(HTML_ERROR_HEAD) - 1); - lpEcb->WriteClient(lpEcb->ConnID, - HTML_ERROR_HEAD, &len, - HSE_IO_SYNC); - StringCbPrintf(body, sizeof(body), HTML_ERROR_BODY_FMT, - status_reason(err), status_title(err), - status_description(err)); - len = (DWORD)(strlen(body)); - lpEcb->WriteClient(lpEcb->ConnID, - body, &len, - HSE_IO_SYNC); - len = (DWORD)(sizeof(HTML_ERROR_TAIL) - 1); - lpEcb->WriteClient(lpEcb->ConnID, - HTML_ERROR_TAIL, &len, - HSE_IO_SYNC); - -} - - -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) -{ - 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 we use proxy error pages, still pass - * through context headers needed for special status codes. - */ - if (s->extension.use_server_error_pages && - status >= s->extension.use_server_error_pages) { - if (status == JK_HTTP_UNAUTHORIZED) { - int found = JK_FALSE; - unsigned int h; - for (h = 0; h < num_of_headers; h++) { - if (!strcasecmp(header_names[h], "WWW-Authenticate")) { - /* - * TODO: we need to save a copy of header_values[h] - * for later reuse in write_error_message() - * which is called later on form HttpExtensionProc - * because of use_server_error_pages. - */ - found = JK_TRUE; - } - } - if (found == JK_FALSE) { - jk_log(logger, JK_LOG_INFO, - "origin server sent 401 without" - " WWW-Authenticate header"); - } - } - return JK_TRUE; - } - - if (!s->response_started) { - char *status_str = NULL; - char *headers_str = NULL; - BOOL keep_alive = FALSE; /* Whether the downstream or us can supply content length */ - BOOL rc; - size_t i, len_of_headers = 0; - - s->response_started = JK_TRUE; - - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Starting response for URI '%s' (protocol %s)", - s->req_uri, s->protocol); - } - - /* - * 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); - - if (chunked_encoding_enabled) { - /* Check if we've got an HTTP/1.1 response */ - if (!strcasecmp(s->protocol, "HTTP/1.1")) { - keep_alive = TRUE; - /* Chunking only when HTTP/1.1 client and enabled */ - p->chunk_content = JK_TRUE; - } - } - - /* - * Create response headers string - */ - - /* Calculate length of headers block */ - for (i = 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 */ - } - - /* - * Exclude status codes that MUST NOT include message bodies - */ - if ((status == 204) || (status == 205) || (status == 304)) { - p->chunk_content = JK_FALSE; - /* Keep alive is still possible */ - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Response status %d implies no message body", status ); - } - if (p->chunk_content) { - for (i = 0; i < num_of_headers; i++) { - /* Check the downstream response to see whether - * it's appropriate to chunk the response content - * and whether it supports keeping the connection open. - - * This implements the rules for HTTP/1.1 message length determination - * with the exception of multipart/byteranges media types. - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 - */ - if (!strcasecmp(CONTENT_LENGTH_HEADER_NAME, header_names[i])) { - p->chunk_content = JK_FALSE; - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Response specifies Content-Length" ); - } - else if (!strcasecmp(CONNECTION_HEADER_NAME, header_names[i]) - && !strcasecmp(CONNECTION_CLOSE_VALUE, header_values[i])) { - keep_alive = FALSE; - p->chunk_content = JK_FALSE; - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Response specifies Connection: Close" ); - } - else if (!strcasecmp(TRANSFER_ENCODING_HEADER_NAME, header_names[i]) - && !strcasecmp(TRANSFER_ENCODING_IDENTITY_VALUE, header_values[i])) { - /* HTTP states that this must include 'chunked' as the last value. - * 'identity' is the same as absence of the header */ - p->chunk_content = JK_FALSE; - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Response specifies Transfer-Encoding" ); - } - } - - /* Provide room in the buffer for the Transfer-Encoding header if we use it. */ - len_of_headers += TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE_LEN + 2; - } - - /* Allocate and init the headers string */ - len_of_headers += 3; /* crlf and terminating null char */ - headers_str = (char *)malloc(len_of_headers); - headers_str[0] = '\0'; - - /* Copy headers into headers block for sending */ - 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); - } - - if (p->chunk_content) { - /* Configure the response if chunked encoding is used */ - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Using Transfer-Encoding: chunked"); - - /** We will supply the transfer-encoding to allow IIS to keep the connection open */ - keep_alive = TRUE; - - /* Indicate to the client that the content will be chunked - - We've already reserved space for this */ - StringCbCat(headers_str, len_of_headers, TRANSFER_ENCODING_CHUNKED_HEADER_COMPLETE); - StringCbCat(headers_str, len_of_headers, CRLF); - } - - /* Terminate the headers */ - StringCbCat(headers_str, len_of_headers, CRLF); - - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "%ssing Keep-Alive", (keep_alive ? "U" : "Not u")); - - if (keep_alive) { - HSE_SEND_HEADER_EX_INFO hi; - - /* Fill in the response */ - hi.pszStatus = status_str; - hi.pszHeader = headers_str; - hi.cchStatus = (DWORD)strlen(status_str); - hi.cchHeader = (DWORD)strlen(headers_str); - - /* - * Using the extended form of the API means we have to get this right, - * i.e. IIS won't keep connections open if there's a Content-Length and close them if there isn't. - */ - hi.fKeepConn = keep_alive; - - /* Send the response to the client */ - rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, - HSE_REQ_SEND_RESPONSE_HEADER_EX, - &hi, - NULL, NULL); - } - else { - DWORD status_str_len = (DWORD)strlen(status_str); - /* Old style response - forces Connection: close if Tomcat response doesn't - specify necessary details to allow keep alive */ - rc = p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, - HSE_REQ_SEND_RESPONSE_HEADER, - status_str, - &status_str_len, - (LPDWORD)headers_str); - } - - if (!rc) { - jk_log(logger, JK_LOG_ERROR, - "HSE_REQ_SEND_RESPONSE_HEADER%s failed with error=%d (0x%08x)", - (keep_alive ? "_EX" : ""), GetLastError(), 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 iis_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)) { - /* ReadClient will succeed with dwSize == 0 for last chunk - if request chunk encoded */ - *a += l; - } - else { - jk_log(logger, JK_LOG_ERROR, - "ReadClient failed with %d (0x%08x)", GetLastError(), 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; -} - -/* - * Writes a buffer to the ISAPI response. - */ -static int isapi_write_client(isapi_private_data_t *p, const char *buf, unsigned int write_length) -{ - unsigned int written = 0; - DWORD try_to_write = 0; - - JK_TRACE_ENTER(logger); - - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Writing %d bytes of data to client", write_length); - - while (written < write_length) { - try_to_write = write_length - written; - if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, - (LPVOID)(buf + written), &try_to_write, HSE_IO_SYNC)) { - jk_log(logger, JK_LOG_ERROR, - "WriteClient failed with %d (0x%08x)", GetLastError(), GetLastError()); - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - written += try_to_write; - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Wrote %d bytes of data successfully", try_to_write); - } - JK_TRACE_EXIT(logger); - return JK_TRUE; -} - -/* - * Write content to the response. - * If chunked encoding has been enabled and the client supports it - *(and it's appropriate for the response), then this will write a - * single "Transfer-Encoding: chunked" chunk - */ -static int JK_METHOD iis_write(jk_ws_service_t *s, const void *b, unsigned int l) -{ - JK_TRACE_ENTER(logger); - - if (!l) { - JK_TRACE_EXIT(logger); - return JK_TRUE; - } - - if (s && s->ws_private && b) { - isapi_private_data_t *p = s->ws_private; - const char *buf = (const char *)b; - - if (!p) { - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - - if (!s->response_started) { - start_response(s, 200, NULL, NULL, NULL, 0); - } - - if (p->chunk_content == JK_FALSE) { - if (isapi_write_client(p, buf, l) == JK_FALSE) { - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - } - else { - char chunk_header[CHUNK_HEADER_BUFFER_SIZE]; - - /* Construct chunk header : HEX CRLF*/ - StringCbPrintf(chunk_header, CHUNK_HEADER_BUFFER_SIZE, "%X%s", l, CRLF); - - if (iis_info.major >= 6) { - HSE_RESPONSE_VECTOR response_vector; - HSE_VECTOR_ELEMENT response_elements[3]; - - response_elements[0].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER; - response_elements[0].pvContext = chunk_header; - response_elements[0].cbOffset = 0; - response_elements[0].cbSize = strlen(chunk_header); - - response_elements[1].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER; - response_elements[1].pvContext = (PVOID)buf; - response_elements[1].cbOffset = 0; - response_elements[1].cbSize = l; - - response_elements[2].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER; - response_elements[2].pvContext = CRLF; - response_elements[2].cbOffset = 0; - response_elements[2].cbSize = CRLF_LEN; - - response_vector.dwFlags = HSE_IO_SYNC; - response_vector.pszStatus = NULL; - response_vector.pszHeaders = NULL; - response_vector.nElementCount = 3; - response_vector.lpElementArray = response_elements; - - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, - "Using vector write for chunk encoded %d byte chunk", l); - - if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, - HSE_REQ_VECTOR_SEND, - &response_vector, - (LPDWORD)NULL, - (LPDWORD)NULL)) { - jk_log(logger, JK_LOG_ERROR, - "Vector write of chunk encoded response failed with %d (0x%08x)", - GetLastError(), GetLastError()); - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - } else { - /* Write chunk header */ - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, - "Using chunked encoding - writing chunk header for %d byte chunk", l); - - if (!isapi_write_client(p, chunk_header, (unsigned int)strlen(chunk_header))) { - jk_log(logger, JK_LOG_ERROR, "WriteClient for chunk header failed"); - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - - /* Write chunk body (or simple body block) */ - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Writing %s of size %d", - (p->chunk_content ? "chunk body" : "simple response"), l); - } - if (!isapi_write_client(p, buf, l)) { - jk_log(logger, JK_LOG_ERROR, "WriteClient for response body chunk failed"); - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - /* Write chunk trailer */ - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Using chunked encoding - writing chunk trailer"); - } - - if (!isapi_write_client(p, CRLF, CRLF_LEN)) { - jk_log(logger, JK_LOG_ERROR, "WriteClient for chunk trailer 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; -} - -/** - * In the case of a Transfer-Encoding: chunked response, this will write the terminator chunk. - */ -static int JK_METHOD iis_done(jk_ws_service_t *s) -{ - JK_TRACE_ENTER(logger); - - if (s && s->ws_private) { - isapi_private_data_t *p = s->ws_private; - - if (p->chunk_content == JK_FALSE) { - JK_TRACE_EXIT(logger); - return JK_TRUE; - } - - /* Write last chunk + terminator */ - if (iis_info.major >= 6) { - HSE_RESPONSE_VECTOR response_vector; - HSE_VECTOR_ELEMENT response_elements[1]; - - response_elements[0].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER; - response_elements[0].pvContext = CHUNKED_ENCODING_TRAILER; - response_elements[0].cbOffset = 0; - response_elements[0].cbSize = CHUNKED_ENCODING_TRAILER_LEN; - - /* HSE_IO_FINAL_SEND lets IIS process the response to the client before we return */ - response_vector.dwFlags = HSE_IO_SYNC | HSE_IO_FINAL_SEND; - response_vector.pszStatus = NULL; - response_vector.pszHeaders = NULL; - response_vector.nElementCount = 1; - response_vector.lpElementArray = response_elements; - - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, - "Using vector write to terminate chunk encoded response."); - - if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, - HSE_REQ_VECTOR_SEND, - &response_vector, - (LPDWORD)NULL, - (LPDWORD)NULL)) { - jk_log(logger, JK_LOG_ERROR, - "Vector termination of chunk encoded response failed with %d (0x%08x)", - GetLastError(), GetLastError()); - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - } - else { - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Terminating chunk encoded response"); - - if (!isapi_write_client(p, CHUNKED_ENCODING_TRAILER, CHUNKED_ENCODING_TRAILER_LEN)) { - jk_log(logger, JK_LOG_ERROR, - "WriteClient for chunked response terminator failed with %d (0x%08x)", - GetLastError(), 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; -} - -BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) -{ - int rc; - 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; - } - JK_ENTER_CS(&(init_cs), rc); - if (!is_inited) { - rv = initialize_extension(); - } - JK_LEAVE_CS(&(init_cs), rc); - pVer->dwFlags = SF_NOTIFY_ORDER_HIGH | - SF_NOTIFY_SECURE_PORT | - SF_NOTIFY_NONSECURE_PORT | - SF_NOTIFY_LOG | - iis_info.filter_notify_event; - - 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) -{ - int rc; - - /* Initialise jk */ - if (is_inited && !is_mapread) { - char serverName[MAX_SERVERNAME] = ""; - char instanceId[MAX_INSTANCEID] = ""; - DWORD dwLen = MAX_SERVERNAME - MAX_INSTANCEID - 1; - - if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) { - if (dwLen > 0) { - serverName[dwLen - 1] = '\0'; - dwLen = MAX_INSTANCEID; - if (pfc->GetServerVariable(pfc, INSTANCE_ID, instanceId, &dwLen)) { - if (dwLen > 0) { - instanceId[dwLen - 1] = '\0'; - StringCbCat(serverName, MAX_SERVERNAME, "_"); - StringCbCat(serverName, MAX_SERVERNAME, instanceId); - } - } - } - JK_ENTER_CS(&(init_cs), rc); - if (!is_mapread && init_jk(serverName)) - is_mapread = JK_TRUE; - JK_LEAVE_CS(&(init_cs), rc); - } - /* If we can't read the map we become dormant */ - if (!is_mapread) - is_inited = JK_FALSE; - } - if (!is_inited) { - /* In case the initialization failed - * return error. This will make entire IIS - * unusable like with Apache servers - */ - SetLastError(ERROR_INVALID_FUNCTION); - return SF_STATUS_REQ_ERROR; - } - if (iis_info.filter_notify_event == 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] = ""; - char swindex[MAX_INSTANCEID] = ""; - 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 (iis_info.filter_notify_event == 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, WORKER_HEADER_INDEX, NULL); - SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL); - - // Suppress logging of original uri/query when we don't map a URL - if (pfc->pFilterContext) { - isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext; - ld->request_matched = JK_FALSE; - } - - 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; - rule_extension_t *extensions; - int worker_index = -1; - 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); - 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); - 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); - worker = map_uri_to_worker_ext(uw_map, uri, snuri, - &extensions, &worker_index, logger); - } - else { - worker = map_uri_to_worker_ext(uw_map, uri, NULL, - &extensions, &worker_index, 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] 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 tries to hack into the site!!!", - uri); - - write_error_response(pfc, 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, - "forwarding 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); - 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 if (uri_select_option == URI_SELECT_OPT_PROXY) { - if (!jk_canonenc(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); - 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, - "rewritten URI [%s]->[%s]", - duri, forwardURI); - } - else if (rregex_rewrite(forwardURI)) { - jk_log(logger, JK_LOG_DEBUG, - "rewritten URI [%s]->[%s]", - duri, forwardURI); - } - } - else { - if (!simple_rewrite(forwardURI)) - rregex_rewrite(forwardURI); - } - - itoa(worker_index, swindex, 10); - if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) || - ((strlen(squery) > 0) - ? !AddHeader(pfc, QUERY_HEADER_NAME, squery) : FALSE) || - !AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) || - !AddHeader(pfc, WORKER_HEADER_INDEX, swindex) || - !SetHeader(pfc, "url", extension_uri)) { - jk_log(logger, JK_LOG_ERROR, - "error while adding request headers"); - SetLastError(ERROR_INVALID_PARAMETER); - 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); - ld->request_matched = JK_TRUE; - 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); - ld->request_matched = JK_TRUE; - } - } - 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 (dwNotificationType == SF_NOTIFY_LOG) { - if (pfc->pFilterContext) { - isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext; - if (ld->request_matched) { - 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) -{ - int rc; - BOOL rv = TRUE; - - pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); - - StringCbCopy(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, (VERSION_STRING)); - - - JK_ENTER_CS(&(init_cs), rc); - if (!is_inited) { - rv = initialize_extension(); - } - JK_LEAVE_CS(&(init_cs), rc); - - return rv; -} - -DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb) -{ - int rv; - 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] = ""; - char instanceId[MAX_INSTANCEID] = ""; - - DWORD dwLen = MAX_SERVERNAME - MAX_INSTANCEID - 1; - if (lpEcb->GetServerVariable(lpEcb->ConnID, - SERVER_NAME, serverName, &dwLen)) { - if (dwLen > 0) { - serverName[dwLen - 1] = '\0'; - dwLen = MAX_INSTANCEID; - if (lpEcb->GetServerVariable(lpEcb->ConnID, - INSTANCE_ID, instanceId, &dwLen)) { - if (dwLen > 0) { - instanceId[dwLen - 1] = '\0'; - StringCbCat(serverName, MAX_SERVERNAME, "_"); - StringCbCat(serverName, MAX_SERVERNAME, instanceId); - } - } - } - JK_ENTER_CS(&(init_cs), rv); - if (!is_mapread && init_jk(serverName)) - is_mapread = JK_TRUE; - JK_LEAVE_CS(&(init_cs), rv); - } - 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; - - if (!watchdog_interval) - wc_maintain(logger); - jk_init_ws_service(&s); - jk_open_pool(&private_data.p, buf, sizeof(buf)); - - private_data.bytes_read_so_far = 0; - private_data.lpEcb = lpEcb; - private_data.chunk_content = JK_FALSE; - - 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; - if (worker->get_endpoint(worker, &e, logger)) { - int is_error = JK_HTTP_SERVER_ERROR; - int result; - if ((result = e->service(e, &s, logger, &is_error)) > 0) { - if (s.extension.use_server_error_pages && - s.http_response_status >= s.extension.use_server_error_pages) { - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Forwarding status=%d" - " for worker=%s", - s.http_response_status, worker_name); - lpEcb->dwHttpStatusCode = s.http_response_status; - write_error_message(lpEcb, s.http_response_status); - } - else { - rc = HSE_STATUS_SUCCESS; - lpEcb->dwHttpStatusCode = s.http_response_status; - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, - "service() returned OK"); - } - } - else { - if ((result == JK_CLIENT_ERROR) && (is_error == JK_HTTP_OK)) { - jk_log(logger, JK_LOG_INFO, - "service() failed because client aborted connection"); - } - 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) -{ - int rc; - - UNREFERENCED_PARAMETER(dwFlags); - - JK_ENTER_CS(&(init_cs), rc); - if (is_inited) { - jk_log(logger, JK_LOG_INFO, "%s stopping", (FULL_VERSION_STRING)); - is_inited = JK_FALSE; - watchdog_interval = 0; - if (watchdog_handle) { - WaitForSingleObject(watchdog_handle, INFINITE); - CloseHandle(watchdog_handle); - watchdog_handle = NULL; - } - 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); - jk_shm_close(); - JK_ENTER_CS(&(log_cs), rc); - if (logger) { - jk_close_file_logger(&logger); - } - JK_LEAVE_CS(&(log_cs), rc); - } - JK_LEAVE_CS(&(init_cs), rc); - - 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 -{ - int rc; - BOOL fReturn = TRUE; - char fname[MAX_PATH]; - - UNREFERENCED_PARAMETER(lpReserved); - - switch (ulReason) { - case DLL_PROCESS_ATTACH: - if (GetModuleFileName(hInst, fname, sizeof(fname))) { - char *p = strrchr(fname, '.'); - if (p) { - *p = '\0'; - StringCbCopy(ini_file_name, MAX_PATH, fname); - StringCbCat(ini_file_name, MAX_PATH, ".properties"); - } - else { - /* Cannot obtain file name ? */ - fReturn = JK_FALSE; - } - if ((p = strrchr(fname, '\\'))) { - *(p++) = '\0'; - StringCbCopy(dll_file_path, MAX_PATH, fname); - jk_map_alloc(&jk_environment_map); - jk_map_add(jk_environment_map, "JKISAPI_PATH", dll_file_path); - jk_map_add(jk_environment_map, "JKISAPI_NAME", p); - } - else { - /* Cannot obtain file name ? */ - fReturn = JK_FALSE; - } - } - 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(WORKER_HEADER_INDEX, MAX_PATH, HEADER_TEMPLATE, WORKER_HEADER_INDEX_BASE, hInst); - StringCbPrintf(TOMCAT_TRANSLATE_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, TOMCAT_TRANSLATE_HEADER_NAME_BASE, hInst); - - /* Construct the HTTP_ headers that will be seen in ExtensionProc */ - 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); - StringCbPrintf(HTTP_WORKER_HEADER_INDEX, MAX_PATH, HTTP_HEADER_TEMPLATE, WORKER_HEADER_INDEX_BASE, hInst); - - JK_INIT_CS(&init_cs, rc); - JK_INIT_CS(&log_cs, rc); - - break; - case DLL_PROCESS_DETACH: - __try { - TerminateFilter(HSE_TERM_MUST_UNLOAD); - } - __except(1) { - } - JK_DELETE_CS(&init_cs, rc); - JK_DELETE_CS(&log_cs, rc); - break; - - default: - break; - } - - return fReturn; -} - -static DWORD WINAPI watchdog_thread(void *param) -{ - int i; - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, - "Watchdog thread initialized with %u second interval", - watchdog_interval); - } - while (watchdog_interval) { - for (i = 0; i < (watchdog_interval * 10); i++) { - if (!watchdog_interval) - break; - Sleep(100); - } - if (!watchdog_interval) - break; - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, - "Watchdog thread running"); - } - if (worker_mount_file[0]) { - jk_shm_lock(); - uri_worker_map_update(uw_map, 0, logger); - jk_shm_unlock(); - } - wc_maintain(logger); - } - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, - "Watchdog thread finished"); - } - return 0; -} - -/* - * Reinitializes the logger, formatting the log file name if rotation is enabled, - * and calculating the next rotation time if applicable. - */ -static int init_logger(int rotate, jk_logger_t **l) -{ - int rc = JK_TRUE; - int log_open = rotate; /* log is assumed open if a rotate is requested */ - char *log_file_name; - char log_file_name_buf[MAX_PATH*2]; - - /* If log rotation is enabled, format the log filename */ - if ((log_rotationtime > 0) || (log_filesize > 0)) { - time_t t; - t = time(NULL); - - if (log_rotationtime > 0) { - /* Align time to rotationtime intervals */ - t = (t / log_rotationtime) * log_rotationtime; - - /* Calculate rotate time */ - log_next_rotate_time = t + log_rotationtime; - } - - log_file_name = log_file_name_buf; - if (strchr(log_file, '%')) { - struct tm *tm_now; - - /* If there are %s in the log file name, treat it as a sprintf format */ - tm_now = localtime(&t); - strftime(log_file_name, sizeof(log_file_name_buf), log_file, tm_now); - } else { - /* Otherwise append the number of seconds to the base name */ - StringCbPrintf(log_file_name, sizeof(log_file_name_buf), "%s.%d", log_file, (long)t); - } - } else { - log_file_name = log_file; - } - - /* Close the current log file if required, and the effective log file name has changed */ - if (log_open && strncmp(log_file_name, log_file_effective, strlen(log_file_name)) != 0) { - FILE* lf = ((jk_file_logger_t* )logger->logger_private)->logfile; - fprintf(lf, "Log rotated to %s\r\n", log_file_name); - fflush(lf); - rc = jk_close_file_logger(&logger); - log_open = JK_FALSE; - } - - if (!log_open) { - if (jk_open_file_logger(&logger, log_file_name, log_level)) { - logger->log = iis_log_to_file; - - /* Remember the current log file name for the next potential rotate */ - StringCbCopy(log_file_effective, sizeof(log_file_effective), log_file_name); - rc = JK_TRUE; - } else { - logger = NULL; - rc = JK_FALSE; - } - } - - /* Update logger being used for this log call so it occurs on new file */ - (*l) = logger; - return rc; -} - -/* - * Checks whether the log needs to be rotated. Must be called while holding the log lock. - * The behaviour here is based on the Apache rotatelogs program. - * http://httpd.apache.org/docs/2.0/programs/rotatelogs.html - */ -static int JK_METHOD rotate_log_file(jk_logger_t **l) -{ - int rc = JK_TRUE; - int rotate = JK_FALSE; - - if (log_rotationtime > 0) { - time_t t = time(NULL); - - if (t >= log_next_rotate_time) { - rotate = JK_TRUE; - } - } else if (log_filesize > 0) { - LARGE_INTEGER filesize; - HANDLE h = (HANDLE)_get_osfhandle(fileno(((jk_file_logger_t *)(*l)->logger_private)->logfile)); - GetFileSizeEx(h, &filesize); - - if ((ULONGLONG)filesize.QuadPart >= log_filesize) { - rotate = JK_TRUE; - } - } - if (rotate) { - rc = init_logger(JK_TRUE, l); - } - return rc; -} - -/* - * Log messages to the log file, rotating the log when required. - */ -static int JK_METHOD iis_log_to_file(jk_logger_t *l, int level, - int used, char *what) -{ - int rc = JK_FALSE; - - if (l && - (l->level <= level || level == JK_LOG_REQUEST_LEVEL) && - l->logger_private && what && used > 0) { - jk_file_logger_t *p = l->logger_private; - rc = JK_TRUE; - - if (p->logfile) { - what[used++] = '\r'; - what[used++] = '\n'; - what[used] = '\0'; - - /* Perform logging within critical section to protect rotation */ - JK_ENTER_CS(&(log_cs), rc); - if (rc && rotate_log_file(&l)) { - /* The rotation process will reallocate the jk_logger_t structure, so refetch */ - FILE *rotated = ((jk_file_logger_t *)l->logger_private)->logfile; - fputs(what, rotated); - fflush(rotated); - JK_LEAVE_CS(&(log_cs), rc); - } - } - } - return rc; -} - -static int init_jk(char *serverName) -{ - char shm_name[MAX_PATH]; - int rc = JK_FALSE; - - init_logger(JK_FALSE, &logger); - /* TODO: Use System logging to notify the user that - * we cannot open the configured log file. - */ - - StringCbCopy(shm_name, MAX_PATH, SHM_DEF_NAME); - - jk_log(logger, JK_LOG_INFO, "Starting %s", (FULL_VERSION_STRING)); - - 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] = '_'; - } - } - - 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)) { - jk_log(logger, JK_LOG_DEBUG, "Detected IIS version %d.%d", iis_info.major, iis_info.minor); - 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 log rotation time %d seconds.", log_rotationtime); - jk_log(logger, JK_LOG_DEBUG, "Using log file size %d bytes.", log_filesize); - - 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); - jk_log(logger, JK_LOG_DEBUG, "Using%s chunked encoding.", (chunked_encoding_enabled ? "" : " no")); - - jk_log(logger, JK_LOG_DEBUG, "Using notification event %s (0x%08x)", - (iis_info.filter_notify_event == SF_NOTIFY_AUTH_COMPLETE) ? - "SF_NOTIFY_AUTH_COMPLETE" : - ((iis_info.filter_notify_event == SF_NOTIFY_PREPROC_HEADERS) ? - "SF_NOTIFY_PREPROC_HEADERS" : "UNKNOWN"), - iis_info.filter_notify_event); - - if (error_page) { - jk_log(logger, JK_LOG_DEBUG, "Using error page '%s'.", error_page); - } - jk_log(logger, JK_LOG_DEBUG, "Using uri header %s.", URI_HEADER_NAME); - jk_log(logger, JK_LOG_DEBUG, "Using query header %s.", QUERY_HEADER_NAME); - jk_log(logger, JK_LOG_DEBUG, "Using worker header %s.", WORKER_HEADER_NAME); - jk_log(logger, JK_LOG_DEBUG, "Using worker index %s.", WORKER_HEADER_INDEX); - jk_log(logger, JK_LOG_DEBUG, "Using translate header %s.", TOMCAT_TRANSLATE_HEADER_NAME); - jk_log(logger, JK_LOG_DEBUG, "Using a default of %d connections per pool.", - DEFAULT_WORKER_THREADS); - } - - if ((log_rotationtime > 0) && (log_filesize > 0)) { - jk_log(logger, JK_LOG_WARNING, - "%s is defined in configuration, but will be ignored because %s is set. ", - LOG_FILESIZE_TAG, LOG_ROTATION_TIME_TAG); - } - - if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) { - if (jk_map_read_properties(rewrite_map, NULL, rewrite_rule_file, - NULL, JK_MAP_HANDLE_RAW, 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; - if (reject_unsafe) - uw_map->reject_unsafe = 1; - else - uw_map->reject_unsafe = 0; - uw_map->reload = worker_mount_reload; - if (worker_mount_file[0]) { - uw_map->fname = worker_mount_file; - 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, NULL, worker_file, NULL, - JK_MAP_HANDLE_DUPLICATES, logger)) { - int rv; - - /* 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"); - } - /* - * Create named shared memory for each server - */ - if (shm_config_size == 0) - shm_config_size = jk_shm_calculate_size(workers_map, logger); - else { - jk_log(logger, JK_LOG_WARNING, - "The optimal shared memory size can now be determined automatically."); - jk_log(logger, JK_LOG_WARNING, - "You can remove the shm_size attribute if you want to use the optimal size."); - } - if ((rv = jk_shm_open(shm_name, shm_config_size, logger)) != 0) { - /* TODO: Do not try to open the worker if we cannot create - * the shared memory segment. - */ - jk_log(logger, JK_LOG_ERROR, - "Initializing shm:%s errno=%d. Load balancing workers will not function properly.", - jk_shm_name(), rv); - } - worker_env.uri_to_worker = uw_map; - worker_env.server_name = serverName; - worker_env.pool = NULL; - - if (wc_open(workers_map, &worker_env, logger)) { - rc = JK_TRUE; - } - uri_worker_map_ext(uw_map, logger); - uri_worker_map_switch(uw_map, logger); - } - 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) { - if (watchdog_interval) { - DWORD wi; - watchdog_handle = CreateThread(NULL, 0, watchdog_thread, - NULL, 0, &wi); - if (!watchdog_handle) { - jk_log(logger, JK_LOG_EMERG, "Error %d (0x%08x) creating Watchdog thread", - GetLastError(), GetLastError()); - watchdog_interval = 0; - } - } - jk_log(logger, JK_LOG_INFO, "%s initialized", (FULL_VERSION_STRING)); - } - return rc; -} - -static BOOL initialize_extension(void) -{ - - if (read_registry_init_data()) { - if (get_iis_info(&iis_info) != JK_TRUE) { - jk_log(logger, JK_LOG_ERROR, "Could not retrieve IIS version from registry"); - } - 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; - } - - if (0 == strcasecmp(uri_select, URI_SELECT_PROXY_VERB)) { - return URI_SELECT_OPT_PROXY; - } - - 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, jk_environment_map, ini_file_name, NULL, - JK_MAP_HANDLE_DUPLICATES, 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); - } - if (get_config_parameter(src, LOG_ROTATION_TIME_TAG, tmpbuf, sizeof(tmpbuf))) { - log_rotationtime = atol(tmpbuf); - if (log_rotationtime < 0) { - log_rotationtime = 0; - } - } - if (get_config_parameter(src, LOG_FILESIZE_TAG, tmpbuf, sizeof(tmpbuf))) { - size_t tl = strlen(tmpbuf); - if (tl > 0) { - /* rotatelogs has an 'M' suffix on filesize, which we optionally support for consistency */ - if (tmpbuf[tl - 1] == 'M') { - tmpbuf[tl - 1] = '\0'; - } - log_filesize = atol(tmpbuf); - if (log_filesize < 0) { - log_filesize = 0; - } - /* Convert to MB as per Apache rotatelogs */ - log_filesize *= (1000 * 1000); - } - } - - 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 = (size_t) get_config_int(src, SHM_SIZE_TAG, 0); - 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); -#ifndef AUTOMATIC_AUTH_NOTIFICATION - use_auth_notification_flags = get_config_int(src, AUTH_COMPLETE_TAG, 1); -#endif - reject_unsafe = get_config_bool(src, REJECT_UNSAFE_TAG, JK_FALSE); - watchdog_interval = get_config_int(src, WATCHDOG_INTERVAL_TAG, 0); - if (watchdog_interval < 0) - watchdog_interval = 0; - chunked_encoding_enabled = get_config_bool(src, ENABLE_CHUNKED_ENCODING_TAG, JK_FALSE); - if (get_config_parameter(src, ERROR_PAGE_TAG, error_page_buf, sizeof(error_page_buf))) { - error_page = error_page_buf; - } - - 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 = (int)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 = NULL; /* should be enough for all */ - int worker_index = -1; - rule_extension_t *e; - char temp_buf[64]; - DWORD huge_buf_sz; - BOOL unknown_content_length = FALSE; - - JK_TRACE_ENTER(logger); - - s->start_response = start_response; - s->read = iis_read; - s->write = iis_write; - s->done = iis_done; - - if (!(huge_buf = jk_pool_alloc(&private_data->p, MAX_PACKET_SIZE))) { - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - huge_buf_sz = MAX_PACKET_SIZE; - 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); - GET_SERVER_VARIABLE_VALUE_INT(HTTP_WORKER_HEADER_INDEX, worker_index, -1); - - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_WORKER_HEADER_NAME, (*worker_name)); - jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %d", HTTP_WORKER_HEADER_INDEX, worker_index); - jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_URI_HEADER_NAME, s->req_uri); - jk_log(logger, JK_LOG_DEBUG, "Reading extension header %s: %s", HTTP_QUERY_HEADER_NAME, s->query_string); - } - - if (s->req_uri == NULL) { - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "No URI header value provided. Defaulting to old behaviour" ); - 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) { - JK_TRACE_EXIT(logger); - 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("REMOTE_PORT", s->remote_port); - 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; - /* Check for Transfer Encoding */ - if (get_server_value(private_data->lpEcb, - "HTTP_TRANSFER_ENCODING", - temp_buf, - (DWORD)sizeof(temp_buf))) { - if (strcasecmp(temp_buf, TRANSFER_ENCODING_CHUNKED_VALUE) == 0) { - s->is_chunked = JK_TRUE; - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Request is Transfer-Encoding: chunked"); - } - } - else { - /* XXX: What to do with non chunked T-E ? - */ - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Unsupported Transfer-Encoding: %s", - temp_buf); - } - } - if (private_data->lpEcb->cbTotalBytes == 0xFFFFFFFF) { - /* We have size larger then 4Gb or Transfer-Encoding: chunked - * ReadClient should be called until no more data is returned - */ - unknown_content_length = TRUE; - } - else { - /* Use the IIS provided content length */ - s->content_length = (jk_uint64_t)private_data->lpEcb->cbTotalBytes; - } - e = get_uri_to_worker_ext(uw_map, worker_index); - if (e) { - if (JK_IS_DEBUG_LEVEL(logger)) - jk_log(logger, JK_LOG_DEBUG, "Applying service extensions" ); - s->extension.reply_timeout = e->reply_timeout; - s->extension.use_server_error_pages = e->use_server_error_pages; - if (e->activation) { - s->extension.activation = jk_pool_alloc(s->pool, e->activation_size * sizeof(int)); - memcpy(s->extension.activation, e->activation, e->activation_size * sizeof(int)); - } - if (e->fail_on_status_size > 0) { - s->extension.fail_on_status_size = e->fail_on_status_size; - s->extension.fail_on_status = jk_pool_alloc(s->pool, e->fail_on_status_size * sizeof(int)); - memcpy(s->extension.fail_on_status, e->fail_on_status, e->fail_on_status_size * sizeof(int)); - } - } - - 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++; - } - } - /* XXX: To make the isapi plugin more consistent with the other web servers */ - /* we should also set s->ssl_cipher, s->ssl_session, and s->ssl_key_size. */ - 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 = MAX_PACKET_SIZE; - 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 = MAX_PACKET_SIZE; - if (get_server_value(private_data->lpEcb, -#ifndef USE_CGI_HEADERS - "ALL_RAW", huge_buf, huge_buf_sz)) { -#else - "ALL_HTTP", huge_buf, huge_buf_sz)) { -#endif - unsigned int cnt = 0; - char *tmp; - - for (tmp = huge_buf; *tmp; tmp++) { - if (*tmp == '\n') { - cnt++; - } - } - - if (cnt) { - char *headers_buf = huge_buf; - unsigned int i; - BOOL need_content_length_header = FALSE; - - if (s->content_length == 0 && unknown_content_length == FALSE) { - /* Add content-length=0 only if really zero - */ - need_content_length_header = TRUE; - } - - /* 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) { - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - - for (i = 0, tmp = headers_buf; *tmp && i < cnt;) { - int real_header = JK_TRUE; - -#ifdef USE_CGI_HEADERS - /* Skip the HTTP_ prefix to the beginning of the header name */ - tmp += HTTP_HEADER_PREFIX_LEN; -#endif - - if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME)) - || !strnicmp(tmp, WORKER_HEADER_NAME, strlen(WORKER_HEADER_NAME)) - || !strnicmp(tmp, WORKER_HEADER_INDEX, strlen(WORKER_HEADER_INDEX)) - || !strnicmp(tmp, QUERY_HEADER_NAME, strlen(QUERY_HEADER_NAME))) { - /* Skip redirector headers */ - cnt--; - real_header = JK_FALSE; - } - else if (!strnicmp(tmp, CONTENT_LENGTH, - sizeof(CONTENT_LENGTH) - 1)) { - need_content_length_header = FALSE; - - /* If the content-length is unknown - * or larger then 4Gb do not send it. - * IIS can also create a synthetic Content-Length header to make - * lpcbTotalBytes and the CONTENT_LENGTH server variable agree - * on small requests where the entire chunk encoded message is - * read into the available buffer. - */ - if (unknown_content_length || s->is_chunked) { - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, - "Disregarding Content-Length in request - content is %s", - s->is_chunked ? "chunked" : "unknown length"); - } - cnt--; - real_header = JK_FALSE; - } - else { - 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) { -#ifdef USE_CGI_HEADERS - if (real_header) { - if ('_' == *tmp) { - *tmp = '-'; - } - else { - *tmp = JK_TOLOWER(*tmp); - } - } -#endif - tmp++; - } - *tmp = '\0'; - tmp++; - - /* Skip all the WS chars after the ':' to the beginning of the header value */ - while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) { - tmp++; - } - - if (real_header) { - s->headers_values[i] = tmp; - } - - while (*tmp && *tmp != '\n' && *tmp != '\r') { - tmp++; - } - *tmp = '\0'; - tmp++; - - /* skip CR LF */ - while (*tmp == '\n' || *tmp == '\r') { - tmp++; - } - - if (real_header) { - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Forwarding request header %s : %s", - s->headers_names[i], s->headers_values[i]); - } - 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) { - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, "Incoming request needs explicit Content-Length: 0 in AJP13"); - } - s->headers_names[cnt] = "Content-Length"; - s->headers_values[cnt] = "0"; - cnt++; - } - s->num_headers = cnt; - } - else { - /* We must have our two headers */ - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - } - else { - JK_TRACE_EXIT(logger); - return JK_FALSE; - } - - /* Dump all connection param so we can trace what's going to - * the remote tomcat - */ - if (JK_IS_DEBUG_LEVEL(logger)) { - jk_log(logger, JK_LOG_DEBUG, - "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d " - "auth=%s user=%s uri=%s", - STRNULL_FOR_NULL(s->protocol), - STRNULL_FOR_NULL(s->method), - STRNULL_FOR_NULL(s->remote_host), - STRNULL_FOR_NULL(s->remote_addr), - STRNULL_FOR_NULL(s->server_name), - s->server_port, - STRNULL_FOR_NULL(s->auth_type), - STRNULL_FOR_NULL(s->remote_user), - STRNULL_FOR_NULL(s->req_uri)); - jk_log(logger, JK_LOG_DEBUG, - "Service request headers=%d attributes=%d " - "chunked=%s content-length=%" JK_UINT64_T_FMT " available=%u", - s->num_headers, - s->num_attributes, - (s->is_chunked == JK_TRUE) ? "yes" : "no", - s->content_length, - private_data->lpEcb->cbTotalBytes); - } - - JK_TRACE_EXIT(logger); - 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); -} - -/** -* Determine version info and the primary notification event -*/ -static int get_iis_info(iis_info_t* iis_info) -{ - HKEY hkey; - long rc; - int rv = JK_FALSE; - - iis_info->major = 0; - iis_info->minor = 0; - iis_info->filter_notify_event = SF_NOTIFY_PREPROC_HEADERS; - - /* Retrieve the IIS version Major/Minor */ - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, - W3SVC_REGISTRY_KEY, (DWORD) 0, KEY_READ, &hkey); - if (ERROR_SUCCESS == rc) { - if (get_registry_config_number(hkey, "MajorVersion", &iis_info->major) == JK_TRUE) { -#ifdef AUTOMATIC_AUTH_NOTIFICATION - if (iis_info->major > 4) -#else - if (use_auth_notification_flags && iis_info->major > 4) -#endif - iis_info->filter_notify_event = SF_NOTIFY_AUTH_COMPLETE; - if (get_registry_config_number(hkey, "MinorVersion", &iis_info->minor) == JK_TRUE) { - -#ifdef AUTOMATIC_AUTH_NOTIFICATION - /* SF_NOTIFY_AUTH_COMPLETE causes redirect failures - * (ERROR_INVALID_PARAMETER) on IIS 5.1 with OPTIONS/PUT - * and is only available from IIS 5+ - */ - if (iis_info->major == 5 && iis_info->minor == 1) { - iis_info->filter_notify_event = SF_NOTIFY_PREPROC_HEADERS; - } -#endif - rv = JK_TRUE; - } - } - } - CloseHandle(hkey); - return rv; -} |