mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
22 KiB
726 lines
22 KiB
/*--
|
|
Copyright (c) 1995-1998 Microsoft Corporation
|
|
Module Name: ISAPI.CPP
|
|
Author: Arul Menezes
|
|
Abstract: ISAPI handling code
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "httpd.h"
|
|
|
|
|
|
#define RK_OLE L"SOFTWARE\\Microsoft\\Ole"
|
|
#define RK_FREETIMEOUT L"FreeTimeout"
|
|
|
|
// Amount of time ISAPI cache timeout thread should sleep between firing.
|
|
#define CACHE_SLEEP_TIMEOUT 60000
|
|
|
|
BOOL InitExtensions(CISAPICache **ppISAPICache, DWORD *pdwCacheSleep)
|
|
{
|
|
CReg reg(HKEY_LOCAL_MACHINE, RK_OLE);
|
|
|
|
// We double the amount of time COM tells us it's using to be safe.
|
|
*pdwCacheSleep = 3*reg.ValueDW(RK_FREETIMEOUT,600000); // default is 30 minutes, 1800000
|
|
*ppISAPICache = new CISAPICache();
|
|
|
|
return(NULL != *ppISAPICache);
|
|
}
|
|
|
|
|
|
BOOL CHttpRequest::ExecuteISAPI(void)
|
|
{
|
|
DWORD dwRet = HSE_STATUS_ERROR;
|
|
EXTENSION_CONTROL_BLOCK ECB;
|
|
CISAPI *pISAPIDLL = NULL;
|
|
|
|
// wrap all calls to the ISAPI in a _try--_except
|
|
|
|
__try
|
|
{
|
|
if (! g_pVars->m_pISAPICache->Load(m_wszPath,&pISAPIDLL))
|
|
return FALSE;
|
|
|
|
// create an ECB (this allocates no memory)
|
|
FillECB(&ECB);
|
|
|
|
DEBUGCHK(m_hEvent == NULL);
|
|
m_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
m_pECB = &ECB;
|
|
|
|
// call the ISAPI
|
|
dwRet = pISAPIDLL->CallExtension(&ECB);
|
|
|
|
// grab log data if any
|
|
if (ECB.lpszLogData[0])
|
|
{
|
|
DEBUGCHK(!m_pszLogParam);
|
|
m_pszLogParam = MySzDupA(ECB.lpszLogData);
|
|
}
|
|
}
|
|
__except(1) // catch all exceptions
|
|
{
|
|
TraceTag(ttidWebServer, "ISAPI DLL caused exception 0x%08x and was terminated", GetExceptionCode());
|
|
g_pVars->m_pLog->WriteEvent(IDS_HTTPD_EXT_EXCEPTION,m_wszPath,GetExceptionCode(),L"HttpExtensionProc",GetLastError());
|
|
}
|
|
|
|
// set keep-alive status based on return code
|
|
m_fKeepAlive = (dwRet==HSE_STATUS_SUCCESS_AND_KEEP_CONN);
|
|
|
|
TraceTag(ttidWebServer, "ISAPI ExtProc returned %d keep=%d", dwRet, m_fKeepAlive);
|
|
|
|
if (dwRet == HSE_STATUS_PENDING)
|
|
{
|
|
DWORD dwRes = WAIT_ABANDONED;
|
|
|
|
// Wait up to 5 minutes for the request to complete
|
|
if (m_hEvent != NULL)
|
|
dwRes = WaitForSingleObject (m_hEvent, 300000);
|
|
|
|
AssertSz(dwRes == WAIT_OBJECT_0, "Pended request was timed out after 5 minutes");
|
|
|
|
if (dwRes == WAIT_OBJECT_0)
|
|
dwRet = m_dwStatus;
|
|
else
|
|
dwRet = HSE_STATUS_ERROR;
|
|
}
|
|
|
|
if (m_hEvent != NULL)
|
|
{
|
|
CloseHandle (m_hEvent);
|
|
m_hEvent = NULL;
|
|
}
|
|
|
|
StartRemoveISAPICacheIfNeeded();
|
|
g_pVars->m_pISAPICache->Unload(pISAPIDLL);
|
|
|
|
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);
|
|
|
|
// BUGBUG 13244: IIS examines dwHttpStatusCode if user doesn't send their own headers
|
|
// and uses it. However, there's a work around for this on CE (direct use to
|
|
// ServerSupportFunction) and using the status code rather than m_rs would
|
|
// be a rearchitecting problem. Fix: None for now.
|
|
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();
|
|
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)
|
|
{
|
|
SetLastError(WSAETIMEDOUT);
|
|
myleave(1402);
|
|
}
|
|
if (iRecv == SOCKET_ERROR)
|
|
{
|
|
// recv() already has called SetLastError with appropriate message
|
|
myleave(1403);
|
|
}
|
|
|
|
|
|
done:
|
|
TraceTag(ttidWebServer, "RecvToBuf returns error err = %d, GLE = 0x%08x",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;
|
|
|
|
dwBytesReceived = RecvToBuf(m_socket,pv,*pdw,RECVTIMEOUT);
|
|
|
|
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
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
myleave(1405);
|
|
}
|
|
}
|
|
|
|
*pdw = dwBytesReceived;
|
|
ret = TRUE;
|
|
done:
|
|
TraceTag(ttidWebServer, "HTTPD:ReadClient failed, GLE = 0x%08x, err = % err",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)
|
|
return((CHttpRequest*)hConn)->WriteClientAsync(pv, pdw, FALSE);
|
|
else
|
|
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_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_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:
|
|
{
|
|
|
|
// close connection, because ISAPI won't have a chance to add headers anyway
|
|
CHttpResponse resp(m_socket, STATUS_MOVED, CONN_CLOSE,this);
|
|
// m_rs = STATUS_MOVED;
|
|
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);
|
|
|
|
if (dwReq == HSE_REQ_MAP_URL_TO_PATH_EX)
|
|
{
|
|
if (!pdwType)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
return MapURLToPath((PSTR)pvBuf,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((PSTR)pvBuf,pdwSize);
|
|
}
|
|
}
|
|
|
|
case HSE_REQ_SEND_RESPONSE_HEADER:
|
|
{
|
|
// no Connection header...let ISAPI send one if it wants
|
|
CHttpResponse resp(m_socket, STATUS_OK, CONN_NONE,this);
|
|
// no body, default or otherwise (leave that to the ISAPI), but add default headers
|
|
m_rs = STATUS_OK;
|
|
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;
|
|
|
|
// Connection header determined by fKeepConn of passed in struct
|
|
CHttpResponse resp(m_socket, STATUS_OK,
|
|
pHeaderEx->fKeepConn ? CONN_KEEP : CONN_CLOSE,
|
|
this);
|
|
|
|
m_fKeepAlive = pHeaderEx->fKeepConn;
|
|
// no body, default or otherwise (leave that to the ISAPI), but add default headers
|
|
m_rs = STATUS_OK;
|
|
resp.SendResponse(pHeaderEx->pszHeader, pHeaderEx->pszStatus);
|
|
return TRUE;
|
|
}
|
|
|
|
case HSE_APPEND_LOG_PARAMETER:
|
|
{
|
|
return MyStrCatA(&m_pszLogParam,(PSTR) pvBuf,",");
|
|
}
|
|
|
|
case HSE_REQ_CLOSE_CONNECTION:
|
|
{
|
|
// Per ISAPI documentation:
|
|
//
|
|
// Once you use the HSE_REQ_CLOSE_CONNECTION server
|
|
// support function to close a connection, you must
|
|
// wait for IIS to call the asynchronous I/O function
|
|
// (specified by HSE_REQ_IO_COMPLETION) before you end
|
|
// the session with HSE_REQ_DONE_WITH_SESSION.
|
|
//
|
|
// Since our async I/O calls are all really sync, we
|
|
// will call the completion routine at the end of the
|
|
// operation and get a HSE_REQ_DONE_WITH_SESSION to do
|
|
// the rest of the cleanup.
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case HSE_REQ_DONE_WITH_SESSION:
|
|
{
|
|
AssertSz (m_hEvent != NULL, "Call to end a non-pended session, treating as error");
|
|
|
|
if (m_hEvent != NULL)
|
|
{
|
|
m_dwStatus = *((DWORD *) pvBuf);
|
|
m_fKeepAlive = FALSE;
|
|
SetEvent (m_hEvent);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
case HSE_REQ_IO_COMPLETION:
|
|
{
|
|
m_pfnCompletion = (PFN_HSE_IO_COMPLETION)pvBuf;
|
|
m_pvContext = (PVOID) pdwType;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CHttpRequest::MapURLToPath(PSTR pszBuffer, PDWORD pdwSize, LPHSE_URL_MAPEX_INFO pUrlMapEx)
|
|
{
|
|
DWORD dwPermissions;
|
|
PWSTR wszPath;
|
|
PSTR pszURL;
|
|
DWORD dwBufNeeded = 0;
|
|
BOOL ret;
|
|
|
|
wszPath = g_pVars->m_pVroots->URLAtoPathW(pszBuffer,&dwPermissions);
|
|
if (!wszPath)
|
|
{
|
|
// Assume failure on matching to virtual root, and not on mem alloc
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
dwBufNeeded = (DWORD) WideCharToMultiByte(CP_ACP,0, wszPath, -1, pszBuffer, 0 ,0,0);
|
|
|
|
// For MAP_EX case, we set these vars from the passed structure, else we use the raw ptrs.
|
|
if (pUrlMapEx)
|
|
{
|
|
pszURL = pUrlMapEx->lpszPath;
|
|
*pdwSize = MAX_PATH;
|
|
}
|
|
else
|
|
{
|
|
pszURL = pszBuffer;
|
|
}
|
|
|
|
|
|
// To keep this like IIS, we translate "/" to "\". We do the conversion
|
|
// only if we're using filters or if there's enough space in the buffer.
|
|
if (g_pVars->m_fFilters || *pdwSize >= dwBufNeeded)
|
|
{
|
|
for (int i = 0; i < (int) wcslen(wszPath); i++)
|
|
{
|
|
if ( wszPath[i] == L'/')
|
|
wszPath[i] = L'\\';
|
|
}
|
|
}
|
|
|
|
if (FALSE == g_pVars->m_fFilters)
|
|
{
|
|
// We check to make sure buffer is the right size because WideToMultyByte
|
|
// will overwrite pieces of pszURL even on failure, leaving pszURL's
|
|
// content invalid
|
|
if (*pdwSize < dwBufNeeded)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
MyW2A(wszPath, pszURL, *pdwSize);
|
|
ret = TRUE;
|
|
}
|
|
*pdwSize = dwBufNeeded;
|
|
}
|
|
else
|
|
{
|
|
// for EX case, put original URL as optional 5th parameter.
|
|
ret = FilterMapURL(pszURL, wszPath, pdwSize,dwBufNeeded, pUrlMapEx ? pszBuffer : NULL);
|
|
}
|
|
|
|
if (ret && pUrlMapEx)
|
|
{
|
|
pUrlMapEx->cchMatchingPath = *pdwSize - 1; // don't count \0
|
|
pUrlMapEx->cchMatchingURL = strlen(pszBuffer);
|
|
pUrlMapEx->dwFlags = dwPermissions;
|
|
}
|
|
|
|
MyFree(wszPath);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void CISAPI::Unload(PWSTR wszDLLName)
|
|
{
|
|
if (m_pfnTerminate)
|
|
{
|
|
__try
|
|
{
|
|
if (SCRIPT_TYPE_FILTER == m_scriptType)
|
|
((PFN_TERMINATEFILTER)m_pfnTerminate)(HSE_TERM_MUST_UNLOAD);
|
|
else if (SCRIPT_TYPE_EXTENSION == m_scriptType)
|
|
((PFN_TERMINATEEXTENSION)m_pfnTerminate)(HSE_TERM_MUST_UNLOAD);
|
|
else if (SCRIPT_TYPE_ASP == m_scriptType)
|
|
((PFN_TERMINATEASP)m_pfnTerminate)();
|
|
|
|
}
|
|
__except(1)
|
|
{
|
|
DWORD dwExceptionCode = SCRIPT_TYPE_FILTER == m_scriptType ?
|
|
IDS_HTTPD_FILT_EXCEPTION : IDS_HTTPD_EXT_EXCEPTION;
|
|
PWSTR wszFunction = SCRIPT_TYPE_FILTER == m_scriptType ?
|
|
L"TerminateFilter" : L"TerminateExtension";
|
|
|
|
TraceTag(ttidWebServer, "HTTPD: TerminateExtension faulted");
|
|
g_pVars->m_pLog->WriteEvent(dwExceptionCode,wszDLLName ? wszDLLName : m_wszDLLName,
|
|
GetExceptionCode(),wszFunction,GetLastError());
|
|
}
|
|
}
|
|
MyFreeLib(m_hinst);
|
|
m_hinst = NULL;
|
|
m_pfnTerminate = NULL;
|
|
}
|
|
|
|
|
|
BOOL CISAPI::Load(PWSTR wszPath)
|
|
{
|
|
m_hinst = LoadLibrary(wszPath);
|
|
if (!m_hinst)
|
|
return FALSE;
|
|
|
|
if (SCRIPT_TYPE_ASP == m_scriptType)
|
|
{
|
|
if (! (m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("ExecuteASP"))))
|
|
goto error;
|
|
if (! (m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateASP"))))
|
|
goto error;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (SCRIPT_TYPE_EXTENSION == m_scriptType)
|
|
{
|
|
m_pfnGetVersion = GetProcAddress(m_hinst, CE_STRING("GetExtensionVersion"));
|
|
m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("HttpExtensionProc"));
|
|
m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateExtension"));
|
|
}
|
|
else if (SCRIPT_TYPE_FILTER == m_scriptType)
|
|
{
|
|
m_pfnGetVersion = GetProcAddress(m_hinst, CE_STRING("GetFilterVersion"));
|
|
m_pfnHttpProc = GetProcAddress(m_hinst, CE_STRING("HttpFilterProc"));
|
|
m_pfnTerminate = GetProcAddress(m_hinst, CE_STRING("TerminateFilter"));
|
|
|
|
}
|
|
|
|
if (!m_pfnHttpProc || !m_pfnGetVersion)
|
|
goto error;
|
|
|
|
__try
|
|
{
|
|
// call GetVersion immediately after load on extensions and Filters, but not ASP
|
|
// if it's a filter we need to do some flags work first
|
|
if (SCRIPT_TYPE_FILTER == m_scriptType)
|
|
{
|
|
HTTP_FILTER_VERSION vFilt;
|
|
// IIS ignores ISAPI version info, so do we.
|
|
((PFN_GETFILTERVERSION)m_pfnGetVersion)(&vFilt);
|
|
dwFilterFlags = vFilt.dwFlags;
|
|
|
|
// client didn't set the prio flags, assign them to default
|
|
// If they set more than one prio we use the highest + ignore others
|
|
if (0 == (dwFilterFlags & SF_NOTIFY_ORDER_MASK))
|
|
{
|
|
dwFilterFlags |= SF_NOTIFY_ORDER_DEFAULT;
|
|
}
|
|
}
|
|
else if (SCRIPT_TYPE_EXTENSION == m_scriptType)
|
|
{
|
|
HSE_VERSION_INFO vExt;
|
|
// IIS ignores ISAPI version info, so do we.
|
|
((PFN_GETEXTENSIONVERSION)m_pfnGetVersion)(&vExt);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
__except(1)
|
|
{
|
|
DWORD dwExceptionCode = SCRIPT_TYPE_FILTER == m_scriptType ?
|
|
IDS_HTTPD_FILT_EXCEPTION : IDS_HTTPD_EXT_EXCEPTION;
|
|
PWSTR wszFunction = SCRIPT_TYPE_FILTER == m_scriptType ?
|
|
L"GetFilterVersion" : L"GetExtensionVersion";
|
|
|
|
TraceTag(ttidWebServer, "HTTPD: GetExtensionVersion faulted");
|
|
g_pVars->m_pLog->WriteEvent(dwExceptionCode,wszPath,GetExceptionCode(),wszFunction,GetLastError());
|
|
}
|
|
// fall through to error
|
|
|
|
error:
|
|
m_pfnGetVersion = 0;
|
|
m_pfnHttpProc = 0;
|
|
m_pfnTerminate = 0;
|
|
MyFreeLib(m_hinst);
|
|
m_hinst = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//**********************************************************************
|
|
// ISAPI Caching functions
|
|
//**********************************************************************
|
|
|
|
|
|
|
|
HINSTANCE CISAPICache::Load(PWSTR wszDLLName, CISAPI **ppISAPI, SCRIPT_TYPE st)
|
|
{
|
|
DEBUG_CODE_INIT;
|
|
BOOL ret = FALSE;
|
|
PISAPINODE pTrav = NULL;
|
|
|
|
EnterCriticalSection(&m_CritSec);
|
|
|
|
for (pTrav = m_pHead; pTrav != NULL; pTrav = pTrav->m_pNext)
|
|
{
|
|
if ( 0 == lstrcmpi(pTrav->m_pISAPI->m_wszDLLName, wszDLLName))
|
|
{
|
|
TraceTag(ttidWebServer, "Found ISAPI dll in cache, name = %s, cur ref count = %d",
|
|
wszDLLName, pTrav->m_pISAPI->m_cRef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NULL == pTrav)
|
|
{
|
|
TraceTag(ttidWebServer, "ISAPI dll name = %s not found in cache, creating new entry",wszDLLName);
|
|
if (NULL == (pTrav = MyAllocNZ(ISAPINODE)))
|
|
myleave(1200);
|
|
|
|
if (NULL == (pTrav->m_pISAPI = new CISAPI(st)))
|
|
myleave(1201);
|
|
|
|
if (NULL == (pTrav->m_pISAPI->m_wszDLLName = MySzDupW(wszDLLName)))
|
|
myleave(1202);
|
|
|
|
if (! pTrav->m_pISAPI->Load(wszDLLName))
|
|
myleave(1203);
|
|
|
|
pTrav->m_pNext = m_pHead;
|
|
m_pHead = pTrav;
|
|
}
|
|
pTrav->m_pISAPI->m_cRef++;
|
|
|
|
|
|
*ppISAPI = pTrav->m_pISAPI;
|
|
ret = TRUE;
|
|
done:
|
|
if (!ret)
|
|
{
|
|
if (pTrav && pTrav->m_pISAPI)
|
|
{
|
|
MyFree(pTrav->m_pISAPI->m_wszDLLName);
|
|
delete pTrav->m_pISAPI;
|
|
}
|
|
|
|
MyFree(pTrav);
|
|
}
|
|
|
|
TraceTag(ttidWebServer, "CISAPICache::LoadISAPI failed, err = %d, GLE = 0x%08x",err,GetLastError());
|
|
LeaveCriticalSection(&m_CritSec);
|
|
|
|
if (ret)
|
|
return pTrav->m_pISAPI->m_hinst;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CHttpRequest::StartRemoveISAPICacheIfNeeded()
|
|
{
|
|
DEBUGCHK(g_pVars->m_fExtensions);
|
|
if (InterlockedCompareExchange(&g_pVars->m_fISAPICacheRunning,1,0) == 0)
|
|
{
|
|
TraceTag(ttidWebServer, "HTTPD: ExecuteISAPI: Creating RemoveUnusedISAPIs timer");
|
|
// We never stop the timer. This is handled on call to Thread Pool Shutdown
|
|
g_pVars->m_pThreadPool->StartTimer(RemoveUnusedISAPIs,0,CACHE_SLEEP_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
// lpv = 0 for thread that sleeps forever.
|
|
// lpv = 1 to remove all ISAPIs, called on shutdown.
|
|
DWORD WINAPI RemoveUnusedISAPIs(LPVOID lpv)
|
|
{
|
|
// We set lpv to 1 if we want to unload all ISAPIs during shutdown.
|
|
if (lpv)
|
|
{
|
|
g_pVars->m_pISAPICache->RemoveUnusedISAPIs(TRUE);
|
|
return 0;
|
|
}
|
|
|
|
if (g_pVars->m_pISAPICache->m_pHead != NULL)
|
|
g_pVars->m_pISAPICache->RemoveUnusedISAPIs(FALSE);
|
|
|
|
g_pVars->m_pThreadPool->StartTimer(RemoveUnusedISAPIs,0,CACHE_SLEEP_TIMEOUT);
|
|
return 0;
|
|
}
|
|
|
|
// to milliseconds (10 * 1000)
|
|
#define FILETIME_TO_MILLISECONDS ((__int64)10000L)
|
|
|
|
// fRemoveAll = TRUE ==> remove all ISAPI's, we're shutting down
|
|
// = FALSE ==> only remove ones who aren't in use and whose time has expired
|
|
|
|
void CISAPICache::RemoveUnusedISAPIs(BOOL fRemoveAll)
|
|
{
|
|
PISAPINODE pTrav = NULL;
|
|
PISAPINODE pFollow = NULL;
|
|
PISAPINODE pDelete = NULL;
|
|
SYSTEMTIME st;
|
|
__int64 ft;
|
|
|
|
EnterCriticalSection(&m_CritSec);
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st,(FILETIME*) &ft);
|
|
|
|
// Figure out what time it was g_pVars->m_dwCacheSleep milliseconds ago.
|
|
// Elements that haven't been used since then and that have no references
|
|
// are deleted.
|
|
|
|
ft -= FILETIME_TO_MILLISECONDS * g_pVars->m_dwCacheSleep;
|
|
|
|
|
|
for (pTrav = m_pHead; pTrav != NULL; )
|
|
{
|
|
if (fRemoveAll ||
|
|
(pTrav->m_pISAPI->m_cRef == 0 && pTrav->m_pISAPI->m_ftLastUsed < ft))
|
|
{
|
|
TraceTag(ttidWebServer, "Freeing unused ISAPI Dll %s",pTrav->m_pISAPI->m_wszDLLName);
|
|
|
|
pTrav->m_pISAPI->Unload();
|
|
delete pTrav->m_pISAPI;
|
|
|
|
if (pFollow)
|
|
pFollow->m_pNext = pTrav->m_pNext;
|
|
else
|
|
m_pHead = pTrav->m_pNext;
|
|
|
|
pDelete = pTrav;
|
|
pTrav = pTrav->m_pNext;
|
|
MyFree(pDelete);
|
|
}
|
|
else
|
|
{
|
|
if (pTrav->m_pISAPI->m_cRef == 0)
|
|
pTrav->m_pISAPI->CoFreeUnusedLibrariesIfASP();
|
|
|
|
pFollow = pTrav;
|
|
pTrav = pTrav->m_pNext;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&m_CritSec);
|
|
}
|