Apache HTTPD
mod_isapi.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * mod_isapi.c - Internet Server Application (ISA) module for Apache
19 * by Alexei Kosut <[email protected]>, significant overhauls and
20 * redesign by William Rowe <[email protected]>, and hints from many
21 * other developer/users who have hit on specific flaws.
22 *
23 * This module implements the ISAPI Handler architecture, allowing
24 * Apache to load Internet Server Applications (ISAPI extensions),
25 * similar to the support in IIS, Zope, O'Reilly's WebSite and others.
26 *
27 * It is a complete implementation of the ISAPI 2.0 specification,
28 * except for "Microsoft extensions" to the API which provide
29 * asynchronous I/O. It is further extended to include additional
30 * "Microsoft extensions" through IIS 5.0, with some deficiencies
31 * where one-to-one mappings don't exist.
32 *
33 * Refer to /manual/mod/mod_isapi.html for additional details on
34 * configuration and use, but check this source for specific support
35 * of the API,
36 */
37
38#include "ap_config.h"
39#include "httpd.h"
40#include "http_config.h"
41#include "http_core.h"
42#include "http_protocol.h"
43#include "http_request.h"
44#include "http_log.h"
45#include "util_script.h"
46#include "mod_core.h"
47#include "apr_lib.h"
48#include "apr_strings.h"
49#include "apr_portable.h"
50#include "apr_buckets.h"
51#include "apr_thread_mutex.h"
52#include "apr_thread_rwlock.h"
53#include "apr_hash.h"
54#include "mod_isapi.h"
55
56/* Retry frequency for a failed-to-load isapi .dll */
57#define ISAPI_RETRY apr_time_from_sec(30)
58
59/**********************************************************
60 *
61 * ISAPI Module Configuration
62 *
63 **********************************************************/
64
65module AP_MODULE_DECLARE_DATA isapi_module;
66
67#define ISAPI_UNDEF -1
68
69/* Our isapi per-dir config structure */
77
79
81 const char *fpath, isapi_loaded** isa);
82
84{
86
87 dir->read_ahead_buflen = ISAPI_UNDEF;
88 dir->log_unsupported = ISAPI_UNDEF;
89 dir->log_to_errlog = ISAPI_UNDEF;
90 dir->log_to_query = ISAPI_UNDEF;
91 dir->fake_async = ISAPI_UNDEF;
92
93 return dir;
94}
95
96static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_)
97{
101
102 dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF)
103 ? base->read_ahead_buflen
104 : add->read_ahead_buflen;
105 dir->log_unsupported = (add->log_unsupported == ISAPI_UNDEF)
106 ? base->log_unsupported
107 : add->log_unsupported;
108 dir->log_to_errlog = (add->log_to_errlog == ISAPI_UNDEF)
109 ? base->log_to_errlog
110 : add->log_to_errlog;
111 dir->log_to_query = (add->log_to_query == ISAPI_UNDEF)
112 ? base->log_to_query
113 : add->log_to_query;
114 dir->fake_async = (add->fake_async == ISAPI_UNDEF)
115 ? base->fake_async
116 : add->fake_async;
117
118 return dir;
119}
120
121static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
122 const char *filename)
123{
124 isapi_loaded *isa;
125 apr_finfo_t tmp;
126 apr_status_t rv;
127 char *fspec;
128
129 /* ### Just an observation ... it would be terribly cool to be
130 * able to use this per-dir, relative to the directory block being
131 * defined. The hash result remains global, but shorthand of
132 * <Directory "c:/webapps/isapi">
133 * ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll
134 * </Directory>
135 * would be very convienent.
136 */
138 if (!fspec) {
140 "invalid module path, skipping %s", filename);
141 return NULL;
142 }
143 if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE,
144 cmd->temp_pool)) != APR_SUCCESS) {
145 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, APLOGNO(02104)
146 "unable to stat, skipping %s", fspec);
147 return NULL;
148 }
149 if (tmp.filetype != APR_REG) {
150 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(02105)
151 "not a regular file, skipping %s", fspec);
152 return NULL;
153 }
154
155 /* Load the extension as cached (with null request_rec) */
156 rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa);
157 if (rv != APR_SUCCESS) {
158 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server, APLOGNO(02106)
159 "unable to cache, skipping %s", fspec);
160 return NULL;
161 }
162
163 return NULL;
164}
165
166static const command_rec isapi_cmds[] = {
167 AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot,
168 (void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen),
169 OR_FILEINFO, "Maximum client request body to initially pass to the"
170 " ISAPI handler (default: 49152)"),
171 AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot,
172 (void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported),
173 OR_FILEINFO, "Log requests not supported by the ISAPI server"
174 " on or off (default: off)"),
175 AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot,
176 (void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog),
177 OR_FILEINFO, "Send all Append Log requests to the error log"
178 " on or off (default: off)"),
179 AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot,
180 (void *)APR_OFFSETOF(isapi_dir_conf, log_to_query),
181 OR_FILEINFO, "Append Log requests are concatenated to the query args"
182 " on or off (default: on)"),
183 AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot,
184 (void *)APR_OFFSETOF(isapi_dir_conf, fake_async),
185 OR_FILEINFO, "Fake Asynchronous support for isapi callbacks"
186 " on or off [Experimental] (default: off)"),
187 AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL,
188 RSRC_CONF, "Cache the specified ISAPI extension in-process"),
189 {NULL}
190};
191
192/**********************************************************
193 *
194 * ISAPI Module Cache handling section
195 *
196 **********************************************************/
197
198/* Our isapi global config values */
204
205/* Our loaded isapi module description structure */
219
221{
222 /* All done with the DLL... get rid of it...
223 *
224 * If optionally cached, and we weren't asked to force the unload,
225 * pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload,
226 * otherwise, leave it alone (it didn't choose to cooperate.)
227 */
228 if (!isa->handle) {
229 return APR_SUCCESS;
230 }
231 if (isa->TerminateExtension) {
232 if (force) {
234 }
235 else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) {
236 return APR_EGENERAL;
237 }
238 }
240 isa->handle = NULL;
241 return APR_SUCCESS;
242}
243
245{
247
248 /* We must force the module to unload, we are about
249 * to lose the isapi structure's allocation entirely.
250 */
251 return isapi_unload(isa, 1);
252}
253
255{
256 apr_status_t rv;
257
259
260 /* TODO: These aught to become overridable, so that we
261 * assure a given isapi can be fooled into behaving well.
262 *
263 * The tricky bit, they aren't really a per-dir sort of
264 * config, they will always be constant across every
265 * reference to the .dll no matter what context (vhost,
266 * location, etc) they apply to.
267 */
268 isa->report_version = 0x500; /* Revision 5.0 */
269 isa->timeout = 300 * 1000000; /* microsecs, not used */
270
271 rv = apr_dso_load(&isa->handle, isa->filename, p);
272 if (rv)
273 {
275 "failed to load %s", isa->filename);
276 isa->handle = NULL;
277 return rv;
278 }
279
280 rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle,
281 "GetExtensionVersion");
282 if (rv)
283 {
285 "missing GetExtensionVersion() in %s",
286 isa->filename);
288 isa->handle = NULL;
289 return rv;
290 }
291
292 rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle,
293 "HttpExtensionProc");
294 if (rv)
295 {
297 "missing HttpExtensionProc() in %s",
298 isa->filename);
300 isa->handle = NULL;
301 return rv;
302 }
303
304 /* TerminateExtension() is an optional interface */
305 rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle,
306 "TerminateExtension");
308
309 /* Run GetExtensionVersion() */
310 if (!(isa->GetExtensionVersion)(isa->isapi_version)) {
313 "failed call to GetExtensionVersion() in %s",
314 isa->filename);
316 isa->handle = NULL;
317 return rv;
318 }
319
322
323 return APR_SUCCESS;
324}
325
327 const char *fpath, isapi_loaded** isa)
328{
329 apr_status_t rv;
330 const char *key;
331
333 return rv;
334 }
335
337
338 if (*isa) {
339
340 /* If we find this lock exists, use a set-aside copy of gainlock
341 * to avoid race conditions on NULLing the in_progress variable
342 * when the load has completed. Release the global isapi hash
343 * lock so other requests can proceed, then rdlock for completion
344 * of loading our desired dll or wrlock if we would like to retry
345 * loading the dll (because last_load_rv failed and retry is up.)
346 */
347 apr_thread_rwlock_t *gainlock = (*isa)->in_progress;
348
349 /* gainlock is NULLed after the module loads successfully.
350 * This free-threaded module can be used without any locking.
351 */
352 if (!gainlock) {
353 rv = (*isa)->last_load_rv;
355 return rv;
356 }
357
358
359 if ((*isa)->last_load_rv == APR_SUCCESS) {
362 != APR_SUCCESS) {
363 return rv;
364 }
365 rv = (*isa)->last_load_rv;
367 return rv;
368 }
369
370 if (apr_time_now() > (*isa)->last_load_time + ISAPI_RETRY) {
371
372 /* Remember last_load_time before releasing the global
373 * hash lock to avoid colliding with another thread
374 * that hit this exception at the same time as our
375 * retry attempt, since we unlock the global mutex
376 * before attempting a write lock for this module.
377 */
378 apr_time_t check_time = (*isa)->last_load_time;
380
382 != APR_SUCCESS) {
383 return rv;
384 }
385
386 /* If last_load_time is unchanged, we still own this
387 * retry, otherwise presume another thread provided
388 * our retry (for good or ill). Relock the global
389 * hash for updating last_load_ vars, so their update
390 * is always atomic to the global lock.
391 */
392 if (check_time == (*isa)->last_load_time) {
393
394 rv = isapi_load(loaded.pool, s, *isa);
395
397 (*isa)->last_load_rv = rv;
398 (*isa)->last_load_time = apr_time_now();
400 }
401 else {
402 rv = (*isa)->last_load_rv;
403 }
405
406 return rv;
407 }
408
409 /* We haven't hit timeup on retry, let's grab the last_rv
410 * within the hash mutex before unlocking.
411 */
412 rv = (*isa)->last_load_rv;
414
415 return rv;
416 }
417
418 /* If the module was not found, it's time to create a hash key entry
419 * before releasing the hash lock to avoid multiple threads from
420 * loading the same module.
421 */
423 *isa = apr_pcalloc(loaded.pool, sizeof(isapi_loaded));
424 (*isa)->filename = key;
425 if (r) {
426 /* A mutex that exists only long enough to attempt to
427 * load this isapi dll, the release this module to all
428 * other takers that came along during the one-time
429 * load process. Short lifetime for this lock would
430 * be great, however, using r->pool is nasty if those
431 * blocked on the lock haven't all unlocked before we
432 * attempt to destroy. A nastier race condition than
433 * I want to deal with at this moment...
434 */
435 apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
436 apr_thread_rwlock_wrlock((*isa)->in_progress);
437 }
438
440
441 /* Now attempt to load the isapi on our own time,
442 * allow other isapi processing to resume.
443 */
445
446 rv = isapi_load(loaded.pool, s, *isa);
447 (*isa)->last_load_time = apr_time_now();
448 (*isa)->last_load_rv = rv;
449
450 if (r && (rv == APR_SUCCESS)) {
451 /* Let others who are blocked on this particular
452 * module resume their requests, for better or worse.
453 */
454 apr_thread_rwlock_t *unlock = (*isa)->in_progress;
455 (*isa)->in_progress = NULL;
457 }
458 else if (!r && (rv != APR_SUCCESS)) {
459 /* We must leave a rwlock around for requests to retry
460 * loading this dll after timeup... since we were in
461 * the setup code we had avoided creating this lock.
462 */
463 apr_thread_rwlock_create(&(*isa)->in_progress, loaded.pool);
464 }
465
466 return (*isa)->last_load_rv;
467}
468
469/**********************************************************
470 *
471 * ISAPI Module request callbacks section
472 *
473 **********************************************************/
474
475/* Our "Connection ID" structure */
487
489 char *variable_name,
490 void *buf_ptr,
492{
493 request_rec *r = cid->r;
494 const char *result;
495 char *buf_data = (char*)buf_ptr;
497
498 if (!strcmp(variable_name, "ALL_HTTP"))
499 {
500 /* crlf delimited, colon split, comma separated and
501 * null terminated list of HTTP_ vars
502 */
504 const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
505 int i;
506
507 for (len = 0, i = 0; i < arr->nelts; i++) {
508 if (!strncmp(elts[i].key, "HTTP_", 5)) {
509 len += strlen(elts[i].key) + strlen(elts[i].val) + 3;
510 }
511 }
512
513 if (*buf_size < len + 1) {
514 *buf_size = len + 1;
516 return 0;
517 }
518
519 for (i = 0; i < arr->nelts; i++) {
520 if (!strncmp(elts[i].key, "HTTP_", 5)) {
521 strcpy(buf_data, elts[i].key);
522 buf_data += strlen(elts[i].key);
523 *(buf_data++) = ':';
524 strcpy(buf_data, elts[i].val);
525 buf_data += strlen(elts[i].val);
526 *(buf_data++) = '\r';
527 *(buf_data++) = '\n';
528 }
529 }
530
531 *(buf_data++) = '\0';
532 *buf_size = len + 1;
533 return 1;
534 }
535
536 if (!strcmp(variable_name, "ALL_RAW"))
537 {
538 /* crlf delimited, colon split, comma separated and
539 * null terminated list of the raw request header
540 */
542 const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
543 int i;
544
545 for (len = 0, i = 0; i < arr->nelts; i++) {
546 len += strlen(elts[i].key) + strlen(elts[i].val) + 4;
547 }
548
549 if (*buf_size < len + 1) {
550 *buf_size = len + 1;
552 return 0;
553 }
554
555 for (i = 0; i < arr->nelts; i++) {
556 strcpy(buf_data, elts[i].key);
557 buf_data += strlen(elts[i].key);
558 *(buf_data++) = ':';
559 *(buf_data++) = ' ';
560 strcpy(buf_data, elts[i].val);
561 buf_data += strlen(elts[i].val);
562 *(buf_data++) = '\r';
563 *(buf_data++) = '\n';
564 }
565 *(buf_data++) = '\0';
566 *buf_size = len + 1;
567 return 1;
568 }
569
570 /* Not a special case */
572
573 if (result) {
574 len = strlen(result);
575 if (*buf_size < len + 1) {
576 *buf_size = len + 1;
578 return 0;
579 }
581 *buf_size = len + 1;
582 return 1;
583 }
584
585 /* Not Found */
587 return 0;
588}
589
591 void *buf_data,
593{
594 request_rec *r = cid->r;
595 apr_uint32_t read = 0;
596 int res = 0;
597
598 if (r->remaining < *buf_size) {
600 }
601
602 while (read < *buf_size &&
603 ((res = ap_get_client_block(r, (char*)buf_data + read,
604 *buf_size - read)) > 0)) {
605 read += res;
606 }
607
608 *buf_size = read;
609 if (res < 0) {
611 }
612 return (res >= 0);
613}
614
615/* Common code invoked for both HSE_REQ_SEND_RESPONSE_HEADER and
616 * the newer HSE_REQ_SEND_RESPONSE_HEADER_EX ServerSupportFunction(s)
617 * as well as other functions that write responses and presume that
618 * the support functions above are optional.
619 *
620 * Other callers trying to split headers and body bytes should pass
621 * head/headlen alone (leaving stat/statlen NULL/0), so that they
622 * get a proper count of bytes consumed. The argument passed to stat
623 * isn't counted as the head bytes are.
624 */
626 const char *stat,
627 const char *head,
630{
631 int head_present = 1;
632 int termarg;
633 int res;
634 int old_status;
635 const char *termch;
636 apr_size_t ate = 0;
637
638 if (!head || headlen == 0 || !*head) {
639 head = stat;
640 stat = NULL;
642 statlen = 0;
643 head_present = 0; /* Don't eat the header */
644 }
645
646 if (!stat || statlen == 0 || !*stat) {
647 if (head && headlen && *head && ((stat = memchr(head, '\r', headlen))
648 || (stat = memchr(head, '\n', headlen))
649 || (stat = memchr(head, '\0', headlen))
650 || (stat = head + headlen))) {
651 statlen = stat - head;
652 if (memchr(head, ':', statlen)) {
653 stat = "Status: 200 OK";
654 statlen = strlen(stat);
655 }
656 else {
657 const char *flip = head;
658 head = stat;
659 stat = flip;
660 headlen -= statlen;
661 ate += statlen;
662 if (*head == '\r' && headlen)
663 ++head, --headlen, ++ate;
664 if (*head == '\n' && headlen)
665 ++head, --headlen, ++ate;
666 }
667 }
668 }
669
670 if (stat && (statlen > 0) && *stat) {
671 char *newstat;
672 if (!apr_isdigit(*stat)) {
673 const char *stattok = stat;
674 int toklen = statlen;
675 while (toklen && *stattok && !apr_isspace(*stattok)) {
676 ++stattok; --toklen;
677 }
678 while (toklen && apr_isspace(*stattok)) {
679 ++stattok; --toklen;
680 }
681 /* Now decide if we follow the xxx message
682 * or the http/x.x xxx message format
683 */
684 if (toklen && apr_isdigit(*stattok)) {
685 statlen = toklen;
686 stat = stattok;
687 }
688 }
689 newstat = apr_palloc(cid->r->pool, statlen + 9);
690 strcpy(newstat, "Status: ");
691 apr_cpystrn(newstat + 8, stat, statlen + 1);
692 stat = newstat;
693 statlen += 8;
694 }
695
696 if (!head || headlen == 0 || !*head) {
697 head = "\r\n";
698 headlen = 2;
699 }
700 else
701 {
702 if (head[headlen - 1] && head[headlen]) {
703 /* Whoops... not NULL terminated */
704 head = apr_pstrndup(cid->r->pool, head, headlen);
705 }
706 }
707
708 /* Seems IIS does not enforce the requirement for \r\n termination
709 * on HSE_REQ_SEND_RESPONSE_HEADER, but we won't panic...
710 * ap_scan_script_header_err_strs handles this aspect for us.
711 *
712 * Parse them out, or die trying
713 */
714 old_status = cid->r->status;
715
716 if (stat) {
719 }
720 else {
723 }
724
725 /* Set our status. */
726 if (res) {
727 /* This is an immediate error result from the parser
728 */
729 cid->r->status = res;
730 cid->r->status_line = ap_get_status_line(cid->r->status);
731 cid->ecb->dwHttpStatusCode = cid->r->status;
732 }
733 else if (cid->r->status) {
734 /* We have a status in r->status, so let's just use it.
735 * This is likely to be the Status: parsed above, and
736 * may also be a delayed error result from the parser.
737 * If it was filled in, status_line should also have
738 * been filled in.
739 */
740 cid->ecb->dwHttpStatusCode = cid->r->status;
741 }
742 else if (cid->ecb->dwHttpStatusCode
743 && cid->ecb->dwHttpStatusCode != HTTP_OK) {
744 /* Now we fall back on dwHttpStatusCode if it appears
745 * ap_scan_script_header fell back on the default code.
746 * Any other results set dwHttpStatusCode to the decoded
747 * status value.
748 */
749 cid->r->status = cid->ecb->dwHttpStatusCode;
750 cid->r->status_line = ap_get_status_line(cid->r->status);
751 }
752 else if (old_status) {
753 /* Well... either there is no dwHttpStatusCode or it's HTTP_OK.
754 * In any case, we don't have a good status to return yet...
755 * Perhaps the one we came in with will be better. Let's use it,
756 * if we were given one (note this is a pedantic case, it would
757 * normally be covered above unless the scan script code unset
758 * the r->status). Should there be a check here as to whether
759 * we are setting a valid response code?
760 */
761 cid->r->status = old_status;
762 cid->r->status_line = ap_get_status_line(cid->r->status);
763 cid->ecb->dwHttpStatusCode = cid->r->status;
764 }
765 else {
766 /* None of dwHttpStatusCode, the parser's r->status nor the
767 * old value of r->status were helpful, and nothing was decoded
768 * from Status: string passed to us. Let's just say HTTP_OK
769 * and get the data out, this was the isapi dev's oversight.
770 */
771 cid->r->status = HTTP_OK;
772 cid->r->status_line = ap_get_status_line(cid->r->status);
773 cid->ecb->dwHttpStatusCode = cid->r->status;
775 "Could not determine HTTP response code; using %d",
776 cid->r->status);
777 }
778
779 if (cid->r->status == HTTP_INTERNAL_SERVER_ERROR) {
780 return -1;
781 }
782
783 /* If only Status was passed, we consumed nothing
784 */
785 if (!head_present)
786 return 0;
787
788 cid->headers_set = 1;
789
790 /* If all went well, tell the caller we consumed the headers complete
791 */
792 if (!termch)
793 return(ate + headlen);
794
795 /* Any data left must be sent directly by the caller, all we
796 * give back is the size of the headers we consumed (which only
797 * happens if the parser got to the head arg, which varies based
798 * on whether we passed stat+head to scan, or only head.
799 */
800 if (termch && (termarg == (stat ? 1 : 0))
801 && head_present && head + headlen > termch) {
802 return ate + termch - head;
803 }
804 return ate;
805}
806
808 void *buf_ptr,
811{
812 request_rec *r = cid->r;
815 char *buf_data = (char*)buf_ptr;
817 apr_bucket *b;
819
820 if (!cid->headers_set) {
821 /* It appears that the foxisapi module and other clients
822 * presume that WriteClient("headers\n\nbody") will work.
823 * Parse them out, or die trying.
824 */
827 if (ate < 0) {
829 return 0;
830 }
831
832 buf_data += ate;
833 buf_size -= ate;
834 }
835
836 if (buf_size) {
837 bb = apr_brigade_create(r->pool, c->bucket_alloc);
840 b = apr_bucket_flush_create(c->bucket_alloc);
843 cid->response_sent = 1;
844 if (rv != APR_SUCCESS)
846 "WriteClient ap_pass_brigade failed: %s",
847 r->filename);
848 }
849
850 if ((flags & HSE_IO_ASYNC) && cid->completion) {
851 if (rv == APR_SUCCESS) {
852 cid->completion(cid->ecb, cid->completion_arg,
854 }
855 else {
856 cid->completion(cid->ecb, cid->completion_arg,
858 }
859 }
860 return (rv == APR_SUCCESS);
861}
862
865 void *buf_ptr,
868{
869 request_rec *r = cid->r;
871 char *buf_data = (char*)buf_ptr;
872 request_rec *subreq;
873 apr_status_t rv;
874
875 switch (HSE_code) {
877 /* Set the status to be returned when the HttpExtensionProc()
878 * is done.
879 * WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP
880 * and HSE_REQ_SEND_URL as equivalent per the Jan 2000 SDK.
881 * They most definitely are not, even in their own samples.
882 */
883 apr_table_set (r->headers_out, "Location", buf_data);
884 cid->r->status = cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY;
885 cid->r->status_line = ap_get_status_line(cid->r->status);
886 cid->headers_set = 1;
887 return 1;
888
889 case HSE_REQ_SEND_URL:
890 /* Soak up remaining input */
891 if (r->remaining > 0) {
894 }
895
896 /* Reset the method to GET */
897 r->method = "GET";
899
900 /* Don't let anyone think there's still data */
901 apr_table_unset(r->headers_in, "Content-Length");
902
903 /* AV fault per PR3598 - redirected path is lost! */
904 buf_data = apr_pstrdup(r->pool, (char*)buf_data);
906 return 1;
907
909 {
910 /* Parse them out, or die trying */
911 apr_size_t statlen = 0, headlen = 0;
913 if (buf_data)
914 statlen = strlen((char*) buf_data);
915 if (data_type)
916 headlen = strlen((char*) data_type);
918 (char*) data_type,
920 if (ate < 0) {
922 return 0;
923 }
924 else if ((apr_size_t)ate < headlen) {
926 apr_bucket *b;
927 bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
929 headlen - ate, c->bucket_alloc);
931 b = apr_bucket_flush_create(c->bucket_alloc);
933 rv = ap_pass_brigade(cid->r->output_filters, bb);
934 cid->response_sent = 1;
935 if (rv != APR_SUCCESS)
937 "ServerSupportFunction "
938 "HSE_REQ_SEND_RESPONSE_HEADER "
939 "ap_pass_brigade failed: %s", r->filename);
940 return (rv == APR_SUCCESS);
941 }
942 /* Deliberately hold off sending 'just the headers' to begin to
943 * accumulate the body and speed up the overall response, or at
944 * least wait for the end the session.
945 */
946 return 1;
947 }
948
950 /* Signal to resume the thread completing this request,
951 * leave it to the pool cleanup to dispose of our mutex.
952 */
953 if (cid->completed) {
954 (void)apr_thread_mutex_unlock(cid->completed);
955 return 1;
956 }
957 else if (cid->dconf.log_unsupported) {
959 "ServerSupportFunction "
960 "HSE_REQ_DONE_WITH_SESSION is not supported: %s",
961 r->filename);
962 }
964 return 0;
965
967 {
968 /* Map a URL to a filename */
969 char *file = (char *)buf_data;
971 subreq = ap_sub_req_lookup_uri(
972 apr_pstrndup(cid->r->pool, file, *buf_size), r, NULL);
973
974 if (!subreq->filename) {
975 ap_destroy_sub_req(subreq);
976 return 0;
977 }
978
979 len = (apr_uint32_t)strlen(subreq->filename);
980
981 if ((subreq->finfo.filetype == APR_DIR)
982 && (!subreq->path_info)
983 && (subreq->filename[len - 1] != '/'))
984 file = apr_pstrcat(cid->r->pool, subreq->filename, "/", NULL);
985 else
986 file = apr_pstrcat(cid->r->pool, subreq->filename,
987 subreq->path_info, NULL);
988
989 ap_destroy_sub_req(subreq);
990
991#ifdef WIN32
992 /* We need to make this a real Windows path name */
994#endif
995
997
998 return 1;
999 }
1000
1002 if (cid->dconf.log_unsupported)
1004 "ServerSupportFunction HSE_REQ_GET_SSPI_INFO "
1005 "is not supported: %s", r->filename);
1007 return 0;
1008
1010 /* Log buf_data, of buf_size bytes, in the URI Query (cs-uri-query) field
1011 */
1012 apr_table_set(r->notes, "isapi-parameter", (char*) buf_data);
1013 if (cid->dconf.log_to_query) {
1014 if (r->args)
1015 r->args = apr_pstrcat(r->pool, r->args, (char*) buf_data, NULL);
1016 else
1017 r->args = apr_pstrdup(r->pool, (char*) buf_data);
1018 }
1019 if (cid->dconf.log_to_errlog)
1021 "%s: %s", cid->r->filename,
1022 (char*) buf_data);
1023 return 1;
1024
1026 /* Emulates a completion port... Record callback address and
1027 * user defined arg, we will call this after any async request
1028 * (e.g. transmitfile) as if the request executed async.
1029 * Per MS docs... HSE_REQ_IO_COMPLETION replaces any prior call
1030 * to HSE_REQ_IO_COMPLETION, and buf_data may be set to NULL.
1031 */
1032 if (cid->dconf.fake_async) {
1033 cid->completion = (PFN_HSE_IO_COMPLETION) buf_data;
1034 cid->completion_arg = (void *) data_type;
1035 return 1;
1036 }
1037 if (cid->dconf.log_unsupported)
1039 "ServerSupportFunction HSE_REQ_IO_COMPLETION "
1040 "is not supported: %s", r->filename);
1042 return 0;
1043
1045 {
1046 /* we do nothing with (tf->dwFlags & HSE_DISCONNECT_AFTER_SEND)
1047 */
1049 apr_uint32_t sent = 0;
1050 apr_ssize_t ate = 0;
1052 apr_bucket *b;
1053 apr_file_t *fd;
1055
1056 if (!cid->dconf.fake_async && (tf->dwFlags & HSE_IO_ASYNC)) {
1057 if (cid->dconf.log_unsupported)
1059 "ServerSupportFunction HSE_REQ_TRANSMIT_FILE "
1060 "as HSE_IO_ASYNC is not supported: %s", r->filename);
1062 return 0;
1063 }
1064
1065 /* Presume the handle was opened with the CORRECT semantics
1066 * for TransmitFile
1067 */
1068 if ((rv = apr_os_file_put(&fd, &tf->hFile,
1070 != APR_SUCCESS) {
1071 return 0;
1072 }
1073 if (tf->BytesToWrite) {
1074 fsize = tf->BytesToWrite;
1075 }
1076 else {
1080 return 0;
1081 }
1082 fsize = fi.size - tf->Offset;
1083 }
1084
1085 /* apr_dupfile_oshandle (&fd, tf->hFile, r->pool); */
1086 bb = apr_brigade_create(r->pool, c->bucket_alloc);
1087
1088 /* According to MS: if calling HSE_REQ_TRANSMIT_FILE with the
1089 * HSE_IO_SEND_HEADERS flag, then you can't otherwise call any
1090 * HSE_SEND_RESPONSE_HEADERS* fn, but if you don't use the flag,
1091 * you must have done so. They document that the pHead headers
1092 * option is valid only for HSE_IO_SEND_HEADERS - we are a bit
1093 * more flexible and assume with the flag, pHead are the
1094 * response headers, and without, pHead simply contains text
1095 * (handled after this case).
1096 */
1097 if ((tf->dwFlags & HSE_IO_SEND_HEADERS) && tf->pszStatusCode) {
1098 ate = send_response_header(cid, tf->pszStatusCode,
1099 (char*)tf->pHead,
1100 strlen(tf->pszStatusCode),
1101 tf->HeadLength);
1102 }
1103 else if (!cid->headers_set && tf->pHead && tf->HeadLength
1104 && *(char*)tf->pHead) {
1105 ate = send_response_header(cid, NULL, (char*)tf->pHead,
1106 0, tf->HeadLength);
1107 if (ate < 0)
1108 {
1111 return 0;
1112 }
1113 }
1114
1115 if (tf->pHead && (apr_size_t)ate < tf->HeadLength) {
1116 b = apr_bucket_transient_create((char*)tf->pHead + ate,
1117 tf->HeadLength - ate,
1118 c->bucket_alloc);
1120 sent = tf->HeadLength;
1121 }
1122
1124 apr_brigade_insert_file(bb, fd, tf->Offset, fsize, r->pool);
1125
1126 if (tf->pTail && tf->TailLength) {
1127 sent += tf->TailLength;
1128 b = apr_bucket_transient_create((char*)tf->pTail,
1129 tf->TailLength, c->bucket_alloc);
1131 }
1132
1133 b = apr_bucket_flush_create(c->bucket_alloc);
1135 rv = ap_pass_brigade(r->output_filters, bb);
1136 cid->response_sent = 1;
1137 if (rv != APR_SUCCESS)
1139 "ServerSupportFunction "
1140 "HSE_REQ_TRANSMIT_FILE "
1141 "ap_pass_brigade failed: %s", r->filename);
1142
1143 /* Use tf->pfnHseIO + tf->pContext, or if NULL, then use cid->fnIOComplete
1144 * pass pContect to the HseIO callback.
1145 */
1146 if (tf->dwFlags & HSE_IO_ASYNC) {
1147 if (tf->pfnHseIO) {
1148 if (rv == APR_SUCCESS) {
1149 tf->pfnHseIO(cid->ecb, tf->pContext,
1151 }
1152 else {
1153 tf->pfnHseIO(cid->ecb, tf->pContext,
1155 }
1156 }
1157 else if (cid->completion) {
1158 if (rv == APR_SUCCESS) {
1159 cid->completion(cid->ecb, cid->completion_arg,
1161 }
1162 else {
1163 cid->completion(cid->ecb, cid->completion_arg,
1165 }
1166 }
1167 }
1168 return (rv == APR_SUCCESS);
1169 }
1170
1172 if (cid->dconf.log_unsupported)
1174 "ServerSupportFunction "
1175 "HSE_REQ_REFRESH_ISAPI_ACL "
1176 "is not supported: %s", r->filename);
1178 return 0;
1179
1181 *((int *)buf_data) = (r->connection->keepalive == AP_CONN_KEEPALIVE);
1182 return 1;
1183
1185 {
1186 apr_uint32_t read = 0;
1187 int res = 0;
1188 if (!cid->dconf.fake_async) {
1189 if (cid->dconf.log_unsupported)
1191 "asynchronous I/O not supported: %s",
1192 r->filename);
1194 return 0;
1195 }
1196
1197 if (r->remaining < *buf_size) {
1199 }
1200
1201 while (read < *buf_size &&
1202 ((res = ap_get_client_block(r, (char*)buf_data + read,
1203 *buf_size - read)) > 0)) {
1204 read += res;
1205 }
1206
1207 if ((*data_type & HSE_IO_ASYNC) && cid->completion) {
1208 /* XXX: Many authors issue their next HSE_REQ_ASYNC_READ_CLIENT
1209 * within the completion logic. An example is MS's own PSDK
1210 * sample web/iis/extensions/io/ASyncRead. This potentially
1211 * leads to stack exhaustion. To refactor, the notification
1212 * logic needs to move to isapi_handler() - differentiating
1213 * the cid->completed event with a new flag to indicate
1214 * an async-notice versus the async request completed.
1215 */
1216 if (res >= 0) {
1217 cid->completion(cid->ecb, cid->completion_arg,
1218 read, ERROR_SUCCESS);
1219 }
1220 else {
1221 cid->completion(cid->ecb, cid->completion_arg,
1222 read, ERROR_READ_FAULT);
1223 }
1224 }
1225 return (res >= 0);
1226 }
1227
1228 case HSE_REQ_GET_IMPERSONATION_TOKEN: /* Added in ISAPI 4.0 */
1229 if (cid->dconf.log_unsupported)
1231 "ServerSupportFunction "
1232 "HSE_REQ_GET_IMPERSONATION_TOKEN "
1233 "is not supported: %s", r->filename);
1235 return 0;
1236
1238 {
1239 /* Map a URL to a filename */
1241 char* test_uri = apr_pstrndup(r->pool, (char *)buf_data, *buf_size);
1242
1244 info->cchMatchingURL = strlen(test_uri);
1245 info->cchMatchingPath = apr_cpystrn(info->lpszPath, subreq->filename,
1246 sizeof(info->lpszPath)) - info->lpszPath;
1247
1248 /* Mapping started with assuming both strings matched.
1249 * Now roll on the path_info as a mismatch and handle
1250 * terminating slashes for directory matches.
1251 */
1252 if (subreq->path_info && *subreq->path_info) {
1253 apr_cpystrn(info->lpszPath + info->cchMatchingPath,
1254 subreq->path_info,
1255 sizeof(info->lpszPath) - info->cchMatchingPath);
1256 info->cchMatchingURL -= strlen(subreq->path_info);
1257 if (subreq->finfo.filetype == APR_DIR
1258 && info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
1259 /* roll forward over path_info's first slash */
1260 ++info->cchMatchingPath;
1261 ++info->cchMatchingURL;
1262 }
1263 }
1264 else if (subreq->finfo.filetype == APR_DIR
1265 && info->cchMatchingPath < sizeof(info->lpszPath) - 1) {
1266 /* Add a trailing slash for directory */
1267 info->lpszPath[info->cchMatchingPath++] = '/';
1268 info->lpszPath[info->cchMatchingPath] = '\0';
1269 }
1270
1271 /* If the matched isn't a file, roll match back to the prior slash */
1272 if (subreq->finfo.filetype == APR_NOFILE) {
1273 while (info->cchMatchingPath && info->cchMatchingURL) {
1274 if (info->lpszPath[info->cchMatchingPath - 1] == '/')
1275 break;
1276 --info->cchMatchingPath;
1277 --info->cchMatchingURL;
1278 }
1279 }
1280
1281 /* Paths returned with back slashes */
1282 for (test_uri = info->lpszPath; *test_uri; ++test_uri)
1283 if (*test_uri == '/')
1284 *test_uri = '\\';
1285
1286 /* is a combination of:
1287 * HSE_URL_FLAGS_READ 0x001 Allow read
1288 * HSE_URL_FLAGS_WRITE 0x002 Allow write
1289 * HSE_URL_FLAGS_EXECUTE 0x004 Allow execute
1290 * HSE_URL_FLAGS_SSL 0x008 Require SSL
1291 * HSE_URL_FLAGS_DONT_CACHE 0x010 Don't cache (VRoot only)
1292 * HSE_URL_FLAGS_NEGO_CERT 0x020 Allow client SSL cert
1293 * HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert
1294 * HSE_URL_FLAGS_MAP_CERT 0x080 Map client SSL cert to account
1295 * HSE_URL_FLAGS_SSL128 0x100 Require 128-bit SSL cert
1296 * HSE_URL_FLAGS_SCRIPT 0x200 Allow script execution
1297 *
1298 * XxX: As everywhere, EXEC flags could use some work...
1299 * and this could go further with more flags, as desired.
1300 */
1301 info->dwFlags = (subreq->finfo.protection & APR_UREAD ? 0x001 : 0)
1302 | (subreq->finfo.protection & APR_UWRITE ? 0x002 : 0)
1303 | (subreq->finfo.protection & APR_UEXECUTE ? 0x204 : 0);
1304 return 1;
1305 }
1306
1308 if (cid->dconf.log_unsupported)
1310 "ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE"
1311 " is not supported: %s", r->filename);
1313 return 0;
1314
1315 case HSE_REQ_GET_CERT_INFO_EX: /* Added in ISAPI 4.0 */
1316 if (cid->dconf.log_unsupported)
1318 "ServerSupportFunction "
1319 "HSE_REQ_GET_CERT_INFO_EX "
1320 "is not supported: %s", r->filename);
1322 return 0;
1323
1324 case HSE_REQ_SEND_RESPONSE_HEADER_EX: /* Added in ISAPI 4.0 */
1325 {
1327
1328 /* Ignore shi->fKeepConn - we don't want the advise
1329 */
1331 shi->pszHeader,
1332 shi->cchStatus,
1333 shi->cchHeader);
1334 if (ate < 0) {
1336 return 0;
1337 }
1338 else if ((apr_size_t)ate < shi->cchHeader) {
1340 apr_bucket *b;
1341 bb = apr_brigade_create(cid->r->pool, c->bucket_alloc);
1342 b = apr_bucket_transient_create(shi->pszHeader + ate,
1343 shi->cchHeader - ate,
1344 c->bucket_alloc);
1346 b = apr_bucket_flush_create(c->bucket_alloc);
1348 rv = ap_pass_brigade(cid->r->output_filters, bb);
1349 cid->response_sent = 1;
1350 if (rv != APR_SUCCESS)
1352 "ServerSupportFunction "
1353 "HSE_REQ_SEND_RESPONSE_HEADER_EX "
1354 "ap_pass_brigade failed: %s", r->filename);
1355 return (rv == APR_SUCCESS);
1356 }
1357 /* Deliberately hold off sending 'just the headers' to begin to
1358 * accumulate the body and speed up the overall response, or at
1359 * least wait for the end the session.
1360 */
1361 return 1;
1362 }
1363
1364 case HSE_REQ_CLOSE_CONNECTION: /* Added after ISAPI 4.0 */
1365 if (cid->dconf.log_unsupported)
1367 "ServerSupportFunction "
1368 "HSE_REQ_CLOSE_CONNECTION "
1369 "is not supported: %s", r->filename);
1371 return 0;
1372
1373 case HSE_REQ_IS_CONNECTED: /* Added after ISAPI 4.0 */
1374 /* Returns True if client is connected c.f. MSKB Q188346
1375 * assuming the identical return mechanism as HSE_REQ_IS_KEEP_CONN
1376 */
1377 *((int *)buf_data) = (r->connection->aborted == 0);
1378 return 1;
1379
1380 case HSE_REQ_EXTENSION_TRIGGER: /* Added after ISAPI 4.0 */
1381 /* Undocumented - defined by the Microsoft Jan '00 Platform SDK
1382 */
1383 if (cid->dconf.log_unsupported)
1385 "ServerSupportFunction "
1386 "HSE_REQ_EXTENSION_TRIGGER "
1387 "is not supported: %s", r->filename);
1389 return 0;
1390
1391 default:
1392 if (cid->dconf.log_unsupported)
1394 "ServerSupportFunction (%d) not supported: "
1395 "%s", HSE_code, r->filename);
1397 return 0;
1398 }
1399}
1400
1401/**********************************************************
1402 *
1403 * ISAPI Module request invocation section
1404 *
1405 **********************************************************/
1406
1408{
1409 isapi_dir_conf *dconf;
1410 apr_table_t *e;
1411 apr_status_t rv;
1412 isapi_loaded *isa;
1413 isapi_cid *cid;
1414 const char *val;
1415 apr_uint32_t read;
1416 int res;
1417
1418 if (strcmp(r->handler, "isapi-isa")
1419 && strcmp(r->handler, "isapi-handler")) {
1420 /* Hang on to the isapi-isa for compatibility with older docs
1421 * (wtf did '-isa' mean in the first place?) but introduce
1422 * a newer and clearer "isapi-handler" name.
1423 */
1424 return DECLINED;
1425 }
1426 dconf = ap_get_module_config(r->per_dir_config, &isapi_module);
1427 e = r->subprocess_env;
1428
1429 /* Use similar restrictions as CGIs
1430 *
1431 * If this fails, it's pointless to load the isapi dll.
1432 */
1433 if (!(ap_allow_options(r) & OPT_EXECCGI)) {
1434 return HTTP_FORBIDDEN;
1435 }
1436 if (r->finfo.filetype == APR_NOFILE) {
1437 return HTTP_NOT_FOUND;
1438 }
1439 if (r->finfo.filetype != APR_REG) {
1440 return HTTP_FORBIDDEN;
1441 }
1443 r->path_info && *r->path_info) {
1444 /* default to accept */
1445 return HTTP_NOT_FOUND;
1446 }
1447
1448 if (isapi_lookup(r->pool, r->server, r, r->filename, &isa)
1449 != APR_SUCCESS) {
1451 }
1452 /* Set up variables */
1455 apr_table_setn(e, "UNMAPPED_REMOTE_USER", "REMOTE_USER");
1456 if ((val = apr_table_get(e, "HTTPS")) && (strcmp(val, "on") == 0))
1457 apr_table_setn(e, "SERVER_PORT_SECURE", "1");
1458 else
1459 apr_table_setn(e, "SERVER_PORT_SECURE", "0");
1460 apr_table_setn(e, "URL", r->uri);
1461
1462 /* Set up connection structure and ecb,
1463 * NULL or zero out most fields.
1464 */
1465 cid = apr_pcalloc(r->pool, sizeof(isapi_cid));
1466
1467 /* Fixup defaults for dconf */
1468 cid->dconf.read_ahead_buflen = (dconf->read_ahead_buflen == ISAPI_UNDEF)
1469 ? 49152 : dconf->read_ahead_buflen;
1470 cid->dconf.log_unsupported = (dconf->log_unsupported == ISAPI_UNDEF)
1471 ? 0 : dconf->log_unsupported;
1472 cid->dconf.log_to_errlog = (dconf->log_to_errlog == ISAPI_UNDEF)
1473 ? 0 : dconf->log_to_errlog;
1474 cid->dconf.log_to_query = (dconf->log_to_query == ISAPI_UNDEF)
1475 ? 1 : dconf->log_to_query;
1476 cid->dconf.fake_async = (dconf->fake_async == ISAPI_UNDEF)
1477 ? 0 : dconf->fake_async;
1478
1479 cid->ecb = apr_pcalloc(r->pool, sizeof(EXTENSION_CONTROL_BLOCK));
1480 cid->ecb->ConnID = cid;
1481 cid->isa = isa;
1482 cid->r = r;
1483 r->status = 0;
1484
1485 cid->ecb->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
1486 cid->ecb->dwVersion = isa->report_version;
1487 cid->ecb->dwHttpStatusCode = 0;
1488 strcpy(cid->ecb->lpszLogData, "");
1489 /* TODO: are copies really needed here?
1490 */
1491 cid->ecb->lpszMethod = (char*) r->method;
1492 cid->ecb->lpszQueryString = (char*) apr_table_get(e, "QUERY_STRING");
1493 cid->ecb->lpszPathInfo = (char*) apr_table_get(e, "PATH_INFO");
1494 cid->ecb->lpszPathTranslated = (char*) apr_table_get(e, "PATH_TRANSLATED");
1495 cid->ecb->lpszContentType = (char*) apr_table_get(e, "CONTENT_TYPE");
1496
1497 /* Set up the callbacks */
1498 cid->ecb->GetServerVariable = regfnGetServerVariable;
1499 cid->ecb->WriteClient = regfnWriteClient;
1500 cid->ecb->ReadClient = regfnReadClient;
1501 cid->ecb->ServerSupportFunction = regfnServerSupportFunction;
1502
1503 /* Set up client input */
1505 if (res) {
1506 return res;
1507 }
1508
1510 /* Time to start reading the appropriate amount of data,
1511 * and allow the administrator to tweak the number
1512 */
1513 if (r->remaining) {
1514 cid->ecb->cbTotalBytes = (apr_size_t)r->remaining;
1515 if (cid->ecb->cbTotalBytes > (apr_uint32_t)cid->dconf.read_ahead_buflen)
1516 cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
1517 else
1518 cid->ecb->cbAvailable = cid->ecb->cbTotalBytes;
1519 }
1520 else
1521 {
1522 cid->ecb->cbTotalBytes = 0xffffffff;
1523 cid->ecb->cbAvailable = cid->dconf.read_ahead_buflen;
1524 }
1525
1526 cid->ecb->lpbData = apr_pcalloc(r->pool, cid->ecb->cbAvailable + 1);
1527
1528 read = 0;
1529 while (read < cid->ecb->cbAvailable &&
1530 ((res = ap_get_client_block(r, (char*)cid->ecb->lpbData + read,
1531 cid->ecb->cbAvailable - read)) > 0)) {
1532 read += res;
1533 }
1534
1535 if (res < 0) {
1537 }
1538
1539 /* Although it's not to spec, IIS seems to null-terminate
1540 * its lpdData string. So we will too.
1541 */
1542 if (res == 0)
1543 cid->ecb->cbAvailable = cid->ecb->cbTotalBytes = read;
1544 else
1545 cid->ecb->cbAvailable = read;
1546 cid->ecb->lpbData[read] = '\0';
1547 }
1548 else {
1549 cid->ecb->cbTotalBytes = 0;
1550 cid->ecb->cbAvailable = 0;
1551 cid->ecb->lpbData = NULL;
1552 }
1553
1554 /* To emulate async behavior...
1555 *
1556 * We create a cid->completed mutex and lock on it so that the
1557 * app can believe is it running async.
1558 *
1559 * This request completes upon a notification through
1560 * ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION), which
1561 * unlocks this mutex. If the HttpExtensionProc() returns
1562 * HSE_STATUS_PENDING, we will attempt to gain this lock again
1563 * which may *only* happen once HSE_REQ_DONE_WITH_SESSION has
1564 * unlocked the mutex.
1565 */
1566 if (cid->dconf.fake_async) {
1567 rv = apr_thread_mutex_create(&cid->completed,
1569 r->pool);
1570 if (cid->completed && (rv == APR_SUCCESS)) {
1571 rv = apr_thread_mutex_lock(cid->completed);
1572 }
1573
1574 if (!cid->completed || (rv != APR_SUCCESS)) {
1576 "Failed to create completion mutex");
1578 }
1579 }
1580
1581 /* All right... try and run the sucker */
1582 rv = (*isa->HttpExtensionProc)(cid->ecb);
1583
1584 /* Check for a log message - and log it */
1585 if (*cid->ecb->lpszLogData) {
1587 "%s: %s", r->filename, cid->ecb->lpszLogData);
1588 }
1589
1590 switch(rv) {
1591 case 0: /* Strange, but MS isapi accepts this as success */
1592 case HSE_STATUS_SUCCESS:
1594 /* Ignore the keepalive stuff; Apache handles it just fine without
1595 * the ISAPI Handler's "advice".
1596 * Per Microsoft: "In IIS versions 4.0 and later, the return
1597 * values HSE_STATUS_SUCCESS and HSE_STATUS_SUCCESS_AND_KEEP_CONN
1598 * are functionally identical: Keep-Alive connections are
1599 * maintained, if supported by the client."
1600 * ... so we were pat all this time
1601 */
1602 break;
1603
1604 case HSE_STATUS_PENDING:
1605 /* emulating async behavior...
1606 */
1607 if (cid->completed) {
1608 /* The completion port was locked prior to invoking
1609 * HttpExtensionProc(). Once we can regain the lock,
1610 * when ServerSupportFunction(HSE_REQ_DONE_WITH_SESSION)
1611 * is called by the extension to release the lock,
1612 * we may finally destroy the request.
1613 */
1614 (void)apr_thread_mutex_lock(cid->completed);
1615 break;
1616 }
1617 else if (cid->dconf.log_unsupported) {
1619 "asynch I/O result HSE_STATUS_PENDING "
1620 "from HttpExtensionProc() is not supported: %s",
1621 r->filename);
1623 }
1624 break;
1625
1626 case HSE_STATUS_ERROR:
1627 /* end response if we have yet to do so.
1628 */
1630 "HSE_STATUS_ERROR result from "
1631 "HttpExtensionProc(): %s", r->filename);
1633 break;
1634
1635 default:
1637 "unrecognized result code %d "
1638 "from HttpExtensionProc(): %s ",
1639 rv, r->filename);
1641 break;
1642 }
1643
1644 /* Flush the response now, including headers-only responses */
1645 if (cid->headers_set || cid->response_sent) {
1646 conn_rec *c = r->connection;
1648 apr_bucket *b;
1649 apr_status_t rv;
1650
1651 bb = apr_brigade_create(r->pool, c->bucket_alloc);
1652 b = apr_bucket_eos_create(c->bucket_alloc);
1654 rv = ap_pass_brigade(r->output_filters, bb);
1655 cid->response_sent = 1;
1656
1657 if (rv != APR_SUCCESS) {
1659 "ap_pass_brigade failed to "
1660 "complete the response: %s ", r->filename);
1661 }
1662
1663 return OK; /* NOT r->status, even if it has changed. */
1664 }
1665
1666 /* As the client returned no error, and if we did not error out
1667 * ourselves, trust dwHttpStatusCode to say something relevant.
1668 */
1669 if (!ap_is_HTTP_SERVER_ERROR(r->status) && cid->ecb->dwHttpStatusCode) {
1670 r->status = cid->ecb->dwHttpStatusCode;
1671 }
1672
1673 /* For all missing-response situations simply return the status,
1674 * and let the core respond to the client.
1675 */
1676 return r->status;
1677}
1678
1679/**********************************************************
1680 *
1681 * ISAPI Module Setup Hooks
1682 *
1683 **********************************************************/
1684
1686{
1687 apr_status_t rv;
1688
1690 if (!loaded.pool) {
1692 "could not create the isapi cache pool");
1693 return APR_EGENERAL;
1694 }
1695 apr_pool_tag(loaded.pool, "mod_isapi_load");
1696
1698 if (!loaded.hash) {
1700 "Failed to create module cache");
1701 return APR_EGENERAL;
1702 }
1703
1705 loaded.pool);
1706 if (rv != APR_SUCCESS) {
1708 "Failed to create module cache lock");
1709 return rv;
1710 }
1711 return OK;
1712}
1713
1719
1722 create_isapi_dir_config, /* create per-dir config */
1723 merge_isapi_dir_configs, /* merge per-dir config */
1724 NULL, /* server config */
1725 NULL, /* merge server config */
1726 isapi_cmds, /* command apr_table_t */
1727 isapi_hooks /* register hooks */
1728};
Symbol export macros and hook functions.
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL Buckets/Bucket Brigades.
APR Hash Tables.
APR general purpose library routines.
APR Portability Routines.
APR Strings library.
APR Thread Mutex Routines.
APR Reader/Writer Lock Routines.
static apr_pool_t * pconf
Definition event.c:441
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
const char * ap_set_int_slot(cmd_parms *cmd, void *struct_ptr, const char *arg)
Definition config.c:1480
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
void ap_hook_pre_config(ap_HOOK_pre_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:91
const char * ap_set_flag_slot(cmd_parms *cmd, void *struct_ptr, int arg)
Definition config.c:1512
request_rec * r
#define HUGE_STRING_LEN
Definition httpd.h:303
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
#define OPT_EXECCGI
Definition http_core.h:78
int ap_allow_options(request_rec *r)
Definition core.c:771
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_DEBUG
Definition http_log.h:71
#define APLOG_MODULE_INDEX
Definition http_log.h:168
int ap_should_client_block(request_rec *r)
long ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz)
const char * ap_get_status_line(int status)
int ap_setup_client_block(request_rec *r, int read_policy)
request_rec * ap_sub_req_lookup_uri(const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2297
void ap_destroy_sub_req(request_rec *r)
Definition request.c:2547
void ap_internal_redirect(const char *new_uri, request_rec *r)
void ap_add_common_vars(request_rec *r)
void ap_add_cgi_vars(request_rec *r)
int ap_scan_script_header_err_strs_ex(request_rec *r, char *buffer, int module_index, const char **termch, int *termarg,...)
void * dummy
Definition http_vhost.h:62
#define APR_EBADPATH
Definition apr_errno.h:332
#define APR_EGENERAL
Definition apr_errno.h:313
#define APR_BRIGADE_INSERT_TAIL(b, e)
apr_bucket * e
apr_file_t * fd
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
const char apr_ssize_t int flags
Definition apr_encode.h:168
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
#define RSRC_CONF
#define OR_FILEINFO
#define HTTP_OK
Definition httpd.h:490
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_MOVED_TEMPORARILY
Definition httpd.h:502
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_NOT_FOUND
Definition httpd.h:512
#define ap_is_HTTP_SERVER_ERROR(x)
Definition httpd.h:558
#define HSE_REQ_ABORTIVE_CLOSE
Definition mod_isapi.h:198
#define ERROR_INVALID_PARAMETER
Definition mod_isapi.h:231
#define HSE_APPEND_LOG_PARAMETER
Definition mod_isapi.h:189
#define HSE_REQ_ASYNC_READ_CLIENT
Definition mod_isapi.h:194
#define HSE_STATUS_PENDING
Definition mod_isapi.h:219
#define ERROR_WRITE_FAULT
Definition mod_isapi.h:237
#define HSE_REQ_SEND_URL
Definition mod_isapi.h:182
#define HSE_REQ_MAP_URL_TO_PATH_EX
Definition mod_isapi.h:197
#define HSE_IO_SEND_HEADERS
Definition mod_isapi.h:121
#define ERROR_INVALID_INDEX
Definition mod_isapi.h:228
#define HSE_REQ_GET_IMPERSONATION_TOKEN
Definition mod_isapi.h:196
#define HSE_REQ_CLOSE_CONNECTION
Definition mod_isapi.h:202
#define HSE_TERM_MUST_UNLOAD
Definition mod_isapi.h:245
#define HSE_REQ_SEND_RESPONSE_HEADER_EX
Definition mod_isapi.h:201
void(APR_THREAD_FUNC * PFN_HSE_IO_COMPLETION)(EXTENSION_CONTROL_BLOCK *ecb, void *ctxt, apr_uint32_t cbIO, apr_uint32_t dwError)
Definition mod_isapi.h:143
#define HSE_TERM_ADVISORY_UNLOAD
Definition mod_isapi.h:246
#define HSE_REQ_SEND_URL_REDIRECT_RESP
Definition mod_isapi.h:181
#define HSE_STATUS_SUCCESS
Definition mod_isapi.h:217
int(APR_THREAD_FUNC * PFN_TERMINATEEXTENSION)(apr_uint32_t flags)
Definition mod_isapi.h:256
#define HSE_IO_ASYNC
Definition mod_isapi.h:134
#define ERROR_INSUFFICIENT_BUFFER
Definition mod_isapi.h:225
#define HSE_REQ_GET_CERT_INFO_EX
Definition mod_isapi.h:200
#define HSE_REQ_SEND_RESPONSE_HEADER
Definition mod_isapi.h:183
#define ERROR_READ_FAULT
Definition mod_isapi.h:234
#define HSE_REQ_TRANSMIT_FILE
Definition mod_isapi.h:191
#define HSE_REQ_DONE_WITH_SESSION
Definition mod_isapi.h:184
#define HSE_REQ_GET_SSPI_INFO
Definition mod_isapi.h:188
#define HSE_REQ_IS_CONNECTED
Definition mod_isapi.h:203
#define HSE_STATUS_ERROR
Definition mod_isapi.h:220
#define HSE_REQ_IS_KEEP_CONN
Definition mod_isapi.h:193
#define HSE_STATUS_SUCCESS_AND_KEEP_CONN
Definition mod_isapi.h:218
#define HSE_REQ_EXTENSION_TRIGGER
Definition mod_isapi.h:204
#define HSE_REQ_IO_COMPLETION
Definition mod_isapi.h:190
#define HSE_REQ_MAP_URL_TO_PATH
Definition mod_isapi.h:187
int(APR_THREAD_FUNC * PFN_GETEXTENSIONVERSION)(HSE_VERSION_INFO *ver_info)
Definition mod_isapi.h:44
apr_uint32_t(APR_THREAD_FUNC * PFN_HTTPEXTENSIONPROC)(EXTENSION_CONTROL_BLOCK *ecb)
Definition mod_isapi.h:210
#define ERROR_SUCCESS
Definition mod_isapi.h:240
#define HSE_REQ_REFRESH_ISAPI_ACL
Definition mod_isapi.h:192
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
@ AP_CONN_KEEPALIVE
Definition httpd.h:1146
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isdigit(c)
Definition apr_lib.h:209
#define APR_FROM_OS_ERROR(e)
Definition apr_errno.h:1214
#define apr_set_os_error(e)
Definition apr_errno.h:1218
#define APR_SUCCESS
Definition apr_errno.h:225
#define apr_get_os_error()
Definition apr_errno.h:1217
int apr_status_t
Definition apr_errno.h:44
@ APR_REG
@ APR_DIR
@ APR_NOFILE
const char * key
const char apr_file_t * file
#define APR_READ
Definition apr_file_io.h:93
#define APR_XTHREAD
#define APR_UEXECUTE
#define APR_UWRITE
#define APR_UREAD
#define APR_FINFO_TYPE
#define APR_FINFO_SIZE
#define APR_FILEPATH_NATIVE
apr_array_header_t ** result
void * memchr(const void *s, int c, size_t n)
apr_pool_t * cont
Definition apr_getopt.h:103
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_dir_t * dir
apr_size_t const char * filename
Definition apr_shm.h:72
const char * s
Definition apr_strings.h:95
const apr_array_header_t * arr
Definition apr_tables.h:187
apr_cmdtype_e cmd
apr_int64_t apr_time_t
Definition apr_time.h:45
#define REQUEST_CHUNKED_ERROR
Definition httpd.h:748
#define AP_REQ_REJECT_PATH_INFO
Definition httpd.h:763
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
mod_core private header file
static const command_rec isapi_cmds[]
Definition mod_isapi.c:166
static void * create_isapi_dir_config(apr_pool_t *p, char *dummy)
Definition mod_isapi.c:83
static apr_status_t isapi_handler(request_rec *r)
Definition mod_isapi.c:1407
static struct isapi_global_conf loaded
static int APR_THREAD_FUNC regfnServerSupportFunction(isapi_cid *cid, apr_uint32_t HSE_code, void *buf_ptr, apr_uint32_t *buf_size, apr_uint32_t *data_type)
Definition mod_isapi.c:863
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r, const char *fpath, isapi_loaded **isa)
Definition mod_isapi.c:326
static int APR_THREAD_FUNC regfnGetServerVariable(isapi_cid *cid, char *variable_name, void *buf_ptr, apr_uint32_t *buf_size)
Definition mod_isapi.c:488
#define ISAPI_UNDEF
Definition mod_isapi.c:67
static int APR_THREAD_FUNC regfnWriteClient(isapi_cid *cid, void *buf_ptr, apr_uint32_t *size_arg, apr_uint32_t flags)
Definition mod_isapi.c:807
static void isapi_hooks(apr_pool_t *cont)
Definition mod_isapi.c:1714
static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa)
Definition mod_isapi.c:254
static apr_status_t cleanup_isapi(void *isa_)
Definition mod_isapi.c:244
#define ISAPI_RETRY
Definition mod_isapi.c:57
static apr_ssize_t send_response_header(isapi_cid *cid, const char *stat, const char *head, apr_size_t statlen, apr_size_t headlen)
Definition mod_isapi.c:625
static int APR_THREAD_FUNC regfnReadClient(isapi_cid *cid, void *buf_data, apr_uint32_t *buf_size)
Definition mod_isapi.c:590
static const char * isapi_cmd_cachefile(cmd_parms *cmd, void *dummy, const char *filename)
Definition mod_isapi.c:121
static apr_status_t isapi_unload(isapi_loaded *isa, int force)
Definition mod_isapi.c:220
static int isapi_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
Definition mod_isapi.c:1685
static void * merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_)
Definition mod_isapi.c:96
ISAPI module extension to Apache.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
apr_filetype_e filetype
apr_fileperms_t protection
Definition apr_tables.h:81
Structure to store things which are per connection.
Definition httpd.h:1152
ap_conn_keepalive_e keepalive
Definition httpd.h:1223
unsigned aborted
Definition httpd.h:1219
isapi_loaded * isa
Definition mod_isapi.c:479
apr_thread_mutex_t * completed
Definition mod_isapi.c:485
int headers_set
Definition mod_isapi.c:481
void * completion_arg
Definition mod_isapi.c:484
request_rec * r
Definition mod_isapi.c:480
PFN_HSE_IO_COMPLETION completion
Definition mod_isapi.c:483
isapi_dir_conf dconf
Definition mod_isapi.c:478
EXTENSION_CONTROL_BLOCK * ecb
Definition mod_isapi.c:477
int response_sent
Definition mod_isapi.c:482
int read_ahead_buflen
Definition mod_isapi.c:71
int log_unsupported
Definition mod_isapi.c:72
apr_pool_t * pool
Definition mod_isapi.c:200
apr_thread_mutex_t * lock
Definition mod_isapi.c:201
apr_hash_t * hash
Definition mod_isapi.c:202
apr_status_t last_load_rv
Definition mod_isapi.c:209
apr_dso_handle_t * handle
Definition mod_isapi.c:211
apr_uint32_t report_version
Definition mod_isapi.c:213
apr_time_t last_load_time
Definition mod_isapi.c:210
PFN_TERMINATEEXTENSION TerminateExtension
Definition mod_isapi.c:217
PFN_GETEXTENSIONVERSION GetExtensionVersion
Definition mod_isapi.c:215
apr_uint32_t timeout
Definition mod_isapi.c:214
apr_thread_rwlock_t * in_progress
Definition mod_isapi.c:208
HSE_VERSION_INFO * isapi_version
Definition mod_isapi.c:212
PFN_HTTPEXTENSIONPROC HttpExtensionProc
Definition mod_isapi.c:216
const char * filename
Definition mod_isapi.c:207
A structure that represents the current request.
Definition httpd.h:845
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
struct ap_filter_t * output_filters
Definition httpd.h:1070
const char * handler
Definition httpd.h:994
apr_table_t * notes
Definition httpd.h:985
int used_path_info
Definition httpd.h:1036
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_off_t remaining
Definition httpd.h:959
conn_rec * connection
Definition httpd.h:849
apr_finfo_t finfo
Definition httpd.h:1094
apr_table_t * headers_in
Definition httpd.h:976
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * method
Definition httpd.h:900
char * path_info
Definition httpd.h:1024
char * args
Definition httpd.h:1026
apr_table_t * headers_out
Definition httpd.h:978
A structure to store information for each virtual server.
Definition httpd.h:1322
Apache script tools.
INT info