📄 mod_isapi.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* mod_isapi.c - Internet Server Application (ISA) module for Apache
* by Alexei Kosut <akosut apache.org>, significant overhauls and
* redesign by William Rowe <wrowe covalent.net>, and hints from many
* other developer/users who have hit on specific flaws.
*
* This module implements the ISAPI Handler architecture, allowing
* Apache to load Internet Server Applications (ISAPI extensions),
* similar to the support in IIS, Zope, O'Reilly's WebSite and others.
*
* It is a complete implementation of the ISAPI 2.0 specification,
* except for "Microsoft extensions" to the API which provide
* asynchronous I/O. It is further extended to include additional
* "Microsoft extentions" through IIS 5.0, with some deficiencies
* where one-to-one mappings don't exist.
*
* Refer to /manual/mod/mod_isapi.html for additional details on
* configuration and use, but check this source for specific support
* of the API,
*/
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_log.h"
#include "util_script.h"
#include "mod_core.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_portable.h"
#include "apr_buckets.h"
#include "apr_thread_mutex.h"
#include "apr_thread_rwlock.h"
#include "apr_hash.h"
#include "mod_isapi.h"
/* Retry frequency for a failed-to-load isapi .dll */
#define ISAPI_RETRY apr_time_from_sec(30)
/**********************************************************
*
* ISAPI Module Configuration
*
**********************************************************/
module AP_MODULE_DECLARE_DATA isapi_module;
#define ISAPI_UNDEF -1
/* Our isapi per-dir config structure */
typedef struct isapi_dir_conf {
int read_ahead_buflen;
int log_unsupported;
int log_to_errlog;
int log_to_query;
int fake_async;
} isapi_dir_conf;
typedef struct isapi_loaded isapi_loaded;
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
const char *fpath, isapi_loaded** isa);
static void *create_isapi_dir_config(apr_pool_t *p, char *dummy)
{
isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
dir->read_ahead_buflen = ISAPI_UNDEF;
dir->log_unsupported = ISAPI_UNDEF;
dir->log_to_errlog = ISAPI_UNDEF;
dir->log_to_query = ISAPI_UNDEF;
dir->fake_async = ISAPI_UNDEF;
return dir;
}
static void *merge_isapi_dir_configs(apr_pool_t *p, void *base_, void *add_)
{
isapi_dir_conf *base = (isapi_dir_conf *) base_;
isapi_dir_conf *add = (isapi_dir_conf *) add_;
isapi_dir_conf *dir = apr_palloc(p, sizeof(isapi_dir_conf));
dir->read_ahead_buflen = (add->read_ahead_buflen == ISAPI_UNDEF)
? base->read_ahead_buflen
: add->read_ahead_buflen;
dir->log_unsupported = (add->log_unsupported == ISAPI_UNDEF)
? base->log_unsupported
: add->log_unsupported;
dir->log_to_errlog = (add->log_to_errlog == ISAPI_UNDEF)
? base->log_to_errlog
: add->log_to_errlog;
dir->log_to_query = (add->log_to_query == ISAPI_UNDEF)
? base->log_to_query
: add->log_to_query;
dir->fake_async = (add->fake_async == ISAPI_UNDEF)
? base->fake_async
: add->fake_async;
return dir;
}
static const char *isapi_cmd_cachefile(cmd_parms *cmd, void *dummy,
const char *filename)
{
isapi_loaded *isa;
apr_finfo_t tmp;
apr_status_t rv;
char *fspec;
/* ### Just an observation ... it would be terribly cool to be
* able to use this per-dir, relative to the directory block being
* defined. The hash result remains global, but shorthand of
* <Directory "c:/webapps/isapi">
* ISAPICacheFile myapp.dll anotherapp.dll thirdapp.dll
* </Directory>
* would be very convienent.
*/
fspec = ap_server_root_relative(cmd->pool, filename);
if (!fspec) {
ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EBADPATH, cmd->server,
"ISAPI: invalid module path, skipping %s", filename);
return NULL;
}
if ((rv = apr_stat(&tmp, fspec, APR_FINFO_TYPE,
cmd->temp_pool)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
"ISAPI: unable to stat, skipping %s", fspec);
return NULL;
}
if (tmp.filetype != APR_REG) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
"ISAPI: not a regular file, skipping %s", fspec);
return NULL;
}
/* Load the extention as cached (with null request_rec) */
rv = isapi_lookup(cmd->pool, cmd->server, NULL, fspec, &isa);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, cmd->server,
"ISAPI: unable to cache, skipping %s", fspec);
return NULL;
}
return NULL;
}
static const command_rec isapi_cmds[] = {
AP_INIT_TAKE1("ISAPIReadAheadBuffer", ap_set_int_slot,
(void *)APR_OFFSETOF(isapi_dir_conf, read_ahead_buflen),
OR_FILEINFO, "Maximum client request body to initially pass to the"
" ISAPI handler (default: 49152)"),
AP_INIT_FLAG("ISAPILogNotSupported", ap_set_flag_slot,
(void *)APR_OFFSETOF(isapi_dir_conf, log_unsupported),
OR_FILEINFO, "Log requests not supported by the ISAPI server"
" on or off (default: off)"),
AP_INIT_FLAG("ISAPIAppendLogToErrors", ap_set_flag_slot,
(void *)APR_OFFSETOF(isapi_dir_conf, log_to_errlog),
OR_FILEINFO, "Send all Append Log requests to the error log"
" on or off (default: off)"),
AP_INIT_FLAG("ISAPIAppendLogToQuery", ap_set_flag_slot,
(void *)APR_OFFSETOF(isapi_dir_conf, log_to_query),
OR_FILEINFO, "Append Log requests are concatinated to the query args"
" on or off (default: on)"),
AP_INIT_FLAG("ISAPIFakeAsync", ap_set_flag_slot,
(void *)APR_OFFSETOF(isapi_dir_conf, fake_async),
OR_FILEINFO, "Fake Asynchronous support for isapi callbacks"
" on or off [Experimental] (default: off)"),
AP_INIT_ITERATE("ISAPICacheFile", isapi_cmd_cachefile, NULL,
RSRC_CONF, "Cache the specified ISAPI extension in-process"),
{NULL}
};
/**********************************************************
*
* ISAPI Module Cache handling section
*
**********************************************************/
/* Our isapi global config values */
static struct isapi_global_conf {
apr_pool_t *pool;
apr_thread_mutex_t *lock;
apr_hash_t *hash;
} loaded;
/* Our loaded isapi module description structure */
struct isapi_loaded {
const char *filename;
apr_thread_rwlock_t *in_progress;
apr_status_t last_load_rv;
apr_time_t last_load_time;
apr_dso_handle_t *handle;
HSE_VERSION_INFO *isapi_version;
apr_uint32_t report_version;
apr_uint32_t timeout;
PFN_GETEXTENSIONVERSION GetExtensionVersion;
PFN_HTTPEXTENSIONPROC HttpExtensionProc;
PFN_TERMINATEEXTENSION TerminateExtension;
};
static apr_status_t isapi_unload(isapi_loaded *isa, int force)
{
/* All done with the DLL... get rid of it...
*
* If optionally cached, and we weren't asked to force the unload,
* pass HSE_TERM_ADVISORY_UNLOAD, and if it returns 1, unload,
* otherwise, leave it alone (it didn't choose to cooperate.)
*/
if (!isa->handle) {
return APR_SUCCESS;
}
if (isa->TerminateExtension) {
if (force) {
(*isa->TerminateExtension)(HSE_TERM_MUST_UNLOAD);
}
else if (!(*isa->TerminateExtension)(HSE_TERM_ADVISORY_UNLOAD)) {
return APR_EGENERAL;
}
}
apr_dso_unload(isa->handle);
isa->handle = NULL;
return APR_SUCCESS;
}
static apr_status_t cleanup_isapi(void *isa_)
{
isapi_loaded* isa = (isapi_loaded*) isa_;
/* We must force the module to unload, we are about
* to lose the isapi structure's allocation entirely.
*/
return isapi_unload(isa, 1);
}
static apr_status_t isapi_load(apr_pool_t *p, server_rec *s, isapi_loaded *isa)
{
apr_status_t rv;
isa->isapi_version = apr_pcalloc(p, sizeof(HSE_VERSION_INFO));
/* TODO: These aught to become overrideable, so that we
* assure a given isapi can be fooled into behaving well.
*
* The tricky bit, they aren't really a per-dir sort of
* config, they will always be constant across every
* reference to the .dll no matter what context (vhost,
* location, etc) they apply to.
*/
isa->report_version = 0x500; /* Revision 5.0 */
isa->timeout = 300 * 1000000; /* microsecs, not used */
rv = apr_dso_load(&isa->handle, isa->filename, p);
if (rv)
{
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"ISAPI: failed to load %s", isa->filename);
isa->handle = NULL;
return rv;
}
rv = apr_dso_sym((void**)&isa->GetExtensionVersion, isa->handle,
"GetExtensionVersion");
if (rv)
{
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"ISAPI: missing GetExtensionVersion() in %s",
isa->filename);
apr_dso_unload(isa->handle);
isa->handle = NULL;
return rv;
}
rv = apr_dso_sym((void**)&isa->HttpExtensionProc, isa->handle,
"HttpExtensionProc");
if (rv)
{
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"ISAPI: missing HttpExtensionProc() in %s",
isa->filename);
apr_dso_unload(isa->handle);
isa->handle = NULL;
return rv;
}
/* TerminateExtension() is an optional interface */
rv = apr_dso_sym((void**)&isa->TerminateExtension, isa->handle,
"TerminateExtension");
apr_set_os_error(0);
/* Run GetExtensionVersion() */
if (!(isa->GetExtensionVersion)(isa->isapi_version)) {
apr_status_t rv = apr_get_os_error();
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"ISAPI: failed call to GetExtensionVersion() in %s",
isa->filename);
apr_dso_unload(isa->handle);
isa->handle = NULL;
return rv;
}
apr_pool_cleanup_register(p, isa, cleanup_isapi,
apr_pool_cleanup_null);
return APR_SUCCESS;
}
apr_status_t isapi_lookup(apr_pool_t *p, server_rec *s, request_rec *r,
const char *fpath, isapi_loaded** isa)
{
apr_status_t rv;
const char *key;
if ((rv = apr_thread_mutex_lock(loaded.lock)) != APR_SUCCESS) {
return rv;
}
*isa = apr_hash_get(loaded.hash, fpath, APR_HASH_KEY_STRING);
if (*isa) {
/* If we find this lock exists, use a set-aside copy of gainlock
* to avoid race conditions on NULLing the in_progress variable
* when the load has completed. Release the global isapi hash
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -