2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 /***************************************************************************
19 * Description: NSAPI plugin for Netscape servers *
20 * Author: Gal Shachor <shachor@il.ibm.com> *
21 * Version: $Revision: 1078805 $ *
22 ***************************************************************************/
26 #define _WIN32_WINNT 0x0500
32 #include "jk_global.h"
37 #include "jk_service.h"
38 #include "jk_worker.h"
42 #define URI_PATTERN "path"
43 #define DEFAULT_WORKER_NAME ("ajp13")
44 #define REJECT_UNSAFE_TAG "reject_unsafe"
46 #define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
48 struct nsapi_private_data
56 typedef struct nsapi_private_data nsapi_private_data_t;
58 static int init_on_other_thread_is_done = JK_FALSE;
59 static int init_on_other_thread_is_ok = JK_FALSE;
61 static const char ssl_cert_start[] = "-----BEGIN CERTIFICATE-----\r\n";
62 static const char ssl_cert_end[] = "\r\n-----END CERTIFICATE-----\r\n";
64 static jk_logger_t *logger = NULL;
65 static jk_worker_env_t worker_env;
66 static jk_map_t *init_map = NULL;
67 static jk_uri_worker_map_t *uw_map = NULL;
68 static size_t jk_shm_size = 0;
71 int (*PR_IsSocketSecure) (SYS_NETFD * csd); /* pointer to PR_IsSocketSecure function */
74 static int JK_METHOD start_response(jk_ws_service_t *s,
77 const char *const *header_names,
78 const char *const *header_values,
79 unsigned num_of_headers);
81 static int JK_METHOD ws_read(jk_ws_service_t *s,
82 void *b, unsigned l, unsigned *a);
84 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l);
86 NSAPI_PUBLIC int jk_init(pblock * pb, Session * sn, Request * rq);
88 NSAPI_PUBLIC void jk_term(void *p);
90 NSAPI_PUBLIC int jk_service(pblock * pb, Session * sn, Request * rq);
92 static int init_ws_service(nsapi_private_data_t * private_data,
95 static int setup_http_headers(nsapi_private_data_t * private_data,
98 static void init_workers_on_other_threads(void *init_d)
100 init_map = (jk_map_t *)init_d;
101 /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
102 /* but where are they here in Netscape ? */
103 if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
105 uw_map->reload = JK_URIMAP_DEF_RELOAD;
106 uw_map->reject_unsafe = jk_map_get_bool(init_map, "worker." REJECT_UNSAFE_TAG, JK_FALSE);
107 worker_env.uri_to_worker = uw_map;
108 worker_env.pool = NULL;
110 if (wc_open(init_map, &worker_env, logger)) {
111 init_on_other_thread_is_ok = JK_TRUE;
112 uri_worker_map_ext(uw_map, logger);
113 uri_worker_map_switch(uw_map, logger);
116 jk_log(logger, JK_LOG_EMERG,
117 "In init_workers_on_other_threads, failed");
121 jk_log(logger, JK_LOG_EMERG,
122 "In init_workers_on_other_threads, failed");
125 init_on_other_thread_is_done = JK_TRUE;
129 * Convert string to lower case.
130 * If string is longer than the provided buffer,
131 * just return the original string.
133 static const char *to_lower(const char *str, char *buf, int bufsz)
135 const char *from = str;
137 char *end = buf + (bufsz - 1);
138 while (to != end && *from) {
139 *to = (char)tolower(*from);
150 static int JK_METHOD start_response(jk_ws_service_t *s,
153 const char *const *header_names,
154 const char *const *header_values,
155 unsigned num_of_headers)
157 if (s && s->ws_private) {
158 nsapi_private_data_t *p = s->ws_private;
159 if (!s->response_started) {
162 s->response_started = JK_TRUE;
164 /* Remove "old" content type */
165 param_free(pblock_remove("content-type", p->rq->srvhdrs));
167 if (num_of_headers) {
169 * NSAPI expects http header names to be lower case.
170 * This is only relevant for the server itself or other
171 * plugins searching for headers in the pblock.
172 * We use a buffer of limited length, because conforming
173 * with this rule should only matter for well-known headers.
176 for (i = 0; i < (int)num_of_headers; i++) {
177 pblock_nvinsert(to_lower(header_names[i], name_buf, 64),
178 header_values[i], p->rq->srvhdrs);
182 pblock_nvinsert("content-type", "text/plain", p->rq->srvhdrs);
185 protocol_status(p->sn, p->rq, status, (char *)reason);
187 protocol_start_response(p->sn, p->rq);
195 static int JK_METHOD ws_read(jk_ws_service_t *s,
196 void *b, unsigned l, unsigned *a)
198 if (s && s->ws_private && b && a) {
199 nsapi_private_data_t *p = s->ws_private;
204 netbuf *inbuf = p->sn->inbuf;
206 /* Until we get a service pack for NW5.1 and earlier that has the latest */
207 /* Enterprise Server, we have to go through the else version of this code*/
208 #if defined(netbuf_getbytes) && !defined(NETWARE)
209 i = netbuf_getbytes(inbuf, b, l);
210 if (NETBUF_EOF == i || NETBUF_ERROR == i) {
217 for (i = 0; i < l; i++) {
218 ch = netbuf_getc(inbuf);
220 * IO_EOF is 0 (zero) which is a very reasonable byte
221 * when it comes to binary data. So we are not breaking
222 * out of the read loop when reading it.
224 * We are protected from an infinit loop by the Java part of
227 if (IO_ERROR == ch) {
246 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l)
248 if (s && s->ws_private && b) {
249 nsapi_private_data_t *p = s->ws_private;
252 if (!s->response_started) {
253 start_response(s, 200, NULL, NULL, NULL, 0);
256 if (net_write(p->sn->csd, (char *)b, (int)l) == IO_ERROR) {
267 NSAPI_PUBLIC int jk_init(pblock * pb, Session * sn, Request * rq)
269 char *worker_prp_file = pblock_findval(JK_WORKER_FILE_TAG, pb);
270 char *log_level_str = pblock_findval(JK_LOG_LEVEL_TAG, pb);
271 char *log_file = pblock_findval(JK_LOG_FILE_TAG, pb);
272 char *shm_file = pblock_findval(JK_SHM_FILE_TAG, pb);
273 char *shm_file_safe = "";
274 char *reject_unsafe = pblock_findval(REJECT_UNSAFE_TAG, pb);
276 int rc = REQ_ABORTED;
278 if (!worker_prp_file) {
279 worker_prp_file = JK_WORKER_FILE_DEF;
282 if (!log_level_str) {
283 log_level_str = JK_LOG_DEF_VERB;
288 "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_LOG_FILE_TAG);
293 shm_file_safe = shm_file;
295 #if !defined(WIN32) && !defined(NETWARE)
298 "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_SHM_FILE_TAG);
304 "In jk_init.\n Worker file = %s.\n Log level = %s.\n Log File = %s\n SHM File = %s\n",
305 worker_prp_file, log_level_str, log_file, shm_file);
307 if (!jk_open_file_logger(&logger, log_file,
308 jk_parse_log_level(log_level_str))) {
312 if (jk_map_alloc(&init_map)) {
313 if (jk_map_read_properties(init_map, NULL, worker_prp_file, NULL,
314 JK_MAP_HANDLE_DUPLICATES, logger)) {
319 if (jk_map_resolve_references(init_map, "worker.", 1, 1, logger) == JK_FALSE) {
320 jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
324 jk_map_add(init_map, "worker." REJECT_UNSAFE_TAG, reject_unsafe);
327 jk_shm_size = jk_shm_calculate_size(init_map, logger);
328 if ((rv = jk_shm_open(shm_file, jk_shm_size, logger)) != 0)
329 jk_log(logger, JK_LOG_ERROR,
330 "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
333 s = systhread_start(SYSTHREAD_DEFAULT_PRIORITY,
334 0, init_workers_on_other_threads, init_map);
335 for (sleep_cnt = 0; sleep_cnt < 60; sleep_cnt++) {
336 systhread_sleep(1000);
337 jk_log(logger, JK_LOG_DEBUG, "jk_init, a second passed");
338 if (init_on_other_thread_is_done) {
343 if (init_on_other_thread_is_done && init_on_other_thread_is_ok) {
344 magnus_atrestart(jk_term, NULL);
346 jk_log(logger, JK_LOG_INFO, "%s initialized", JK_FULL_EXPOSED_VERSION);
349 /* if(wc_open(init_map, NULL, logger)) {
350 magnus_atrestart(jk_term, NULL);
359 (int (*)(void **))ImportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
364 NSAPI_PUBLIC void jk_term(void *p)
367 if (NULL != PR_IsSocketSecure) {
368 UnimportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
369 PR_IsSocketSecure = NULL;
373 uri_worker_map_free(&uw_map, logger);
377 jk_map_free(&init_map);
383 jk_close_file_logger(&logger);
387 NSAPI_PUBLIC int jk_service(pblock * pb, Session * sn, Request * rq)
389 char *worker_name = pblock_findval(JK_WORKER_NAME_TAG, pb);
390 char *uri_pattern = pblock_findval(URI_PATTERN, pb);
392 int rc = REQ_ABORTED;
395 char *uri = pblock_findval("uri", rq->reqpb);
397 if (0 != shexp_match(uri, uri_pattern)) {
403 worker_name = DEFAULT_WORKER_NAME;
406 worker = wc_get_worker_for_name(worker_name, logger);
408 nsapi_private_data_t private_data;
410 jk_pool_atom_t buf[SMALL_POOL_SIZE];
412 jk_open_pool(&private_data.p, buf, sizeof(buf));
414 private_data.pb = pb;
415 private_data.sn = sn;
416 private_data.rq = rq;
418 jk_init_ws_service(&s);
420 s.ws_private = &private_data;
421 s.pool = &private_data.p;
424 if (init_ws_service(&private_data, &s)) {
425 jk_endpoint_t *e = NULL;
426 if (worker->get_endpoint(worker, &e, logger)) {
427 int is_error = JK_HTTP_SERVER_ERROR;
429 if ((result = e->service(e, &s, logger, &is_error)) > 0) {
431 if (JK_IS_DEBUG_LEVEL(logger))
432 jk_log(logger, JK_LOG_DEBUG,
433 "service() returned OK");
436 protocol_status(sn, rq, is_error, NULL);
437 if ((result == JK_CLIENT_ERROR) && (is_error == JK_HTTP_OK)) {
439 jk_log(logger, JK_LOG_INFO,
440 "service() failed because client aborted connection");
444 jk_log(logger, JK_LOG_ERROR,
445 "service() failed with http error %d", is_error);
452 jk_close_pool(&private_data.p);
458 static int init_ws_service(nsapi_private_data_t * private_data,
465 s->start_response = start_response;
469 s->auth_type = pblock_findval("auth-type", private_data->rq->vars);
470 s->remote_user = pblock_findval("auth-user", private_data->rq->vars);
473 rc = request_header("content-length",
474 &tmp, private_data->sn, private_data->rq);
476 if ((rc != REQ_ABORTED) && tmp) {
477 sscanf(tmp, "%" JK_UINT64_T_FMT, &(s->content_length));
480 s->method = pblock_findval("method", private_data->rq->reqpb);
481 s->protocol = pblock_findval("protocol", private_data->rq->reqpb);
483 s->remote_host = session_dns(private_data->sn);
484 s->remote_addr = pblock_findval("ip", private_data->sn->client);
485 /* Remote port is not available from NSAPI. */
486 s->remote_port = "0";
488 tmp = pblock_findval("uri", private_data->rq->reqpb);
489 size = 3 * strlen(tmp) + 1;
490 s->req_uri = jk_pool_alloc(s->pool, size);
491 jk_canonenc(tmp, s->req_uri, size);
493 s->query_string = pblock_findval("query", private_data->rq->reqpb);
495 s->server_name = server_hostname;
498 /* On NetWare, since we have virtual servers, we have a different way of
499 * getting the port that we need to try first.
501 tmp = pblock_findval("server_port", private_data->sn->client);
503 s->server_port = atoi(tmp);
506 s->server_port = server_portnum;
507 s->server_software = system_version();
512 /* on NetWare, we can have virtual servers that are secure.
513 * PR_IsSocketSecure is an api made available with virtual servers to check
514 * if the socket is secure or not
516 if (NULL != PR_IsSocketSecure)
517 s->is_ssl = PR_IsSocketSecure(private_data->sn->csd);
520 s->is_ssl = security_active;
523 char *ssl_cert = pblock_findval("auth-cert", private_data->rq->vars);
524 if (ssl_cert != NULL) {
525 s->ssl_cert = jk_pool_alloc(s->pool, sizeof(ssl_cert_start)+
527 sizeof(ssl_cert_end));
528 strcpy(s->ssl_cert, ssl_cert_start);
529 strcat(s->ssl_cert, ssl_cert);
530 strcat(s->ssl_cert, ssl_cert_end);
531 s->ssl_cert_len = strlen(s->ssl_cert);
533 s->ssl_cipher = pblock_findval("cipher", private_data->sn->client);
534 s->ssl_session = pblock_findval("ssl-id", private_data->sn->client);
535 /* XXX: We need to investigate how to set s->ssl_key_size */
538 rc = setup_http_headers(private_data, s);
540 /* Dump all connection param so we can trace what's going to
543 if (JK_IS_DEBUG_LEVEL(logger)) {
544 jk_log(logger, JK_LOG_DEBUG,
545 "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d auth=%s user=%s uri=%s",
546 STRNULL_FOR_NULL(s->protocol),
547 STRNULL_FOR_NULL(s->method),
548 STRNULL_FOR_NULL(s->remote_host),
549 STRNULL_FOR_NULL(s->remote_addr),
550 STRNULL_FOR_NULL(s->server_name),
552 STRNULL_FOR_NULL(s->auth_type),
553 STRNULL_FOR_NULL(s->remote_user),
554 STRNULL_FOR_NULL(s->req_uri));
561 static int setup_http_headers(nsapi_private_data_t * private_data,
564 int need_content_length_header =
565 (s->content_length == 0) ? JK_TRUE : JK_FALSE;
567 pblock *headers_jar = private_data->rq->headers;
571 for (i = 0, cnt = 0; i < headers_jar->hsize; i++) {
572 struct pb_entry *h = headers_jar->ht[i];
573 while (h && h->param) {
579 s->headers_names = NULL;
580 s->headers_values = NULL;
581 s->num_headers = cnt;
583 /* allocate an extra header slot in case we need to add a content-length header */
585 jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
587 jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
589 if (s->headers_names && s->headers_values) {
590 for (i = 0, cnt = 0; i < headers_jar->hsize; i++) {
591 struct pb_entry *h = headers_jar->ht[i];
592 while (h && h->param) {
593 s->headers_names[cnt] = h->param->name;
594 s->headers_values[cnt] = h->param->value;
595 if (need_content_length_header &&
596 !strncmp(h->param->name, "content-length", 14)) {
597 need_content_length_header = JK_FALSE;
603 /* Add a content-length = 0 header if needed.
604 * Ajp13 assumes an absent content-length header means an unknown,
605 * but non-zero length body.
607 if (need_content_length_header) {
608 s->headers_names[cnt] = "content-length";
609 s->headers_values[cnt] = "0";
612 s->num_headers = cnt;
617 if (need_content_length_header) {
619 jk_pool_alloc(&private_data->p, sizeof(char *));
621 jk_pool_alloc(&private_data->p, sizeof(char *));
622 if (s->headers_names && s->headers_values) {
623 s->headers_names[0] = "content-length";
624 s->headers_values[0] = "0";