This is mod_isapi.c in view mode; [Download] [Up]
/* ==================================================================== * Copyright (c) 1995-1997 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see <http://www.apache.org/>. * */ /* * 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. */ #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" /* We use the exact same header file as the original */ #include <HttpExt.h> module isapi_module; /* 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); int isapi_handler (request_rec *r) { LPEXTENSION_CONTROL_BLOCK ecb = pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK)); HSE_VERSION_INFO *pVer = 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 = pcalloc(r->pool, sizeof(isapi_cid)); table *e = r->subprocess_env; int retval; /* Use similar restrictions as CGIs */ if (!(allow_options(r) & OPT_EXECCGI)) return FORBIDDEN; if (S_ISDIR(r->finfo.st_mode)) return FORBIDDEN; if (r->finfo.st_mode == 0) return NOT_FOUND; /* Load the module */ if (!(isapi_handle = LoadLibraryEx(r->filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))) { log_reason("Could not load DLL", r->filename, r); return SERVER_ERROR; } if (!(isapi_version = (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) { log_reason("DLL could not load GetExtensionVersion()", r->filename, r); FreeLibrary(isapi_handle); return SERVER_ERROR; } if (!(isapi_entry = (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) { log_reason("DLL could not load HttpExtensionProc()", r->filename, r); FreeLibrary(isapi_handle); return SERVER_ERROR; } isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension")); /* Run GetExtensionVersion() */ if ((*isapi_version)(pVer) != TRUE) { log_reason("ISAPI GetExtensionVersion() failed", r->filename, r); FreeLibrary(isapi_handle); return SERVER_ERROR; } /* Set up variables */ add_common_vars(r); add_cgi_vars(r); /* 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 = r->method; ecb->lpszQueryString = table_get(e, "QUERY_STRING"); ecb->lpszPathInfo = table_get(e, "PATH_INFO"); ecb->lpszPathTranslated = table_get(e, "PATH_TRANSLATED"); ecb->lpszContentType = table_get(e, "CONTENT_TYPE"); /* Set up client input */ if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR))) { if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); FreeLibrary(isapi_handle); return retval; } if (should_client_block(r)) { /* Unlike IIS, which limits this to 48k, we read the whole * sucker in. I suppose this could be bad for memory if someone * uploaded the complete works of Shakespeare. Well, WebSite * does the same thing. */ long to_read = atol(table_get(e, "CONTENT_LENGTH")); long read; /* Actually, let's cap it at 48k, until we figure out what * to do with this... we don't want a Content-Length: 1000000000 * taking out the machine. */ if (to_read > 49152) { if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); FreeLibrary(isapi_handle); return HTTP_REQUEST_ENTITY_TOO_LARGE; } ecb->lpbData = pcalloc(r->pool, 1 + to_read); if ((read = get_client_block(r, ecb->lpbData, to_read)) < 0) { if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); FreeLibrary(isapi_handle); return SERVER_ERROR; } /* Although its not to spec, IIS seems to null-terminate * its lpdData string. So we will too. To make sure * cbAvailable matches cbTotalBytes, we'll up the latter * and equalize them. */ ecb->cbAvailable = ecb->cbTotalBytes = read + 1; 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, "")) log_reason(ecb->lpszLogData, r->filename, r); /* All done with the DLL... get rid of it */ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); FreeLibrary(isapi_handle); switch(retval) { 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 */ log_reason("ISAPI asynchronous I/O not supported", r->filename, r); case HSE_STATUS_ERROR: default: return SERVER_ERROR; } } BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) { request_rec *r = ((isapi_cid *)hConn)->r; table *e = r->subprocess_env; char *result; /* Mostly, we just grab it from the environment, but there are * a couple of special cases */ if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) { /* We don't support NT users, so this is always the same as * REMOTE_USER */ result = table_get(e, "REMOTE_USER"); } else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) { /* Apache doesn't support secure requests inherently, so * we have no way of knowing. We'll be conservative, and say * all requests are insecure. */ result = "0"; } else if (!strcasecmp(lpszVariableName, "URL")) { result = r->uri; } else { result = table_get(e, lpszVariableName); } if (result) { if (strlen(result) > *lpdwSizeofBuffer) { *lpdwSizeofBuffer = strlen(result); SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } strncpy(lpvBuffer, result, *lpdwSizeofBuffer); return TRUE; } /* Didn't find it */ SetLastError(ERROR_INVALID_INDEX); return FALSE; } BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, DWORD dwReserved) { request_rec *r = ((isapi_cid *)ConnID)->r; int writ; /* written, actually, but why shouldn't I make up words? */ /* We only support synchronous writing */ if (dwReserved && dwReserved != HSE_IO_SYNC) { log_reason("ISAPI asynchronous I/O not supported", r->filename, r); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if ((writ = rwrite(Buffer, *lpwdwBytes, r)) == EOF) { SetLastError(ERROR); /* XXX: Find the right error code */ return FALSE; } *lpwdwBytes = writ; return TRUE; } BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) { /* Doesn't need to do anything; we've read all the data already */ return TRUE; } BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType) { isapi_cid *cid = (isapi_cid *)hConn; request_rec *subreq, *r = cid->r; char *data; switch (dwHSERequest) { case HSE_REQ_SEND_URL_REDIRECT_RESP: /* Set the status to be returned when the HttpExtensionProc() * is done. */ table_set (r->headers_out, "Location", lpvBuffer); cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT; return TRUE; case HSE_REQ_SEND_URL: /* Read any additional input */ if (r->remaining > 0) { char argsbuffer[HUGE_STRING_LEN]; while (get_client_block(r, argsbuffer, HUGE_STRING_LEN)); } /* Reset the method to GET */ r->method = pstrdup(r->pool, "GET"); r->method_number = M_GET; /* Don't let anyone think there's still data */ table_unset(r->headers_in, "Content-Length"); internal_redirect((char *)lpvBuffer, r); return TRUE; case HSE_REQ_SEND_RESPONSE_HEADER: r->status_line = lpvBuffer ? lpvBuffer : pstrdup(r->pool, "200 OK"); sscanf(r->status_line, "%d", &r->status); cid->ecb->dwHttpStatusCode = r->status; /* Now fill in the HTTP headers, and the rest of it. Ick. * lpdwDataType contains a string that has headers (in MIME * format), a blank like, then (possibly) data. We need * to parse it. * * Easy case first: */ if (!lpdwDataType) { send_http_header(r); return TRUE; } /* Make a copy - don't disturb the original */ data = pstrdup(r->pool, (char *)lpdwDataType); /* We *should* break before this while loop ends */ while (*data) { char *value, *lf = strchr(data, '\n'); int p; if (!lf) { /* Huh? Invalid data, I think */ log_reason("ISA sent invalid headers", r->filename, r); SetLastError(ERROR); /* XXX: Find right error */ return FALSE; } /* Get rid of \n and \r */ *lf = '\0'; p = strlen(data); if (p > 0 && data[p-1] == '\r') data[p-1] = '\0'; /* End of headers */ if (*data == '\0') { data = lf + 1; /* Reset data */ break; } if (!(value = strchr(data, ':'))) { SetLastError(ERROR); /* XXX: Find right error */ log_reason("ISA sent invalid headers", r->filename, r); return FALSE; } *value++ = '\0'; while (*value && isspace(*value)) ++value; /* Check all the special-case headers. Similar to what * scan_script_header() does (see that function for * more detail) */ if (!strcasecmp(data, "Content-Type")) { /* Nuke trailing whitespace */ char *endp = value + strlen(value) - 1; while (endp > value && isspace(*endp)) *endp-- = '\0'; r->content_type = pstrdup (r->pool, value); } else if (!strcasecmp(data, "Content-Length")) { table_set(r->headers_out, data, value); } else if (!strcasecmp(data, "Transfer-Encoding")) { table_set(r->headers_out, data, value); } else if (!strcasecmp(data, "Set-Cookie")) { table_add(r->err_headers_out, data, value); } else { table_merge(r->err_headers_out, data, value); } /* Reset data */ data = lf + 1; } /* All the headers should be set now */ send_http_header(r); /* Any data left should now be sent directly */ rputs(data, r); return TRUE; case HSE_REQ_MAP_URL_TO_PATH: /* Map a URL to a filename */ subreq = sub_req_lookup_uri(pstrndup(r->pool, (char *)lpvBuffer, *lpdwSize), r); GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL); /* IIS puts a trailing slash on directories, Apache doesn't */ if (S_ISDIR (subreq->finfo.st_mode)) { int l = strlen((char *)lpvBuffer); ((char *)lpvBuffer)[l] = '\\'; ((char *)lpvBuffer)[l + 1] = '\0'; } return TRUE; case HSE_REQ_DONE_WITH_SESSION: /* Do nothing... since we don't support async I/O, they'll * return from HttpExtensionProc soon */ return TRUE; /* We don't support all this async I/O, Microsoft-specific stuff */ case HSE_REQ_IO_COMPLETION: case HSE_REQ_TRANSMIT_FILE: log_reason("ISAPI asynchronous I/O not supported", r->filename, r); default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } 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 */ NULL, /* 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 */ };
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.