| /* 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> |
| * |
| * This module implements Microsoft's ISAPI, allowing Apache (when running |
| * under Windows) to load Internet Server Applications (ISAPI extensions). |
| * It implements all of the ISAPI 2.0 specification, except for the |
| * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI |
| * extensions that use only synchronous I/O and are compatible with the |
| * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should |
| * function as well). |
| * |
| * To load, simply place the ISA in a location in the document tree. |
| * Then add an "AddHandler isapi-isa dll" into your config file. |
| * You should now be able to load ISAPI DLLs just be reffering to their |
| * URLs. Make sure the ExecCGI option is active in the directory |
| * the ISA is in. |
| */ |
| |
| #ifdef WIN32 |
| |
| /* A lousy hack to include ap_check_cmd_context(): */ |
| #define CORE_PRIVATE |
| |
| #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 <stdlib.h> |
| /* We use the exact same header file as the original */ |
| #include <HttpExt.h> |
| |
| /* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER, |
| define this to conform */ |
| #define RELAX_HEADER_RULE |
| |
| #if !defined(HSE_REQ_SEND_RESPONSE_HEADER_EX) \ |
| || !defined(HSE_REQ_MAP_URL_TO_PATH_EX) |
| #pragma message("WARNING: This build of Apache is missing the recent changes") |
| #pragma message("in the Microsoft Win32 Platform SDK; some mod_isapi features") |
| #pragma message("will be disabled. To obtain the latest Platform SDK files,") |
| #pragma message("please refer to:") |
| #pragma message("http://msdn.microsoft.com/downloads/sdks/platform/platform.asp") |
| #endif |
| |
| module isapi_module; |
| |
| static DWORD ReadAheadBuffer = 49152; |
| static int LogNotSupported = -1; |
| static int AppendLogToErrors = 0; |
| static int AppendLogToQuery = 0; |
| |
| /* Our "Connection ID" structure */ |
| |
| typedef struct { |
| LPEXTENSION_CONTROL_BLOCK ecb; |
| request_rec *r; |
| int status; |
| } isapi_cid; |
| |
| /* Declare the ISAPI functions */ |
| |
| BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, |
| LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer); |
| BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, |
| DWORD dwReserved); |
| BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize); |
| BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, |
| LPVOID lpvBuffer, LPDWORD lpdwSize, |
| LPDWORD lpdwDataType); |
| |
| /* |
| The optimiser blows it totally here. What happens is that autos are addressed relative to the |
| stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere |
| between setting isapi_entry and calling through it. We work around the problem by forcing it to |
| use frame pointers. |
| */ |
| #pragma optimize("y",off) |
| |
| int isapi_handler (request_rec *r) { |
| LPEXTENSION_CONTROL_BLOCK ecb = |
| ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK)); |
| HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO)); |
| |
| HINSTANCE isapi_handle; |
| BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */ |
| DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */ |
| BOOL (*isapi_term)(DWORD); /* optional entry point 3 */ |
| |
| isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid)); |
| table *e = r->subprocess_env; |
| DWORD read; |
| char *p; |
| int retval; |
| int res; |
| |
| /* Use similar restrictions as CGIs */ |
| |
| if (!(ap_allow_options(r) & OPT_EXECCGI)) |
| return FORBIDDEN; |
| |
| if (r->finfo.st_mode == 0) |
| return NOT_FOUND; |
| |
| if (S_ISDIR(r->finfo.st_mode)) |
| return FORBIDDEN; |
| |
| if (!(isapi_handle = ap_os_dso_load(r->filename))) { |
| ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, |
| "ISAPI Could not load DLL: %s", r->filename); |
| return SERVER_ERROR; |
| } |
| |
| if (!(isapi_version = |
| (void *)(ap_os_dso_sym(isapi_handle, "GetExtensionVersion")))) { |
| ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, |
| "DLL could not load GetExtensionVersion(): %s", |
| r->filename); |
| ap_os_dso_unload(isapi_handle); |
| return SERVER_ERROR; |
| } |
| |
| if (!(isapi_entry = |
| (void *)(ap_os_dso_sym(isapi_handle, "HttpExtensionProc")))) { |
| ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, |
| "DLL could not load HttpExtensionProc(): %s", |
| r->filename); |
| ap_os_dso_unload(isapi_handle); |
| return SERVER_ERROR; |
| } |
| |
| isapi_term = (void *)(ap_os_dso_sym(isapi_handle, "TerminateExtension")); |
| |
| /* Run GetExtensionVersion() */ |
| |
| if (!(*isapi_version)(pVer)) { |
| ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, |
| "ISAPI GetExtensionVersion() failed: %s", r->filename); |
| ap_os_dso_unload(isapi_handle); |
| return SERVER_ERROR; |
| } |
| |
| /* Set up variables. There are a couple of special cases for ISAPI. |
| * XXX: These were taken verbatim from GetServerVariable, and should |
| * be reviewed carefully. |
| */ |
| ap_add_common_vars(r); |
| ap_add_cgi_vars(r); |
| ap_table_setn(r->subprocess_env, "UNMAPPED_REMOTE_USER", "REMOTE_USER"); |
| ap_table_setn(r->subprocess_env, "SERVER_PORT_SECURE", "0"); |
| ap_table_setn(r->subprocess_env, "URL", r->uri); |
| |
| /* Set up connection ID */ |
| ecb->ConnID = (HCONN)cid; |
| cid->ecb = ecb; |
| cid->r = r; |
| cid->status = 0; |
| |
| ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK); |
| ecb->dwVersion = MAKELONG(0, 2); |
| ecb->dwHttpStatusCode = 0; |
| strcpy(ecb->lpszLogData, ""); |
| ecb->lpszMethod = ap_pstrdup(r->pool, r->method); |
| ecb->lpszQueryString = ap_pstrdup(r->pool, ap_table_get(e, "QUERY_STRING")); |
| ecb->lpszPathInfo = ap_pstrdup(r->pool, ap_table_get(e, "PATH_INFO")); |
| ecb->lpszPathTranslated = ap_pstrdup(r->pool, ap_table_get(e, "PATH_TRANSLATED")); |
| ecb->lpszContentType = ap_pstrdup(r->pool, ap_table_get(e, "CONTENT_TYPE")); |
| |
| /* Set up client input */ |
| if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { |
| if (isapi_term) (*isapi_term)( 2 /* HSE_TERM_MUST_UNLOAD */); |
| ap_os_dso_unload(isapi_handle); |
| return retval; |
| } |
| |
| if (ap_should_client_block(r)) { |
| /* Time to start reading the appropriate amount of data, |
| * and allow the administrator to tweak the number |
| * TODO: add the httpd.conf option for ReadAheadBuffer. |
| */ |
| if (r->remaining) { |
| ecb->cbTotalBytes = r->remaining; |
| if (ecb->cbTotalBytes > ReadAheadBuffer) |
| ecb->cbAvailable = ReadAheadBuffer; |
| else |
| ecb->cbAvailable = ecb->cbTotalBytes; |
| } |
| else |
| { |
| ecb->cbTotalBytes = 0xffffffff; |
| ecb->cbAvailable = ReadAheadBuffer; |
| } |
| |
| ecb->lpbData = ap_pcalloc(r->pool, ecb->cbAvailable + 1); |
| |
| p = ecb->lpbData; |
| read = 0; |
| while (read < ecb->cbAvailable && |
| ((res = ap_get_client_block(r, ecb->lpbData + read, |
| ecb->cbAvailable - read)) > 0)) { |
| read += res; |
| } |
| |
| if (res < 0) { |
| if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); |
| ap_os_dso_unload(isapi_handle); |
| return SERVER_ERROR; |
| } |
| |
| /* Although its not to spec, IIS seems to null-terminate |
| * its lpdData string. So we will too. |
| * |
| * XXX: This must be an issue... backing out the null |
| * from the count of bytes. |
| */ |
| if (res == 0) |
| ecb->cbAvailable = ecb->cbTotalBytes = read; |
| else |
| ecb->cbAvailable = read; |
| ecb->lpbData[read] = '\0'; |
| } |
| else { |
| ecb->cbTotalBytes = 0; |
| ecb->cbAvailable = 0; |
| ecb->lpbData = NULL; |
| } |
| |
| /* Set up the callbacks */ |
| |
| ecb->GetServerVariable = &GetServerVariable; |
| ecb->WriteClient = &WriteClient; |
| ecb->ReadClient = &ReadClient; |
| ecb->ServerSupportFunction = &ServerSupportFunction; |
| |
| /* All right... try and load the sucker */ |
| retval = (*isapi_entry)(ecb); |
| |
| /* Set the status (for logging) */ |
| if (ecb->dwHttpStatusCode) |
| r->status = ecb->dwHttpStatusCode; |
| |
| /* Check for a log message - and log it */ |
| if (ecb->lpszLogData && strcmp(ecb->lpszLogData, "")) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, |
| "ISAPI: %s: %s", ecb->lpszLogData, r->filename); |
| |
| /* Soak up any remaining input */ |
| if (r->remaining > 0) { |
| char argsbuffer[HUGE_STRING_LEN]; |
| while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0); |
| } |
| |
| /* All done with the DLL... get rid of it */ |
| if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); |
| ap_os_dso_unload(isapi_handle); |
| |
| switch(retval) { |
| case 0: /* Strange, but MS isapi accepts this as success */ |
| case HSE_STATUS_SUCCESS: |
| case HSE_STATUS_SUCCESS_AND_KEEP_CONN: |
| /* Ignore the keepalive stuff; Apache handles it just fine without |
| * the ISA's "advice". |
| */ |
| |
| if (cid->status) /* We have a special status to return */ |
| return cid->status; |
| |
| return OK; |
| case HSE_STATUS_PENDING: /* We don't support this */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI asynchronous I/O not supported: %s", |
| r->filename); |
| case HSE_STATUS_ERROR: |
| default: |
| return SERVER_ERROR; |
| } |
| |
| } |
| #pragma optimize("",on) |
| |
| BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, |
| LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) { |
| request_rec *r = ((isapi_cid *)hConn)->r; |
| const char *result; |
| DWORD len; |
| |
| if (!strcmp(lpszVariableName, "ALL_HTTP")) { |
| /* lf delimited, colon split, comma seperated and |
| * null terminated list of HTTP_ vars |
| */ |
| char **env = (char**) ap_table_elts(r->subprocess_env)->elts; |
| int nelts = 2 * ap_table_elts(r->subprocess_env)->nelts; |
| int i; |
| |
| for (len = 0, i = 0; i < nelts; i += 2) |
| if (!strncmp(env[i], "HTTP_", 5)) |
| len += strlen(env[i]) + strlen(env[i + 1]) + 2; |
| |
| if (*lpdwSizeofBuffer < len + 1) { |
| *lpdwSizeofBuffer = len + 1; |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| return FALSE; |
| } |
| |
| for (i = 0; i < nelts; i += 2) |
| if (!strncmp(env[i], "HTTP_", 5)) { |
| strcpy(lpvBuffer, env[i]); |
| ((char*)lpvBuffer) += strlen(env[i]); |
| *(((char*)lpvBuffer)++) = ':'; |
| strcpy(lpvBuffer, env[i + 1]); |
| ((char*)lpvBuffer) += strlen(env[i + 1]); |
| *(((char*)lpvBuffer)++) = '\n'; |
| } |
| *(((char*)lpvBuffer)++) = '\0'; |
| *lpdwSizeofBuffer = len; |
| return TRUE; |
| } |
| |
| if (!strcmp(lpszVariableName, "ALL_RAW")) { |
| /* lf delimited, colon split, comma seperated and |
| * null terminated list of the raw request header |
| */ |
| char **raw = (char**) ap_table_elts(r->headers_in)->elts; |
| int nelts = 2 * ap_table_elts(r->headers_in)->nelts; |
| int i; |
| |
| for (len = 0, i = 0; i < nelts; i += 2) |
| len += strlen(raw[i]) + strlen(raw[i + 1]) + 2; |
| |
| if (*lpdwSizeofBuffer < len + 1) { |
| *lpdwSizeofBuffer = len + 1; |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| return FALSE; |
| } |
| |
| for (i = 0; i < nelts; i += 2) { |
| strcpy(lpvBuffer, raw[i]); |
| ((char*)lpvBuffer) += strlen(raw[i]); |
| *(((char*)lpvBuffer)++) = ':'; |
| *(((char*)lpvBuffer)++) = ' '; |
| strcpy(lpvBuffer, raw[i + 1]); |
| ((char*)lpvBuffer) += strlen(raw[i + 1]); |
| *(((char*)lpvBuffer)++) = '\n'; |
| i += 2; |
| } |
| *(((char*)lpvBuffer)++) = '\0'; |
| *lpdwSizeofBuffer = len; |
| return TRUE; |
| } |
| |
| /* Not a special case */ |
| result = ap_table_get(r->subprocess_env, lpszVariableName); |
| if (result) { |
| len = strlen(result); |
| if (*lpdwSizeofBuffer < len + 1) { |
| *lpdwSizeofBuffer = len + 1; |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| return FALSE; |
| } |
| strcpy(lpvBuffer, result); |
| *lpdwSizeofBuffer = len; |
| return TRUE; |
| } |
| |
| /* Not Found */ |
| SetLastError(ERROR_INVALID_INDEX); |
| return FALSE; |
| } |
| |
| BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, |
| DWORD dwReserved) { |
| request_rec *r = ((isapi_cid *)ConnID)->r; |
| |
| /* We only support synchronous writing */ |
| if (dwReserved && dwReserved != HSE_IO_SYNC) { |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI asynchronous I/O not supported: %s", |
| r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| if ((*lpwdwBytes = ap_rwrite(Buffer, *lpwdwBytes, r)) <= 0) { |
| if (!GetLastError()) |
| SetLastError(ERROR); /* XXX: Find the right error code */ |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) { |
| request_rec *r = ((isapi_cid *)ConnID)->r; |
| DWORD read = 0; |
| int res; |
| |
| if (r->remaining < (long) *lpdwSize) |
| *lpdwSize = r->remaining; |
| |
| while (read < *lpdwSize && |
| ((res = ap_get_client_block(r, (char*)lpvBuffer + read, |
| *lpdwSize - read)) > 0)) { |
| if (res < 0) { |
| *lpdwSize = 0; |
| if (!GetLastError()) |
| SetLastError(ERROR); /* XXX: Find the right error code */ |
| return FALSE; |
| } |
| |
| read += res; |
| } |
| |
| *lpdwSize = read; |
| return TRUE; |
| } |
| |
| static BOOL SendResponseHeaderEx(isapi_cid *cid, const char *stat, |
| const char *head, DWORD statlen, |
| DWORD headlen) |
| { |
| int termarg; |
| char *termch; |
| |
| if (!stat || statlen == 0 || !*stat) { |
| stat = "Status: 200 OK"; |
| } |
| else { |
| char *newstat; |
| newstat = ap_palloc(cid->r->pool, statlen + 9); |
| strcpy(newstat, "Status: "); |
| ap_cpystrn(newstat + 8, stat, statlen + 1); |
| stat = newstat; |
| } |
| |
| if (!head || headlen == 0 || !*head) { |
| head = "\r\n"; |
| } |
| else |
| { |
| if (head[headlen]) { |
| /* Whoops... not NULL terminated */ |
| head = ap_pstrndup(cid->r->pool, head, headlen); |
| } |
| } |
| |
| /* Parse them out, or die trying */ |
| cid->status = ap_scan_script_header_err_strs(cid->r, NULL, &termch, |
| &termarg, stat, head, NULL); |
| cid->ecb->dwHttpStatusCode = cid->r->status; |
| |
| /* All the headers should be set now */ |
| ap_send_http_header(cid->r); |
| |
| /* Any data left should now be sent directly, |
| * it may be raw if headlen was provided. |
| */ |
| if (termch && (termarg == 1)) { |
| if (headlen == -1 && *termch) |
| ap_rputs(termch, cid->r); |
| else if (headlen > (size_t) (termch - head)) |
| ap_rwrite(termch, headlen - (termch - head), cid->r); |
| } |
| |
| if (cid->status == HTTP_INTERNAL_SERVER_ERROR) |
| return FALSE; |
| return TRUE; |
| } |
| |
| /* XXX: Is there is still an O(n^2) attack possible here? Please detail. */ |
| BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, |
| LPVOID lpvBuffer, LPDWORD lpdwSize, |
| LPDWORD lpdwDataType) { |
| isapi_cid *cid = (isapi_cid *)hConn; |
| request_rec *r = cid->r; |
| request_rec *subreq; |
| |
| switch (dwHSERequest) { |
| case 1: /* HSE_REQ_SEND_URL_REDIRECT_RESP */ |
| /* Set the status to be returned when the HttpExtensionProc() |
| * is done. |
| * WARNING: Microsoft now advertises HSE_REQ_SEND_URL_REDIRECT_RESP |
| * and HSE_REQ_SEND_URL as equivalant per the Jan 2000 SDK. |
| * They most definately are not, even in their own samples. |
| */ |
| ap_table_set(r->headers_out, "Location", lpvBuffer); |
| cid->status = cid->r->status |
| = cid->ecb->dwHttpStatusCode = HTTP_MOVED_TEMPORARILY; |
| return TRUE; |
| |
| case 2: /* HSE_REQ_SEND_URL */ |
| /* Soak up remaining input (there should be none) */ |
| if (r->remaining > 0) { |
| char argsbuffer[HUGE_STRING_LEN]; |
| while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0); |
| } |
| |
| /* Reset the method to GET */ |
| r->method = ap_pstrdup(r->pool, "GET"); |
| r->method_number = M_GET; |
| |
| /* Don't let anyone think there's still data */ |
| ap_table_unset(r->headers_in, "Content-Length"); |
| |
| /* AV fault per PR3598 - redirected path is lost! */ |
| (char*)lpvBuffer = ap_pstrdup(r->pool, (char*)lpvBuffer); |
| ap_internal_redirect((char*)lpvBuffer, r); |
| return TRUE; |
| |
| case 3: /* HSE_REQ_SEND_RESPONSE_HEADER */ |
| { |
| /* Parse them out, or die trying */ |
| DWORD statlen = 0, headlen = 0; |
| if (lpvBuffer) |
| statlen = strlen((char*) lpvBuffer); |
| if (lpdwDataType) |
| headlen = strlen((char*) lpdwDataType); |
| return SendResponseHeaderEx(cid, (char*) lpvBuffer, (char*) lpdwDataType, |
| statlen, headlen); |
| } |
| |
| case 4: /* HSE_REQ_DONE_WITH_SESSION */ |
| /* Do nothing... since we don't support async I/O, they'll |
| * return from HttpExtensionProc soon |
| */ |
| return TRUE; |
| |
| case 1001: /* HSE_REQ_MAP_URL_TO_PATH */ |
| { |
| /* Map a URL to a filename */ |
| char *file = (char *)lpvBuffer; |
| DWORD len; |
| subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, file, *lpdwSize), r); |
| |
| len = ap_cpystrn(file, subreq->filename, *lpdwSize) - file; |
| |
| /* IIS puts a trailing slash on directories, Apache doesn't */ |
| if (S_ISDIR (subreq->finfo.st_mode)) { |
| if (len < *lpdwSize - 1) { |
| file[len++] = '\\'; |
| file[len] = '\0'; |
| } |
| } |
| *lpdwSize = len; |
| return TRUE; |
| } |
| |
| case 1002: /* HSE_REQ_GET_SSPI_INFO */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction HSE_REQ_GET_SSPI_INFO " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1003: /* HSE_APPEND_LOG_PARAMETER */ |
| /* Log lpvBuffer, of lpdwSize bytes, in the URI Query (cs-uri-query) field |
| * This code will do for now... |
| */ |
| ap_table_set(r->notes, "isapi-parameter", (char*) lpvBuffer); |
| if (AppendLogToQuery) { |
| if (r->args) |
| r->args = ap_pstrcat(r->pool, r->args, (char*) lpvBuffer, NULL); |
| else |
| r->args = ap_pstrdup(r->pool, (char*) lpvBuffer); |
| } |
| if (AppendLogToErrors) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, |
| "ISAPI %s: %s", cid->r->filename, |
| (char*) lpvBuffer); |
| return TRUE; |
| |
| /* We don't support all this async I/O, Microsoft-specific stuff */ |
| case 1005: /* HSE_REQ_IO_COMPLETION */ |
| case 1006: /* HSE_REQ_TRANSMIT_FILE */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI asynchronous I/O not supported: %s", |
| r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1007: /* HSE_REQ_REFRESH_ISAPI_ACL */ |
| /* Since we don't override the user ID and access, we can't reset. |
| */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction " |
| "HSE_REQ_REFRESH_ISAPI_ACL " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1008: /* HSE_REQ_IS_KEEP_CONN */ |
| *((LPBOOL) lpvBuffer) = (r->connection->keepalive == 1); |
| return TRUE; |
| |
| case 1010: /* HSE_REQ_ASYNC_READ_CLIENT */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI asynchronous I/O not supported: %s", |
| r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1011: /* HSE_REQ_GET_IMPERSONATION_TOKEN Added in ISAPI 4.0 */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction " |
| "HSE_REQ_GET_IMPERSONATION_TOKEN " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| #ifdef HSE_REQ_MAP_URL_TO_PATH_EX |
| case 1012: /* HSE_REQ_MAP_URL_TO_PATH_EX */ |
| { |
| /* Map a URL to a filename */ |
| LPHSE_URL_MAPEX_INFO info = (LPHSE_URL_MAPEX_INFO) lpdwDataType; |
| char* test_uri = ap_pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize); |
| |
| subreq = ap_sub_req_lookup_uri(test_uri, r); |
| info->cchMatchingURL = strlen(test_uri); |
| info->cchMatchingPath = ap_cpystrn(info->lpszPath, subreq->filename, |
| MAX_PATH) - info->lpszPath; |
| |
| /* Mapping started with assuming both strings matched. |
| * Now roll on the path_info as a mismatch and handle |
| * terminating slashes for directory matches. |
| */ |
| if (subreq->path_info && *subreq->path_info) { |
| ap_cpystrn(info->lpszPath + info->cchMatchingPath, |
| subreq->path_info, MAX_PATH - info->cchMatchingPath); |
| info->cchMatchingURL -= strlen(subreq->path_info); |
| if (S_ISDIR(subreq->finfo.st_mode) |
| && info->cchMatchingPath < MAX_PATH - 1) { |
| /* roll forward over path_info's first slash */ |
| ++info->cchMatchingPath; |
| ++info->cchMatchingURL; |
| } |
| } |
| else if (S_ISDIR(subreq->finfo.st_mode) |
| && info->cchMatchingPath < MAX_PATH - 1) { |
| /* Add a trailing slash for directory */ |
| info->lpszPath[info->cchMatchingPath++] = '/'; |
| info->lpszPath[info->cchMatchingPath] = '\0'; |
| } |
| |
| /* If the matched isn't a file, roll match back to the prior slash */ |
| if (!subreq->finfo.st_mode) { |
| while (info->cchMatchingPath && info->cchMatchingURL) { |
| if (info->lpszPath[info->cchMatchingPath - 1] == '/') |
| break; |
| --info->cchMatchingPath; |
| --info->cchMatchingURL; |
| } |
| } |
| |
| /* Paths returned with back slashes */ |
| for (test_uri = info->lpszPath; *test_uri; ++test_uri) |
| if (*test_uri == '/') |
| *test_uri = '\\'; |
| |
| /* is a combination of: |
| * HSE_URL_FLAGS_READ 0x001 Allow read |
| * HSE_URL_FLAGS_WRITE 0x002 Allow write |
| * HSE_URL_FLAGS_EXECUTE 0x004 Allow execute |
| * HSE_URL_FLAGS_SSL 0x008 Require SSL |
| * HSE_URL_FLAGS_DONT_CACHE 0x010 Don't cache (VRoot only) |
| * HSE_URL_FLAGS_NEGO_CERT 0x020 Allow client SSL cert |
| * HSE_URL_FLAGS_REQUIRE_CERT 0x040 Require client SSL cert |
| * HSE_URL_FLAGS_MAP_CERT 0x080 Map client SSL cert to account |
| * HSE_URL_FLAGS_SSL128 0x100 Require 128-bit SSL cert |
| * HSE_URL_FLAGS_SCRIPT 0x200 Allow script execution |
| * |
| * XxX: As everywhere, EXEC flags could use some work... |
| * and this could go further with more flags, as desired. |
| */ |
| info->dwFlags = (subreq->finfo.st_mode & _S_IREAD ? 0x001 : 0) |
| | (subreq->finfo.st_mode & _S_IWRITE ? 0x002 : 0) |
| | (subreq->finfo.st_mode & _S_IEXEC ? 0x204 : 0); |
| return TRUE; |
| } |
| #endif |
| |
| case 1014: /* HSE_REQ_ABORTIVE_CLOSE */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction HSE_REQ_ABORTIVE_CLOSE" |
| " is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1015: /* HSE_REQ_GET_CERT_INFO_EX Added in ISAPI 4.0 */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction " |
| "HSE_REQ_GET_CERT_INFO_EX " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| #ifdef HSE_REQ_SEND_RESPONSE_HEADER_EX |
| case 1016: /* HSE_REQ_SEND_RESPONSE_HEADER_EX Added in ISAPI 4.0 */ |
| { |
| LPHSE_SEND_HEADER_EX_INFO shi |
| = (LPHSE_SEND_HEADER_EX_INFO) lpvBuffer; |
| /* XXX: ignore shi->fKeepConn? We shouldn't need the advise */ |
| /* r->connection->keepalive = shi->fKeepConn; */ |
| return SendResponseHeaderEx(cid, shi->pszStatus, shi->pszHeader, |
| shi->cchStatus, shi->cchHeader); |
| } |
| #endif |
| |
| case 1017: /* HSE_REQ_CLOSE_CONNECTION Added after ISAPI 4.0 */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction " |
| "HSE_REQ_CLOSE_CONNECTION " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| case 1018: /* HSE_REQ_IS_CONNECTED Added after ISAPI 4.0 */ |
| /* Returns True if client is connected c.f. MSKB Q188346 |
| * XXX: That statement is very ambigious... assuming the |
| * identical return mechanism as HSE_REQ_IS_KEEP_CONN. |
| */ |
| *((LPBOOL) lpvBuffer) = (r->connection->aborted == 0); |
| return TRUE; |
| |
| case 1020: /* HSE_REQ_EXTENSION_TRIGGER Added after ISAPI 4.0 */ |
| /* Undocumented - defined by the Microsoft Jan '00 Platform SDK |
| */ |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction " |
| "HSE_REQ_EXTENSION_TRIGGER " |
| "is not supported: %s", r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| |
| |
| default: |
| if (LogNotSupported) |
| ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, |
| "ISAPI ServerSupportFunction (%d) not supported: " |
| "%s", dwHSERequest, r->filename); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| } |
| |
| /* |
| * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1 |
| */ |
| static const char *isapi_cmd_readaheadbuffer(cmd_parms *cmd, void *config, |
| char *arg) |
| { |
| long val; |
| const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
| if (err != NULL) { |
| return err; |
| } |
| |
| if (((val = ap_strtol(arg, (char **) &err, 10)) <= 0) || *err) |
| return "ISAPIReadAheadBuffer must be a legitimate value."; |
| |
| ReadAheadBuffer = val; |
| return NULL; |
| } |
| |
| /* |
| * Command handler for the ISAPIReadAheadBuffer directive, which is TAKE1 |
| */ |
| static const char *isapi_cmd_lognotsupported(cmd_parms *cmd, void *config, |
| char *arg) |
| { |
| const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
| if (err != NULL) { |
| return err; |
| } |
| |
| if (strcasecmp(arg, "on") == 0) { |
| LogNotSupported = -1; |
| } |
| else if (strcasecmp(arg, "off") == 0) { |
| LogNotSupported = 0; |
| } |
| else { |
| return "ISAPILogNotSupported must be on or off"; |
| } |
| return NULL; |
| } |
| |
| static const char *isapi_cmd_appendlogtoerrors(cmd_parms *cmd, void *config, |
| char *arg) |
| { |
| const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
| if (err != NULL) { |
| return err; |
| } |
| |
| if (strcasecmp(arg, "on") == 0) { |
| AppendLogToErrors = -1; |
| } |
| else if (strcasecmp(arg, "off") == 0) { |
| AppendLogToErrors = 0; |
| } |
| else { |
| return "ISAPIAppendLogToErrors must be on or off"; |
| } |
| return NULL; |
| } |
| |
| static const char *isapi_cmd_appendlogtoquery(cmd_parms *cmd, void *config, |
| char *arg) |
| { |
| const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
| if (err != NULL) { |
| return err; |
| } |
| |
| if (strcasecmp(arg, "on") == 0) { |
| AppendLogToQuery = -1; |
| } |
| else if (strcasecmp(arg, "off") == 0) { |
| AppendLogToQuery = 0; |
| } |
| else { |
| return "ISAPIAppendLogToQuery must be on or off"; |
| } |
| return NULL; |
| } |
| |
| static const command_rec isapi_cmds[] = { |
| { "ISAPIReadAheadBuffer", isapi_cmd_readaheadbuffer, NULL, RSRC_CONF, TAKE1, |
| "Maximum bytes to initially pass to the ISAPI handler" }, |
| { "ISAPILogNotSupported", isapi_cmd_lognotsupported, NULL, RSRC_CONF, TAKE1, |
| "Log requests not supported by the ISAPI server" }, |
| { "ISAPIAppendLogToErrors", isapi_cmd_appendlogtoerrors, NULL, RSRC_CONF, TAKE1, |
| "Send all Append Log requests to the error log" }, |
| { "ISAPIAppendLogToQuery", isapi_cmd_appendlogtoquery, NULL, RSRC_CONF, TAKE1, |
| "Append Log requests are concatinated to the query args" }, |
| { NULL } |
| }; |
| |
| handler_rec isapi_handlers[] = { |
| { "isapi-isa", isapi_handler }, |
| { NULL} |
| }; |
| |
| module isapi_module = { |
| STANDARD_MODULE_STUFF, |
| NULL, /* initializer */ |
| NULL, /* create per-dir config */ |
| NULL, /* merge per-dir config */ |
| NULL, /* server config */ |
| NULL, /* merge server config */ |
| isapi_cmds, /* command table */ |
| isapi_handlers, /* handlers */ |
| NULL, /* filename translation */ |
| NULL, /* check_user_id */ |
| NULL, /* check auth */ |
| NULL, /* check access */ |
| NULL, /* type_checker */ |
| NULL, /* logger */ |
| NULL /* header parser */ |
| }; |
| |
| #endif /* WIN32 */ |