2
* Copyright 1999-2004 The Apache Software Foundation
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
/***************************************************************************
18
* Description: ISAPI plugin for IIS/PWS *
19
* Author: Gal Shachor <shachor@il.ibm.com> *
20
* Author: Larry Isaacs <larryi@apache.org> *
21
* Author: Ignacio J. Ortega <nacho@apache.org> *
22
* Author: Mladen Turk <mturk@apache.org> *
23
* Version: $Revision: 1.49 $ *
24
***************************************************************************/
26
// This define is needed to include wincrypt,h, needed to get client certificates
27
#define _WIN32_WINNT 0x0400
33
#include "jk_global.h"
37
#include "jk_service.h"
38
#include "jk_worker.h"
39
#include "jk_uri_worker_map.h"
42
#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
44
#define DEFAULT_WORKER_NAME ("ajp13")
46
* We use special headers to pass values from the filter to the
47
* extension. These values are:
49
* 1. The real URI before redirection took place
50
* 2. The name of the worker to be used.
51
* 3. The contents of the Translate header, if any
54
#define URI_HEADER_NAME ("TOMCATURI:")
55
#define QUERY_HEADER_NAME ("TOMCATQUERY:")
56
#define WORKER_HEADER_NAME ("TOMCATWORKER:")
57
#define TOMCAT_TRANSLATE_HEADER_NAME ("TOMCATTRANSLATE:")
58
#define CONTENT_LENGTH ("CONTENT_LENGTH:")
60
#define HTTP_URI_HEADER_NAME ("HTTP_TOMCATURI")
61
#define HTTP_QUERY_HEADER_NAME ("HTTP_TOMCATQUERY")
62
#define HTTP_WORKER_HEADER_NAME ("HTTP_TOMCATWORKER")
64
#define REGISTRY_LOCATION ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
65
#define EXTENSION_URI_TAG ("extension_uri")
67
#define URI_SELECT_TAG ("uri_select")
68
#define URI_SELECT_PARSED_VERB ("parsed")
69
#define URI_SELECT_UNPARSED_VERB ("unparsed")
70
#define URI_SELECT_ESCAPED_VERB ("escaped")
72
#define BAD_REQUEST -1
74
#define MAX_SERVERNAME 128
76
#define HTML_ERROR_400 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
77
"<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>" \
78
"<BODY><H1>Bad request!</H1><DL><DD>\n" \
79
"Your browser (or proxy) sent a request that " \
80
"this server could not understand.</DL></DD></BODY></HTML>"
82
#define HTML_ERROR_404 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
83
"<HTML><HEAD><TITLE>Object not found!</TITLE></HEAD>" \
84
"<BODY><H1>The requested URL was not found on this server" \
85
"</H1><DL><DD>\nIf you entered the URL manually please check your" \
86
"spelling and try again.</DL></DD></BODY></HTML>"
89
#define JK_TOLOWER(x) ((char)tolower((BYTE)(x)))
91
#define GET_SERVER_VARIABLE_VALUE(name, place) \
94
huge_buf_sz = sizeof(huge_buf); \
95
if (get_server_value(private_data->lpEcb, \
99
(place) = jk_pool_strdup(&private_data->p, \
103
#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def) \
105
huge_buf_sz = sizeof(huge_buf); \
106
if (get_server_value(private_data->lpEcb, \
110
(place) = atoi(huge_buf); \
111
if (0 == (place)) { \
118
static char ini_file_name[MAX_PATH];
119
static int using_ini_file = JK_FALSE;
120
static int is_inited = JK_FALSE;
121
static int is_mapread = JK_FALSE;
122
static int iis5 = -1;
124
static jk_uri_worker_map_t *uw_map = NULL;
125
static jk_logger_t *logger = NULL;
126
static char *SERVER_NAME = "SERVER_NAME";
127
static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
128
static char *CONTENT_TYPE = "Content-Type:text/html\r\n\r\n";
130
static char extension_uri[INTERNET_MAX_URL_LENGTH] =
131
"/jakarta/isapi_redirect.dll";
132
static char log_file[MAX_PATH * 2];
133
static int log_level = JK_LOG_EMERG_LEVEL;
134
static char worker_file[MAX_PATH * 2];
135
static char worker_mount_file[MAX_PATH * 2] = {0};
137
#define URI_SELECT_OPT_PARSED 0
138
#define URI_SELECT_OPT_UNPARSED 1
139
#define URI_SELECT_OPT_ESCAPED 2
141
static int uri_select_option = URI_SELECT_OPT_PARSED;
143
static jk_worker_env_t worker_env;
145
typedef struct isapi_private_data_t isapi_private_data_t;
146
struct isapi_private_data_t
151
unsigned int bytes_read_so_far;
152
LPEXTENSION_CONTROL_BLOCK lpEcb;
155
typedef struct isapi_log_data_t isapi_log_data_t;
156
struct isapi_log_data_t {
157
char uri[INTERNET_MAX_URL_LENGTH];
158
char query[INTERNET_MAX_URL_LENGTH];
161
static int JK_METHOD start_response(jk_ws_service_t *s,
164
const char *const *header_names,
165
const char *const *header_values,
166
unsigned int num_of_headers);
168
static int JK_METHOD read(jk_ws_service_t *s,
169
void *b, unsigned int l, unsigned int *a);
171
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l);
173
static int init_ws_service(isapi_private_data_t * private_data,
174
jk_ws_service_t *s, char **worker_name);
176
static int init_jk(char *serverName);
178
static int initialize_extension(void);
180
static int read_registry_init_data(void);
182
static int get_registry_config_parameter(HKEY hkey,
183
const char *tag, char *b, DWORD sz);
186
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
188
char *buf, DWORD bufsz);
190
static int base64_encode_cert_len(int len);
192
static int base64_encode_cert(char *encoded,
193
const char *string, int len);
196
static char x2c(const char *what)
201
((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
204
(what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
208
static int unescape_url(char *url)
210
register int x, y, badesc, badpath;
214
for (x = 0, y = 0; url[y]; ++x, ++y) {
218
if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
223
url[x] = x2c(&url[y + 1]);
225
if (url[x] == '/' || url[x] == '\0')
239
static void getparents(char *name)
243
/* Four paseses, as per RFC 1808 */
244
/* a) remove ./ path segments */
246
for (l = 0, w = 0; name[l] != '\0';) {
247
if (name[l] == '.' && name[l + 1] == '/'
248
&& (l == 0 || name[l - 1] == '/'))
251
name[w++] = name[l++];
254
/* b) remove trailing . path, segment */
255
if (w == 1 && name[0] == '.')
257
else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
261
/* c) remove all xx/../ segments. (including leading ../ and /../) */
264
while (name[l] != '\0') {
265
if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
266
(l == 0 || name[l - 1] == '/')) {
267
register int m = l + 3, n;
271
while (l >= 0 && name[l] != '/')
278
while ((name[n] = name[m]) != '\0') {
287
/* d) remove trailing xx/.. segment. */
288
if (l == 2 && name[0] == '.' && name[1] == '.')
290
else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
291
&& name[l - 3] == '/') {
294
while (l >= 0 && name[l] != '/')
304
/* Apache code to escape a URL */
306
#define T_OS_ESCAPE_PATH (4)
308
static const BYTE test_char_table[256] = {
309
0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14,
310
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
311
14, 0, 7, 6, 1, 6, 1, 1, 9, 9, 1, 0, 8, 0, 0, 10,
312
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15, 15, 8, 15, 15,
313
8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
314
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 7, 0,
315
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
316
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 7, 15, 1, 14,
317
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
318
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
319
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
320
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
321
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
322
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
323
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
324
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
327
#define TEST_CHAR(c, f) (test_char_table[(unsigned int)(c)] & (f))
329
static const char c2x_table[] = "0123456789abcdef";
331
static BYTE *c2x(unsigned int what, BYTE *where)
334
*where++ = c2x_table[what >> 4];
335
*where++ = c2x_table[what & 0xf];
339
static char *status_reason(int status)
341
static struct reasons {
346
{ 101, "Switching Protocols" },
350
{ 203, "Non-Authoritative Information" },
351
{ 204, "No Content" },
352
{ 205, "Reset Content" },
353
{ 206, "Partial Content" },
354
{ 300, "Multiple Choices" },
355
{ 301, "Moved Permanently" },
356
{ 302, "Moved Temporarily" },
357
{ 303, "See Other" },
358
{ 304, "Not Modified" },
359
{ 305, "Use Proxy" },
360
{ 400, "Bad Request" },
361
{ 401, "Unauthorized" },
362
{ 402, "Payment Required" },
363
{ 403, "Forbidden" },
364
{ 404, "Not Found" },
365
{ 405, "Method Not Allowed" },
366
{ 406, "Not Acceptable" },
367
{ 407, "Proxy Authentication Required" },
368
{ 408, "Request Timeout" },
371
{ 411, "Length Required" },
372
{ 412, "Precondition Failed" },
373
{ 413, "Request Entity Too Large" },
374
{ 414, "Request-URI Too Long" },
375
{ 415, "Unsupported Media Type" },
376
{ 500, "Internal Server Error" },
377
{ 501, "Not Implemented" },
378
{ 502, "Bad Gateway" },
379
{ 503, "Service Unavailable" },
380
{ 504, "Gateway Timeout" },
381
{ 505, "HTTP Version Not Supported" },
386
while (r->status <= status)
387
if (r->status == status)
394
static int escape_url(const char *path, char *dest, int destsize)
396
const BYTE *s = (const BYTE *)path;
397
BYTE *d = (BYTE *)dest;
398
BYTE *e = d + destsize - 1;
399
BYTE *ee = d + destsize - 3;
402
if (TEST_CHAR(*s, T_OS_ESCAPE_PATH)) {
419
* Find the first occurrence of find in s.
421
static char *stristr(const char *s, const char *find)
426
if ((c = tolower((unsigned char)(*find++))) != 0) {
430
if ((sc = tolower((unsigned char)(*s++))) == 0)
433
} while (strnicmp(s, find, len) != 0);
439
static int uri_is_web_inf(const char *uri)
441
if (stristr(uri, "web-inf")) {
444
if (stristr(uri, "meta-inf")) {
451
static void write_error_response(PHTTP_FILTER_CONTEXT pfc, char *status,
454
DWORD len = (DWORD)strlen(msg);
457
pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0);
458
pfc->ServerSupportFunction(pfc,
459
SF_REQ_SEND_RESPONSE_HEADER,
461
pfc->WriteClient(pfc, msg, &len, 0);
465
static int JK_METHOD start_response(jk_ws_service_t *s,
468
const char *const *header_names,
469
const char *const *header_values,
470
unsigned int num_of_headers)
472
static char crlf[3] = { (char)13, (char)10, '\0' };
474
JK_TRACE_ENTER(logger);
475
if (status < 100 || status > 1000) {
476
jk_log(logger, JK_LOG_ERROR,
479
JK_TRACE_EXIT(logger);
483
if (s && s->ws_private) {
484
isapi_private_data_t *p = s->ws_private;
485
if (!p->request_started) {
486
size_t len_of_status;
490
p->request_started = JK_TRUE;
493
* Create the status line
496
reason = status_reason(status);
498
status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char));
499
sprintf(status_str, "%d %s", status, reason);
500
len_of_status = strlen(status_str);
503
* Create response headers string
505
if (num_of_headers) {
506
size_t i, len_of_headers;
507
for (i = 0, len_of_headers = 0; i < num_of_headers; i++) {
508
len_of_headers += strlen(header_names[i]);
509
len_of_headers += strlen(header_values[i]);
510
len_of_headers += 4; /* extra for colon, space and crlf */
513
len_of_headers += 3; /* crlf and terminating null char */
514
headers_str = (char *)_alloca(len_of_headers * sizeof(char));
515
headers_str[0] = '\0';
517
for (i = 0; i < num_of_headers; i++) {
518
strcat(headers_str, header_names[i]);
519
strcat(headers_str, ": ");
520
strcat(headers_str, header_values[i]);
521
strcat(headers_str, crlf);
523
strcat(headers_str, crlf);
529
if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
530
HSE_REQ_SEND_RESPONSE_HEADER,
532
(LPDWORD) &len_of_status,
533
(LPDWORD) headers_str)) {
534
jk_log(logger, JK_LOG_ERROR,
535
"HSE_REQ_SEND_RESPONSE_HEADER failed");
536
JK_TRACE_EXIT(logger);
540
JK_TRACE_EXIT(logger);
545
JK_LOG_NULL_PARAMS(logger);
546
JK_TRACE_EXIT(logger);
550
static int JK_METHOD read(jk_ws_service_t *s,
551
void *b, unsigned int l, unsigned int *a)
553
JK_TRACE_ENTER(logger);
555
if (s && s->ws_private && b && a) {
556
isapi_private_data_t *p = s->ws_private;
561
DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
563
if (already_read >= l) {
564
memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, l);
565
p->bytes_read_so_far += l;
570
* Try to copy what we already have
572
if (already_read > 0) {
573
memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far,
577
p->bytes_read_so_far = p->lpEcb->cbAvailable;
583
* Now try to read from the client ...
585
if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, (LPDWORD)&l)) {
589
jk_log(logger, JK_LOG_ERROR,
590
"ReadClient failed with %08x", GetLastError());
591
JK_TRACE_EXIT(logger);
596
JK_TRACE_EXIT(logger);
600
JK_LOG_NULL_PARAMS(logger);
601
JK_TRACE_EXIT(logger);
605
static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l)
607
JK_TRACE_ENTER(logger);
609
if (s && s->ws_private && b) {
610
isapi_private_data_t *p = s->ws_private;
613
unsigned int written = 0;
614
char *buf = (char *)b;
616
if (!p->request_started) {
617
start_response(s, 200, NULL, NULL, NULL, 0);
620
while (written < l) {
621
DWORD try_to_write = l - written;
622
if (!p->lpEcb->WriteClient(p->lpEcb->ConnID,
623
buf + written, &try_to_write, 0)) {
624
jk_log(logger, JK_LOG_ERROR,
625
"WriteClient failed with %08x", GetLastError());
626
JK_TRACE_EXIT(logger);
629
written += try_to_write;
633
JK_TRACE_EXIT(logger);
638
JK_LOG_NULL_PARAMS(logger);
639
JK_TRACE_EXIT(logger);
643
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
645
ULONG http_filter_revision = HTTP_FILTER_REVISION;
647
pVer->dwFilterVersion = pVer->dwServerFilterVersion;
649
if (pVer->dwFilterVersion > http_filter_revision) {
650
pVer->dwFilterVersion = http_filter_revision;
653
pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
654
SF_NOTIFY_SECURE_PORT |
655
SF_NOTIFY_NONSECURE_PORT |
656
SF_NOTIFY_PREPROC_HEADERS |
658
SF_NOTIFY_AUTH_COMPLETE;
660
strcpy(pVer->lpszFilterDesc, VERSION_STRING);
663
return initialize_extension();
669
DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
670
DWORD dwNotificationType, LPVOID pvNotification)
673
if (is_inited && !is_mapread) {
674
char serverName[MAX_SERVERNAME];
675
DWORD dwLen = sizeof(serverName);
677
if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) {
679
serverName[dwLen - 1] = '\0';
680
if (init_jk(serverName))
681
is_mapread = JK_TRUE;
683
/* If we can't read the map we become dormant */
685
is_inited = JK_FALSE;
688
if (is_inited && (iis5 < 0)) {
689
char serverSoftware[256];
690
DWORD dwLen = sizeof(serverSoftware);
693
GetServerVariable(pfc, SERVER_SOFTWARE, serverSoftware, &dwLen)) {
694
iis5 = (atof(serverSoftware + 14) >= 5.0);
696
jk_log(logger, JK_LOG_DEBUG, "Detected IIS >= 5.0");
699
jk_log(logger, JK_LOG_DEBUG, "Detected IIS < 5.0");
705
(((SF_NOTIFY_PREPROC_HEADERS == dwNotificationType) && !iis5) ||
706
((SF_NOTIFY_AUTH_COMPLETE == dwNotificationType) && iis5)
709
char uri[INTERNET_MAX_URL_LENGTH];
710
char snuri[INTERNET_MAX_URL_LENGTH] = "/";
711
char Host[INTERNET_MAX_URL_LENGTH] = "";
712
char Port[INTERNET_MAX_URL_LENGTH] = "";
713
char Translate[INTERNET_MAX_URL_LENGTH];
714
BOOL(WINAPI * GetHeader)
715
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
716
LPVOID lpvBuffer, LPDWORD lpdwSize);
717
BOOL(WINAPI * SetHeader)
718
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
720
BOOL(WINAPI * AddHeader)
721
(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
724
DWORD sz = sizeof(uri);
725
DWORD szHost = sizeof(Host);
726
DWORD szPort = sizeof(Port);
727
DWORD szTranslate = sizeof(Translate);
731
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->GetHeader;
733
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->SetHeader;
735
((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->AddHeader;
739
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->GetHeader;
741
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->SetHeader;
743
((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->AddHeader;
746
if (JK_IS_DEBUG_LEVEL(logger))
747
jk_log(logger, JK_LOG_DEBUG, "Filter started");
750
* Just in case somebody set these headers in the request!
752
SetHeader(pfc, URI_HEADER_NAME, NULL);
753
SetHeader(pfc, QUERY_HEADER_NAME, NULL);
754
SetHeader(pfc, WORKER_HEADER_NAME, NULL);
755
SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
757
if (!GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz)) {
758
jk_log(logger, JK_LOG_ERROR,
759
"error while getting the url");
760
return SF_STATUS_REQ_ERROR;
765
const char *worker = NULL;
766
query = strchr(uri, '?');
771
rc = unescape_url(uri);
772
if (rc == BAD_REQUEST) {
773
jk_log(logger, JK_LOG_ERROR,
774
"[%s] contains one or more invalid escape sequences.",
776
write_error_response(pfc, "400 Bad Request",
778
return SF_STATUS_REQ_FINISHED;
780
else if (rc == BAD_PATH) {
781
jk_log(logger, JK_LOG_EMERG,
782
"[%s] contains forbidden escape sequences.",
784
write_error_response(pfc, "404 Not Found",
786
return SF_STATUS_REQ_FINISHED;
790
GetServerVariable(pfc, SERVER_NAME, (LPVOID) Host,
791
(LPDWORD) & szHost)) {
793
Host[szHost - 1] = '\0';
798
GetServerVariable(pfc, "SERVER_PORT", (LPVOID) Port,
799
(LPDWORD) & szPort)) {
801
Port[szPort - 1] = '\0';
805
if (szPort != 80 && szPort != 443 && szHost > 0) {
812
if (JK_IS_DEBUG_LEVEL(logger))
813
jk_log(logger, JK_LOG_DEBUG,
814
"Virtual Host redirection of %s",
816
worker = map_uri_to_worker(uw_map, snuri, logger);
819
if (JK_IS_DEBUG_LEVEL(logger))
820
jk_log(logger, JK_LOG_DEBUG,
821
"Default redirection of %s",
823
worker = map_uri_to_worker(uw_map, uri, logger);
826
* Check if somebody is feading us with his own TOMCAT data headers.
827
* We reject such postings !
829
if (JK_IS_DEBUG_LEVEL(logger))
830
jk_log(logger, JK_LOG_DEBUG,
831
"check if [%s] is points to the web-inf directory",
834
if (uri_is_web_inf(uri)) {
835
jk_log(logger, JK_LOG_EMERG,
836
"[%s] points to the web-inf or meta-inf directory.\nSomebody try to hack into the site!!!",
839
write_error_response(pfc, "404 Not Found",
841
return SF_STATUS_REQ_FINISHED;
847
/* This is a servlet, should redirect ... */
848
jk_log(logger, JK_LOG_DEBUG,
849
"[%s] is a servlet url - should redirect to %s",
852
/* get URI we should forward */
853
if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
854
/* get original unparsed URI */
855
GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz);
856
/* restore terminator for uri portion */
859
if (JK_IS_DEBUG_LEVEL(logger))
860
jk_log(logger, JK_LOG_DEBUG,
861
"fowarding original URI [%s]",
865
else if (uri_select_option == URI_SELECT_OPT_ESCAPED) {
866
if (!escape_url(uri, snuri, INTERNET_MAX_URL_LENGTH)) {
867
jk_log(logger, JK_LOG_ERROR,
868
"[%s] re-encoding request exceeds maximum buffer size.",
870
write_error_response(pfc, "400 Bad Request",
872
return SF_STATUS_REQ_FINISHED;
874
if (JK_IS_DEBUG_LEVEL(logger))
875
jk_log(logger, JK_LOG_DEBUG,
876
"fowarding escaped URI [%s]",
884
if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
885
((query != NULL && strlen(query) > 0)
886
? !AddHeader(pfc, QUERY_HEADER_NAME, query) : FALSE) ||
887
!AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) ||
888
!SetHeader(pfc, "url", extension_uri)) {
889
jk_log(logger, JK_LOG_ERROR,
890
"error while adding request headers");
891
return SF_STATUS_REQ_ERROR;
894
/* Move Translate: header to a temporary header so
895
* that the extension proc will be called.
896
* This allows the servlet to handle 'Translate: f'.
899
(pfc, "Translate:", (LPVOID) Translate,
900
(LPDWORD) & szTranslate) && Translate != NULL
901
&& szTranslate > 0) {
903
(pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
904
jk_log(logger, JK_LOG_ERROR,
905
"error while adding Tomcat-Translate headers");
906
return SF_STATUS_REQ_ERROR;
908
SetHeader(pfc, "Translate:", NULL);
910
if (!pfc->pFilterContext) {
911
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->AllocMem(pfc, sizeof(isapi_log_data_t), 0);
913
jk_log(logger, JK_LOG_ERROR,
914
"error while allocating memory");
915
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
916
return SF_STATUS_REQ_ERROR;
918
memset(ld, 0, sizeof(isapi_log_data_t));
919
strcpy(ld->uri, forwardURI);
920
if (query && strlen(query) > 0)
921
strcpy(ld->query, query);
922
pfc->pFilterContext = ld;
926
if (JK_IS_DEBUG_LEVEL(logger))
927
jk_log(logger, JK_LOG_DEBUG,
928
"[%s] is not a servlet url", uri);
932
else if (is_inited && (dwNotificationType == SF_NOTIFY_LOG)) {
933
if (pfc->pFilterContext) {
934
isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
935
HTTP_FILTER_LOG *pl = (HTTP_FILTER_LOG *)pvNotification;
936
pl->pszTarget = ld->uri;
937
pl->pszParameters = ld->query;
940
return SF_STATUS_REQ_NEXT_NOTIFICATION;
944
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
946
pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
948
strcpy(pVer->lpszExtensionDesc, VERSION_STRING);
952
return initialize_extension();
958
DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb)
960
DWORD rc = HSE_STATUS_ERROR;
962
lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
964
JK_TRACE_ENTER(logger);
967
if (is_inited && !is_mapread) {
968
char serverName[MAX_SERVERNAME];
969
DWORD dwLen = sizeof(serverName);
971
GetServerVariable(lpEcb->ConnID, SERVER_NAME, serverName,
974
serverName[dwLen - 1] = '\0';
975
if (init_jk(serverName))
976
is_mapread = JK_TRUE;
979
is_inited = JK_FALSE;
983
isapi_private_data_t private_data;
985
jk_pool_atom_t buf[SMALL_POOL_SIZE];
989
jk_init_ws_service(&s);
990
jk_open_pool(&private_data.p, buf, sizeof(buf));
992
private_data.request_started = JK_FALSE;
993
private_data.bytes_read_so_far = 0;
994
private_data.lpEcb = lpEcb;
996
s.ws_private = &private_data;
997
s.pool = &private_data.p;
999
if (init_ws_service(&private_data, &s, &worker_name)) {
1000
jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
1002
if (JK_IS_DEBUG_LEVEL(logger))
1003
jk_log(logger, JK_LOG_DEBUG,
1004
"%s a worker for name %s",
1005
worker ? "got" : "could not get", worker_name);
1008
jk_endpoint_t *e = NULL;
1009
/* Update retries for this worker */
1010
s.retries = worker->retries;
1011
if (worker->get_endpoint(worker, &e, logger)) {
1012
int is_error = JK_HTTP_SERVER_ERROR;
1013
if (e->service(e, &s, logger, &is_error)) {
1014
rc = HSE_STATUS_SUCCESS;
1015
lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
1016
jk_log(logger, JK_LOG_DEBUG,
1017
"service() returned OK");
1020
lpEcb->dwHttpStatusCode = is_error;
1021
jk_log(logger, JK_LOG_ERROR,
1022
"service() failed");
1024
e->done(&e, logger);
1028
jk_log(logger, JK_LOG_ERROR,
1029
"could not get a worker for name %s",
1033
jk_close_pool(&private_data.p);
1036
jk_log(logger, JK_LOG_ERROR,
1040
JK_TRACE_EXIT(logger);
1046
BOOL WINAPI TerminateExtension(DWORD dwFlags)
1048
return TerminateFilter(dwFlags);
1051
BOOL WINAPI TerminateFilter(DWORD dwFlags)
1053
UNREFERENCED_PARAMETER(dwFlags);
1056
is_inited = JK_FALSE;
1059
uri_worker_map_free(&uw_map, logger);
1060
is_mapread = JK_FALSE;
1064
jk_close_file_logger(&logger);
1072
BOOL WINAPI DllMain(HINSTANCE hInst, // Instance Handle of the DLL
1073
ULONG ulReason, // Reason why NT called this DLL
1074
LPVOID lpReserved) // Reserved parameter for future use
1076
BOOL fReturn = TRUE;
1077
char drive[_MAX_DRIVE];
1079
char fname[_MAX_FNAME];
1080
char file_name[_MAX_PATH];
1082
UNREFERENCED_PARAMETER(lpReserved);
1085
case DLL_PROCESS_ATTACH:
1086
if (GetModuleFileName(hInst, file_name, sizeof(file_name))) {
1087
_splitpath(file_name, drive, dir, fname, NULL);
1088
_makepath(ini_file_name, drive, dir, fname, ".properties");
1094
case DLL_PROCESS_DETACH:
1096
TerminateFilter(HSE_TERM_MUST_UNLOAD);
1109
static int init_jk(char *serverName)
1114
if (!jk_open_file_logger(&logger, log_file, log_level)) {
1117
/* Simulate shared memory
1118
* For now use fixed size.
1120
jk_shm_open(NULL, JK_SHM_DEF_SIZE, logger);
1122
/* 10 is minimum supported on WINXP */
1123
jk_set_worker_def_cache_size(10);
1125
/* Logging the initialization type: registry or properties file in virtual dir
1127
if (JK_IS_DEBUG_LEVEL(logger)) {
1128
if (using_ini_file) {
1129
jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.", ini_file_name);
1132
jk_log(logger, JK_LOG_DEBUG, "Using registry.");
1135
jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
1136
jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
1137
jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
1138
jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
1139
jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
1141
jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option);
1143
if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
1145
uw_map->fname = worker_mount_file;
1146
if (worker_mount_file[0])
1147
rc = uri_worker_map_load(uw_map, logger);
1151
if (jk_map_alloc(&map)) {
1152
if (jk_map_read_properties(map, worker_file, NULL)) {
1153
/* we add the URI->WORKER MAP since workers using AJP14 will feed it */
1155
worker_env.uri_to_worker = uw_map;
1156
worker_env.server_name = serverName;
1158
if (wc_open(map, &worker_env, logger)) {
1163
jk_log(logger, JK_LOG_EMERG,
1164
"Unable to read worker file %s.", worker_file);
1173
static int initialize_extension(void)
1176
if (read_registry_init_data()) {
1177
is_inited = JK_TRUE;
1182
int parse_uri_select(const char *uri_select)
1184
if (0 == strcasecmp(uri_select, URI_SELECT_PARSED_VERB)) {
1185
return URI_SELECT_OPT_PARSED;
1188
if (0 == strcasecmp(uri_select, URI_SELECT_UNPARSED_VERB)) {
1189
return URI_SELECT_OPT_UNPARSED;
1192
if (0 == strcasecmp(uri_select, URI_SELECT_ESCAPED_VERB)) {
1193
return URI_SELECT_OPT_ESCAPED;
1199
static int read_registry_init_data(void)
1201
char tmpbuf[INTERNET_MAX_URL_LENGTH];
1208
if (jk_map_alloc(&map)) {
1209
if (jk_map_read_properties(map, ini_file_name, NULL)) {
1210
using_ini_file = JK_TRUE;
1213
if (using_ini_file) {
1214
tmp = jk_map_get_string(map, JK_LOG_FILE_TAG, NULL);
1216
strcpy(log_file, tmp);
1221
tmp = jk_map_get_string(map, JK_LOG_LEVEL_TAG, NULL);
1223
log_level = jk_parse_log_level(tmp);
1228
tmp = jk_map_get_string(map, EXTENSION_URI_TAG, NULL);
1230
strcpy(extension_uri, tmp);
1235
tmp = jk_map_get_string(map, JK_WORKER_FILE_TAG, NULL);
1237
strcpy(worker_file, tmp);
1242
tmp = jk_map_get_string(map, JK_MOUNT_FILE_TAG, NULL);
1244
strcpy(worker_mount_file, tmp);
1249
tmp = jk_map_get_string(map, URI_SELECT_TAG, NULL);
1251
int opt = parse_uri_select(tmp);
1253
uri_select_option = opt;
1262
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1263
REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
1264
if (ERROR_SUCCESS != rc) {
1268
if (get_registry_config_parameter(hkey,
1270
tmpbuf, sizeof(log_file))) {
1271
strcpy(log_file, tmpbuf);
1277
if (get_registry_config_parameter(hkey,
1279
tmpbuf, sizeof(tmpbuf))) {
1280
log_level = jk_parse_log_level(tmpbuf);
1286
if (get_registry_config_parameter(hkey,
1288
tmpbuf, sizeof(extension_uri))) {
1289
strcpy(extension_uri, tmpbuf);
1295
if (get_registry_config_parameter(hkey,
1297
tmpbuf, sizeof(worker_file))) {
1298
strcpy(worker_file, tmpbuf);
1304
if (get_registry_config_parameter(hkey,
1307
sizeof(worker_mount_file))) {
1308
strcpy(worker_mount_file, tmpbuf);
1314
if (get_registry_config_parameter(hkey,
1316
tmpbuf, sizeof(tmpbuf))) {
1317
int opt = parse_uri_select(tmpbuf);
1319
uri_select_option = opt;
1331
static int get_registry_config_parameter(HKEY hkey,
1332
const char *tag, char *b, DWORD sz)
1337
lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
1338
if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
1347
static int init_ws_service(isapi_private_data_t * private_data,
1348
jk_ws_service_t *s, char **worker_name)
1350
char huge_buf[16 * 1024]; /* should be enough for all */
1354
s->jvm_route = NULL;
1356
s->start_response = start_response;
1361
/* Clear RECO status */
1362
s->reco_status = RECO_NONE;
1364
GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));
1365
GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);
1366
GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);
1368
if (s->req_uri == NULL) {
1369
s->query_string = private_data->lpEcb->lpszQueryString;
1370
*worker_name = DEFAULT_WORKER_NAME;
1371
GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);
1372
if (unescape_url(s->req_uri) < 0)
1374
getparents(s->req_uri);
1377
GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
1378
GET_SERVER_VARIABLE_VALUE("REMOTE_USER", s->remote_user);
1379
GET_SERVER_VARIABLE_VALUE("SERVER_PROTOCOL", s->protocol);
1380
GET_SERVER_VARIABLE_VALUE("REMOTE_HOST", s->remote_host);
1381
GET_SERVER_VARIABLE_VALUE("REMOTE_ADDR", s->remote_addr);
1382
GET_SERVER_VARIABLE_VALUE(SERVER_NAME, s->server_name);
1383
GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80);
1384
GET_SERVER_VARIABLE_VALUE(SERVER_SOFTWARE, s->server_software);
1385
GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
1387
s->method = private_data->lpEcb->lpszMethod;
1388
s->content_length = private_data->lpEcb->cbTotalBytes;
1391
s->ssl_cert_len = 0;
1392
s->ssl_cipher = NULL;
1393
s->ssl_session = NULL;
1394
s->ssl_key_size = -1;
1396
s->headers_names = NULL;
1397
s->headers_values = NULL;
1401
* Add SSL IIS environment
1404
char *ssl_env_names[9] = {
1408
"HTTPS_SERVER_SUBJECT",
1410
"HTTPS_SECRETKEYSIZE",
1411
"CERT_SERIALNUMBER",
1412
"HTTPS_SERVER_ISSUER",
1415
char *ssl_env_values[9] = {
1427
unsigned int num_of_vars = 0;
1429
for (i = 0; i < 9; i++) {
1430
GET_SERVER_VARIABLE_VALUE(ssl_env_names[i], ssl_env_values[i]);
1431
if (ssl_env_values[i]) {
1438
s->attributes_names =
1439
jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
1440
s->attributes_values =
1441
jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
1444
for (i = 0; i < 9; i++) {
1445
if (ssl_env_values[i]) {
1446
s->attributes_names[j] = ssl_env_names[i];
1447
s->attributes_values[j] = ssl_env_values[i];
1451
s->num_attributes = num_of_vars;
1452
if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
1454
cc.cbAllocated = sizeof(huge_buf);
1455
cc.CertContext.pbCertEncoded = (BYTE *) huge_buf;
1456
cc.CertContext.cbCertEncoded = 0;
1458
if (private_data->lpEcb->
1459
ServerSupportFunction(private_data->lpEcb->ConnID,
1460
(DWORD) HSE_REQ_GET_CERT_INFO_EX,
1461
(LPVOID) & cc, NULL,
1463
jk_log(logger, JK_LOG_DEBUG,
1464
"Client Certificate encoding:%d sz:%d flags:%ld",
1466
dwCertEncodingType & X509_ASN_ENCODING,
1467
cc.CertContext.cbCertEncoded,
1468
cc.dwCertificateFlags);
1470
jk_pool_alloc(&private_data->p,
1471
base64_encode_cert_len(cc.CertContext.
1474
s->ssl_cert_len = base64_encode_cert(s->ssl_cert,
1483
huge_buf_sz = sizeof(huge_buf);
1484
if (get_server_value(private_data->lpEcb,
1485
"ALL_HTTP", huge_buf, huge_buf_sz)) {
1486
unsigned int cnt = 0;
1489
for (tmp = huge_buf; *tmp; tmp++) {
1496
char *headers_buf = jk_pool_strdup(&private_data->p, huge_buf);
1498
size_t len_of_http_prefix = strlen("HTTP_");
1499
BOOL need_content_length_header = (s->content_length == 0);
1501
cnt -= 2; /* For our two special headers */
1502
/* allocate an extra header slot in case we need to add a content-length header */
1504
jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
1506
jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
1508
if (!s->headers_names || !s->headers_values || !headers_buf) {
1512
for (i = 0, tmp = headers_buf; *tmp && i < cnt;) {
1513
int real_header = JK_TRUE;
1515
/* Skipp the HTTP_ prefix to the beginning of th header name */
1516
tmp += len_of_http_prefix;
1518
if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME))
1519
|| !strnicmp(tmp, WORKER_HEADER_NAME,
1520
strlen(WORKER_HEADER_NAME))) {
1521
real_header = JK_FALSE;
1523
else if (need_content_length_header &&
1524
!strnicmp(tmp, CONTENT_LENGTH,
1525
strlen(CONTENT_LENGTH))) {
1526
need_content_length_header = FALSE;
1527
s->headers_names[i] = tmp;
1529
else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
1530
strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
1531
tmp += 6; /* TOMCAT */
1532
s->headers_names[i] = tmp;
1535
s->headers_names[i] = tmp;
1538
while (':' != *tmp && *tmp) {
1543
*tmp = JK_TOLOWER(*tmp);
1550
/* Skip all the WS chars after the ':' to the beginning of th header value */
1551
while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
1556
s->headers_values[i] = tmp;
1559
while (*tmp != '\n' && *tmp != '\r') {
1566
while (*tmp == '\n' || *tmp == '\r') {
1574
/* Add a content-length = 0 header if needed.
1575
* Ajp13 assumes an absent content-length header means an unknown,
1576
* but non-zero length body.
1578
if (need_content_length_header) {
1579
s->headers_names[cnt] = "Content-Length";
1580
s->headers_values[cnt] = "0";
1583
s->num_headers = cnt;
1586
/* We must have our two headers */
1597
static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
1598
char *name, char *buf, DWORD bufsz)
1602
if (!lpEcb->GetServerVariable(lpEcb->ConnID, name,
1603
buf, (LPDWORD) &sz))
1611
static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
1613
static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
1615
static const char basis_64[] =
1616
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1618
static int base64_encode_cert_len(int len)
1620
int n = ((len + 2) / 3 * 4) + 1; /* base64 encoded size */
1621
n += (n + 63 / 64) * 2; /* add CRLF's */
1622
n += sizeof(begin_cert) + sizeof(end_cert) - 2; /* add enclosing strings. */
1626
static int base64_encode_cert(char *encoded,
1627
const char *string, int len)
1640
for (i = 0; i < len - 2; i += 3) {
1641
*p++ = basis_64[(string[i] >> 2) & 0x3F];
1642
*p++ = basis_64[((string[i] & 0x3) << 4) |
1643
((int)(string[i + 1] & 0xF0) >> 4)];
1644
*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
1645
((int)(string[i + 2] & 0xC0) >> 6)];
1646
*p++ = basis_64[string[i + 2] & 0x3F];
1655
*p++ = basis_64[(string[i] >> 2) & 0x3F];
1656
if (i == (len - 1)) {
1657
*p++ = basis_64[((string[i] & 0x3) << 4)];
1661
*p++ = basis_64[((string[i] & 0x3) << 4) |
1662
((int)(string[i + 1] & 0xF0) >> 4)];
1663
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
1678
return (int)(p - encoded);