📄 extns.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: ISAPI.CPP
Abstract: ISAPI handling code
--*/
#include "httpd.h"
// Amount of time before unloading an unused ISAPI
#define SCRIPT_TIMEOUT_DEFAULT (1000*60*30)
const DWORD g_fIsapiExtensionModule = TRUE;
BOOL CGlobalVariables::InitExtensions(CReg *pWebsite) {
DEBUGCHK(m_fRootSite);
m_dwCacheSleep = pWebsite->ValueDW(RV_SCRIPTTIMEOUT,SCRIPT_TIMEOUT_DEFAULT);
m_pISAPICache = new CISAPICache();
if (NULL == m_pISAPICache)
return FALSE;
InitScriptMapping(&m_scriptMap);
InitScriptNoUnloadList(&m_scriptNoUnload);
return TRUE;
}
void RemoveScriptNodes(PSCRIPT_MAP_NODE pScriptNodes, DWORD dwEntries);
void RemoveScriptNoUnloadList(WCHAR **szArray, DWORD dwEntries);
void CGlobalVariables::DeInitExtensions(void) {
DEBUGCHK(m_fRootSite);
RemoveScriptNodes(m_scriptMap.pScriptNodes,m_scriptMap.dwEntries);
RemoveScriptNoUnloadList(m_scriptNoUnload.ppszNoUnloadEntry,m_scriptNoUnload.dwEntries);
}
BOOL CHttpRequest::ExecuteISAPI(WCHAR *wszExecutePath) {
DWORD dwRet = HSE_STATUS_ERROR;
EXTENSION_CONTROL_BLOCK ECB;
CISAPI *pISAPIDLL = NULL;
__try {
if (! g_pVars->m_pISAPICache->Load(wszExecutePath,&pISAPIDLL))
return FALSE;
// create an ECB (this allocates no memory)
FillECB(&ECB);
// call the ISAPI
dwRet = pISAPIDLL->CallExtension(&ECB);
// grab log data if any
if(ECB.lpszLogData[0]) {
MyFree(m_pszLogParam);
// silently fail on alloc failure. ISAPI has sent all request
// headers at this point anyway, we just lose log info.
m_pszLogParam = MySzDupA(ECB.lpszLogData);
}
}
__except(ReportFault(GetExceptionInformation(),0), EXCEPTION_EXECUTE_HANDLER) { // catch all exceptions
DEBUGMSG(ZONE_ERROR, (L"HTTPD: ISAPI DLL caused exception 0x%08x and was terminated\r\n", GetExceptionCode()));
g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXT_EXCEPTION,wszExecutePath,GetExceptionCode(),L"HttpExtensionProc",GetLastError());
}
g_pVars->m_pISAPICache->Unload(pISAPIDLL);
// set keep-alive status based on return code
m_fKeepAlive = (dwRet==HSE_STATUS_SUCCESS_AND_KEEP_CONN);
DEBUGMSG(ZONE_ISAPI, (L"HTTPD: ISAPI ExtProc returned %d keep=%d\r\n", dwRet, m_fKeepAlive));
DEBUGCHK(dwRet != HSE_STATUS_PENDING);
return (dwRet!=HSE_STATUS_ERROR);
}
void CHttpRequest::FillECB(LPEXTENSION_CONTROL_BLOCK pECB) {
ZEROMEM(pECB);
pECB->cbSize = sizeof(*pECB);
pECB->dwVersion = HSE_VERSION;
pECB->ConnID = (HCONN)this;
DEBUGCHK(m_pszMethod);
// NOTE: IIS examines dwHttpStatusCode on the return of HttpExtensionProc and
// uses this value for logging. CE does not support this functionality.
pECB->dwHttpStatusCode = 200;
pECB->lpszMethod = m_pszMethod;
pECB->lpszQueryString = (PSTR)(m_pszQueryString ? m_pszQueryString : cszEmpty);
pECB->lpszContentType = (PSTR)(m_pszContentType ? m_pszContentType : cszEmpty);
pECB->lpszPathInfo = (PSTR) (m_pszPathInfo ? m_pszPathInfo : cszEmpty);
pECB->lpszPathTranslated = (PSTR) (m_pszPathTranslated ? m_pszPathTranslated : cszEmpty);
pECB->cbTotalBytes = m_dwContentLength;
pECB->cbAvailable = m_bufRequest.Count();
DEBUGCHK(pECB->cbAvailable <= pECB->cbTotalBytes);
pECB->lpbData = m_bufRequest.Data();
pECB->GetServerVariable = ::GetServerVariable;
pECB->WriteClient = ::WriteClient;
pECB->ReadClient = ::ReadClient;
pECB->ServerSupportFunction = ::ServerSupportFunction;
}
BOOL WINAPI GetServerVariable(HCONN hConn, PSTR psz, PVOID pv, PDWORD pdw) {
CHECKHCONN(hConn);
CHECKPTRS2(psz, pdw);
return ((CHttpRequest*)hConn)->GetServerVariable(psz, pv, pdw, FALSE);
}
int RecvToBuf(SOCKET socket, PVOID pv, DWORD dwReadBufSize, DWORD dwTimeout) {
DEBUG_CODE_INIT;
int iRecv = SOCKET_ERROR;
DWORD dwAvailable;
if (!MySelect(socket,dwTimeout)) {
SetLastError(WSAETIMEDOUT);
myleave(1400);
}
if(ioctlsocket(socket, FIONREAD, &dwAvailable)) {
SetLastError(WSAETIMEDOUT);
myleave(1401);
}
iRecv = recv(socket, (PSTR) pv, (dwAvailable < dwReadBufSize) ? dwAvailable : dwReadBufSize, 0);
if (iRecv == 0) {
DEBUGMSG(ZONE_REQUEST,(L"HTTP: ReadBuffer call to recv() returns 0\r\n"));
SetLastError(WSAETIMEDOUT);
myleave(1402);
}
if (iRecv == SOCKET_ERROR) {
DEBUGMSG(ZONE_REQUEST,(L"HTTP: ReadBuffer call to recv() returns SOCKET_ERROR,GLE=0x%08x\r\n",GetLastError()));
// recv() already has called SetLastError with appropriate message
myleave(1403);
}
done:
DEBUGMSG_ERR(ZONE_REQUEST,(L"HTTPD: RecvToBuf returns error err = %d, GLE = 0x%08x\r\n",err,GetLastError()));
return iRecv;
}
BOOL WINAPI ReadClient(HCONN hConn, PVOID pv, PDWORD pdw) {
CHECKHCONN(hConn);
CHECKPTRS2(pv, pdw);
if ( *pdw == 0) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return ((CHttpRequest*)hConn)->ReadClient(pv,pdw);
}
BOOL CHttpRequest::ReadClient(PVOID pv, PDWORD pdw) {
DEBUG_CODE_INIT;
BOOL ret = FALSE;
PVOID pvFilterModify = pv;
DWORD dwBytesReceived;
DWORD dwBufferSize = *pdw;
m_bufRequest.InvalidateNextRequestIfAlreadyRead();
if (!m_fHandleSSL)
dwBytesReceived = RecvToBuf(m_socket,pv,*pdw,g_pVars->m_dwConnectionTimeout);
else {
DEBUGCHK(g_pVars->m_fSSL);
// The buffer is designed so it will immediatly unencrypt a request sent to
// it, so there's never encrypted data at end of buf.
DWORD dwReadyBytes = m_bufRequest.UnaccessedCount();
if (dwReadyBytes >= *pdw) {
// We've already read in this data
memcpy(pv,m_bufRequest.m_pszBuf+m_bufRequest.m_iNextInFollow,*pdw);
m_bufRequest.m_iNextInFollow += (int) *pdw;
dwBytesReceived = *pdw;
}
else {
if (m_hrPreviousReadClient == INPUT_ERROR) {
myleave(1395);
}
else if (m_hrPreviousReadClient == INPUT_TIMEOUT) {
SetLastError(WSAETIMEDOUT);
myleave(1396);
}
// copy what's in buf, reset where our read starts (we'll reuse
// portion of buffer that's not in initial POST), and recv()
PSTR pszNextWrite = (PSTR)pv + dwReadyBytes;
DWORD dwToRead = *pdw-dwReadyBytes;
if (dwReadyBytes)
memcpy(pv,m_bufRequest.m_pszBuf+m_bufRequest.m_iNextInFollow,dwReadyBytes);
m_bufRequest.m_iNextDecrypt = m_bufRequest.m_iNextInFollow = m_bufRequest.m_iNextIn = (int) m_dwInitialPostRead;
// It's theoretically possible that the amount of bytes we have to read after
// doing the above memcpy() is less than the size of an SSL header. (ie if buf size=500,
// we have 496 encrypted bytes, then we'd only be trying to read 4 bytes, which is bad if SSL header is 5 bytes long.)
DWORD dwRecvOffWire = max(m_SSLInfo.m_Sizes.cbHeader,dwToRead);
m_hrPreviousReadClient = m_bufRequest.RecvBody(m_socket,dwRecvOffWire,TRUE,FALSE,this,FALSE);
if (m_hrPreviousReadClient == INPUT_TIMEOUT || m_hrPreviousReadClient == INPUT_ERROR) {
// Only return FALSE in this case if there was no data in buffer, otherwise
// we'll copy what we have and return FALSE next time client calls us.
if (dwReadyBytes == 0) {
if (m_hrPreviousReadClient == INPUT_ERROR)
SetLastError(WSAETIMEDOUT);
myleave(1398);
}
}
DWORD dwToCopy = min(m_bufRequest.UnaccessedCount(),dwToRead);
memcpy(pszNextWrite,m_bufRequest.m_pszBuf+m_bufRequest.m_iNextInFollow,dwToCopy);
m_bufRequest.m_iNextInFollow += dwToCopy;
dwBytesReceived = dwReadyBytes + dwToCopy;
}
}
if (dwBytesReceived == 0 || dwBytesReceived == SOCKET_ERROR)
myleave(1399);
if (g_pVars->m_fFilters && !CallFilter(SF_NOTIFY_READ_RAW_DATA,(PSTR*) &pvFilterModify,(int*) &dwBytesReceived, NULL, (int *) &dwBufferSize))
myleave(1404);
// Check if filter modified pointer, copy if there's enough room for it.
if (pvFilterModify != pv) {
if (*pdw <= dwBufferSize) {
memcpy(pv,pvFilterModify,dwBufferSize);
}
else {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: An ISAPI read filter modified data buffer so it was too big for ISAPI ReadClient buf, no copy will be done\r\n"));
SetLastError(ERROR_INSUFFICIENT_BUFFER);
myleave(1405);
}
}
*pdw = dwBytesReceived;
ret = TRUE;
done:
DEBUGMSG_ERR(ZONE_ISAPI,(L"HTTPD:ReadClient failed, GLE = 0x%08x, err = % err\r\n",GetLastError(), err));
return ret;
}
BOOL WINAPI WriteClient(HCONN hConn, PVOID pv, PDWORD pdw, DWORD dwFlags) {
CHECKHCONN(hConn);
CHECKPTRS2(pv, pdw);
if(dwFlags & HSE_IO_ASYNC) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
return ((CHttpRequest*)hConn)->WriteClient(pv, pdw,FALSE);
}
BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType) {
CHECKHCONN(hConn);
return ((CHttpRequest*)hConn)->ServerSupportFunction(dwReq, pvBuf, pdwSize, pdwType);
}
BOOL CHttpRequest::ServerSupportFunction(DWORD dwReq, PVOID pvBuf, PDWORD pdwSize, PDWORD pdwType) {
switch(dwReq)
{
// Can never support these
//case HSE_REQ_ABORTIVE_CLOSE:
//case HSE_REQ_DONE_WITH_SESSION:
//case HSE_REQ_ASYNC_READ_CLIENT:
//case HSE_REQ_GET_CERT_INFO_EX:
//case HSE_REQ_GET_IMPERSONATION_TOKEN:
//case HSE_REQ_GET_SSPI_INFO:
//case HSE_REQ_IO_COMPLETION:
//case HSE_REQ_REFRESH_ISAPI_ACL:
//case HSE_REQ_TRANSMIT_FILE:
default:
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
case HSE_REQ_IS_KEEP_CONN:
{
CHECKPTR(pvBuf);
*((BOOL *) pvBuf) = m_fKeepAlive;
return TRUE;
}
case HSE_REQ_SEND_URL:
case HSE_REQ_SEND_URL_REDIRECT_RESP:
{
CHECKPTR(pvBuf);
// close connection, because ISAPI won't have a chance to add headers anyway
CHttpResponse resp(this,STATUS_MOVED,CONN_CLOSE);
resp.SendRedirect((PSTR)pvBuf); // send a special redirect body
m_fKeepAlive = FALSE;
return TRUE;
}
case HSE_REQ_MAP_URL_TO_PATH_EX:
case HSE_REQ_MAP_URL_TO_PATH:
{
CHECKPTRS2(pvBuf,pdwSize);
PSTR szURLToMap = (PSTR)pvBuf;
if (!IsSlash(szURLToMap[0])) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: ServerSupportFunction(HSE_REQ_MAP_URL_TO_PATH) returns failure because URL to map did not begin with a '/' or '\\'\r\n"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (dwReq == HSE_REQ_MAP_URL_TO_PATH_EX) {
if (!pdwType) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return MapURLToPath(szURLToMap,pdwSize,(LPHSE_URL_MAPEX_INFO) pdwType);
}
else {
// IIS docs are misleading here, but even if a valid param is passed in non-EX
// case, ignore it. (Like IIS.)
return MapURLToPath(szURLToMap,pdwSize);
}
}
case HSE_REQ_SEND_RESPONSE_HEADER:
{
// no Connection header...let ISAPI send one if it wants
CHttpResponse resp(this, STATUS_OK, CONN_NONE);
// no body, default or otherwise (leave that to the ISAPI), but add default headers
resp.SendResponse((PSTR) pdwType, (PSTR) pvBuf);
return TRUE;
}
case HSE_REQ_SEND_RESPONSE_HEADER_EX:
{
// Note: We ignore cchStatus and cchHeader members.
CHECKPTR(pvBuf);
HSE_SEND_HEADER_EX_INFO *pHeaderEx = (HSE_SEND_HEADER_EX_INFO *) pvBuf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -