⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mod_isapi.c

📁 Apache 2.0.63 is the current stable version of the 2.0 series, and is recommended over any previous
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 + -