Leaked source code of windows server 2003
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.
 
 
 
 
 
 

977 lines
32 KiB

/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
rpcsrv.cpp
Abstract:
Implements the L-RPC server for the system Auto-Proxy Service.
Author:
Biao Wang (biaow) 10-May-2002
--*/
#include "wininetp.h"
#include <Rpcdce.h>
#include "apsvcdefs.h"
#include "apsvc.h"
#include "rpcsrv.h"
extern AUTOPROXY_RPC_SERVER* g_pRpcSrv;
#ifdef ENABLE_DEBUG
extern HKEY g_hKeySvcParams;
#endif
/*
This is the security callback function that we specified when we registered our RPC interface
during AUTOPROXY_RPC_SERVER::Open(). It will be called on every connect attempt by a client,
and in this context we need to make sure that the client call is via Local RPC from the local
machine.
*/
RPC_STATUS
RPC_ENTRY
RpcSecurityCallback (
IN RPC_IF_HANDLE InterfaceUuid,
IN void *Context
)
{
UNREFERENCED_PARAMETER(InterfaceUuid);
// todo: sanity checking on InterfaceUuid ?
if (g_pRpcSrv == NULL)
{
LOG_EVENT(AP_ERROR, MSG_WINHTTP_AUTOPROXY_SVC_DATA_CORRUPT);
return RPC_S_ACCESS_DENIED;
}
return g_pRpcSrv->OnSecurityCallback(Context);
}
RPC_STATUS AUTOPROXY_RPC_SERVER::OnSecurityCallback(void *Context)
{
UNREFERENCED_PARAMETER(Context);
RPC_STATUS RpcStatus;
// note: I_RpcBindingInqTransportType() is a no-yet-published API that RPC folks told me to use
// for better performance
unsigned int TransportType;
RpcStatus = ::I_RpcBindingInqTransportType(NULL, // test the current call
&TransportType);
if ((RpcStatus == RPC_S_OK) && (TransportType == TRANSPORT_TYPE_LPC))
{
return RPC_S_OK;
}
else
{
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"I_RpcBindingInqTransportType()",
RpcStatus);
}
if (TransportType != TRANSPORT_TYPE_LPC)
{
WCHAR wTransType[16] = {0};
::swprintf(wTransType, L"%d", TransportType);
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_NON_LRPC_REQUEST,
wTransType);
}
return RPC_S_ACCESS_DENIED;
}
}
/*
we registered this callback function to receive the internal BEGIN_PROXY_SCRIPT_RUN event if we
are impersonating a client. We need to revert the impersonation before we run the untrusted
proxy script code.
*/
VOID WinHttpStatusCallback(HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
UNREFERENCED_PARAMETER(lpvStatusInformation);
UNREFERENCED_PARAMETER(dwStatusInformationLength);
if (hInternet)
{
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_BEGIN_PROXY_SCRIPT_RUN)
{
// *note* Be aware that we are assuming this callback is coming in from the same
// thread that initiatedthe WinHttpGetProxyForUrl() call. Because of this
// assumption, we are accessing the local variables of the original call
// stck by pointers.
LPBOOL pfImpersonating = (LPBOOL)dwContext; // this is the address of fImpersonating local variable in
// AUTOPROXY_RPC_SERVER::GetProxyForUrl
if (*pfImpersonating)
{
RPC_STATUS RpcStatus = ::RpcRevertToSelf();
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcRevertToSelf()",
RpcStatus);
}
else
{
// LOG_DEBUG_EVENT(AP_WARNING,
// "[debug] L-RPC: GetProxyForUrl() now reverted impersonating (about to run unsafe script)");
*pfImpersonating = FALSE; // this sets the local variable "fImpersonating" inside
// AUTOPROXY_RPC_SERVER::GetProxyForUrl() to FALSE
}
}
}
}
}
AUTOPROXY_RPC_SERVER::AUTOPROXY_RPC_SERVER(VOID)
{
_fInService = FALSE;
_hSession = NULL;
// _hClientBinding = NULL;
_pServiceStatus = NULL;
_hExitEvent = NULL;
_fServiceIdle = TRUE;
_dwIdleTimeStamp = ::GetTickCount();
}
AUTOPROXY_RPC_SERVER::~AUTOPROXY_RPC_SERVER(VOID)
{
if (_hSession)
{
::WinHttpCloseHandle(_hSession);
}
if (_hExitEvent)
{
::CloseHandle(_hExitEvent);
}
}
/*
The Open() call registers w/ RPC runtime our Protocol Sequence (i.e. ncalrpc), the Auto-Proxy
Interface, and the end point, then it enters the listening mode and we are ready to accept
client requests. (upon successful security check)
*/
BOOL AUTOPROXY_RPC_SERVER::Open(LPSERVICE_STATUS pServiceStatus)
{
BOOL fRet = FALSE;
BOOL fServerRegistered = FALSE;
AP_ASSERT(pServiceStatus != NULL);
if (_pServiceStatus)
{
LOG_EVENT(AP_ERROR, MSG_WINHTTP_AUTOPROXY_SVC_DATA_CORRUPT);
goto exit;
}
_pServiceStatus = pServiceStatus;
if (InitializeSerializedList(&_PendingRequestList) == FALSE)
{
LOG_EVENT(AP_ERROR, MSG_WINHTTP_AUTOPROXY_SVC_FAILED_ALLOCATE_RESOURCE);
goto exit;
}
_RpcStatus = ::RpcServerUseProtseqW(AUTOPROXY_L_RPC_PROTOCOL_SEQUENCE,
0, // ignored for ncalrpc
NULL);
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcServerUseProtseqW()",
_RpcStatus);
goto exit;
}
_RpcStatus = ::RpcServerRegisterIf2(WINHTTP_AUTOPROXY_SERVICE_v5_1_s_ifspec, // MIDL-generated constant
NULL, // UUID
NULL, // EPV
RPC_IF_AUTOLISTEN,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
(unsigned int) -1, // no MaxRpcSize check
RpcSecurityCallback); // the callback will set _fInService to TRUE if access is granted
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcServerRegisterIf2()",
_RpcStatus);
goto exit;
}
fServerRegistered = TRUE;
RPC_BINDING_VECTOR* pBindingVector = NULL;
_RpcStatus = ::RpcServerInqBindings(&pBindingVector);
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcServerInqBindings()",
_RpcStatus);
goto exit;
}
_RpcStatus = ::RpcEpRegisterW(WINHTTP_AUTOPROXY_SERVICE_v5_1_s_ifspec,
pBindingVector,
NULL,
L"WinHttp Auto-Proxy Service");
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcEpRegisterW()",
_RpcStatus);
goto exit;
}
_RpcStatus = ::RpcBindingVectorFree(&pBindingVector);
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcBindingVectorFree()",
_RpcStatus);
goto exit;
}
_fInService = TRUE;
fRet = TRUE;
exit:
if (fRet == FALSE)
{
if (fServerRegistered)
{
_RpcStatus = ::RpcServerUnregisterIf(WINHTTP_AUTOPROXY_SERVICE_v5_1_s_ifspec,
NULL,
1 // wait for all RPC alls to complete
);
if (_RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcServerUnregisterIf()",
_RpcStatus);
}
}
}
return fRet;
}
/*
The Close() method first cancel all on-going requests, it then waits for all canceled calls to abort
gracefully
*/
BOOL AUTOPROXY_RPC_SERVER::Close(VOID)
{
RPC_STATUS RpcStatus;
BOOL fRet = TRUE;
Pause();
if (LockSerializedList(&_PendingRequestList))
{
if (!IsSerializedListEmpty(&_PendingRequestList))
{
_hExitEvent = ::CreateEvent(NULL,
TRUE, // manual reset
FALSE, // not initally set
NULL);
if (_hExitEvent == NULL)
{
DWORD dwError = ::GetLastError();
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"CreateEvent()",
dwError);
fRet = FALSE;
}
}
UnlockSerializedList(&_PendingRequestList);
if (_hExitEvent)
{
if (::WaitForSingleObject(_hExitEvent, AUTOPROXY_SERVICE_STOP_WAIT_HINT) == WAIT_TIMEOUT)
{
WCHAR wWaitHint[16] = {0};
::swprintf(wWaitHint, L"%d", AUTOPROXY_SERVICE_STOP_WAIT_HINT/1000);
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_TIMEOUT_GRACEFUL_SHUTDOWN,
wWaitHint);
fRet = FALSE;
}
::CloseHandle(_hExitEvent);
_hExitEvent = NULL;
}
}
else
{
// LOG_DEBUG_EVENT(AP_WARNING, "The Auto-Proxy Service failed to shutdown gracefully");
fRet = FALSE;
}
// if something goes wrong during close() we don't unregister L-RPC so that the service can be
// resumed later.
if (fRet == TRUE)
{
RpcStatus = ::RpcServerUnregisterIf(WINHTTP_AUTOPROXY_SERVICE_v5_1_s_ifspec,
NULL,
1 // wait for all RPC alls to complete
);
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcServerUnregisterIf()",
RpcStatus);
fRet = FALSE;
}
}
return fRet;
}
/*
This is the entry point of a RPC auto-proxy call. For each call request we create an object
keeping track of its states and queue it in the pending request list. Then we call the
WinHttpGetProxyForUrl() to resolve the proxy. At the end the object will be dequeued and
deleted, and, assuming the call is not cancled, we then complete the RPC call. Call can be
canceled by the client, or by SCM (e.g. service stop, system stand-by, and etc)
*/
/* [async] */ void GetProxyForUrl(
/* [in] */ PRPC_ASYNC_STATE GetProxyForUrl_AsyncHandle,
/* [in] */ handle_t hBinding,
/* [string][in] */ const wchar_t *pcwszUrl,
/* [in] */ const P_AUTOPROXY_OPTIONS pAutoProxyOptions,
/* [in] */ const P_SESSION_OPTIONS pSessionOptions,
/* [out][in] */ P_AUTOPROXY_RESULT pAutoProxyResult,
/* [out][in] */ unsigned long *pdwLastError)
{
g_pRpcSrv->GetProxyForUrl(GetProxyForUrl_AsyncHandle,
hBinding,
pcwszUrl,
pAutoProxyOptions,
pSessionOptions,
pAutoProxyResult,
pdwLastError);
}
VOID AUTOPROXY_RPC_SERVER::GetProxyForUrl(
/* [in] */ PRPC_ASYNC_STATE GetProxyForUrl_AsyncHandle,
/* [in] */ handle_t hBinding,
/* [string][in] */ const wchar_t *pcwszUrl,
/* [in] */ const P_AUTOPROXY_OPTIONS pAutoProxyOptions,
/* [in] */ const P_SESSION_OPTIONS pSessionOptions,
/* [out][in] */ P_AUTOPROXY_RESULT pAutoProxyResult,
/* [out][in] */ unsigned long *pdwLastError)
{
RPC_STATUS RpcStatus;
// LOG_DEBUG_EVENT(AP_INFO, "[debug] L-RPC: GetProxyForUrl() called; url=%wq", pcwszUrl);
if ((pdwLastError == NULL) || ::IsBadWritePtr(pdwLastError, sizeof(DWORD)) ||
(pAutoProxyOptions == NULL) || ::IsBadWritePtr(pAutoProxyOptions, sizeof(_AUTOPROXY_OPTIONS)) ||
(pSessionOptions == NULL) || ::IsBadWritePtr(pSessionOptions, sizeof(_SESSION_OPTIONS)))
{
// we call abort here because RpcAsyncCompleteCall() may not return LastError safely;
// pdwLastError may not point to valid memory
LOG_EVENT(AP_WARNING, MSG_WINHTTP_AUTOPROXY_SVC_INVALID_PARAMETER);
RpcStatus = ::RpcAsyncAbortCall(GetProxyForUrl_AsyncHandle,
ERROR_WINHTTP_INTERNAL_ERROR);
if (RpcStatus != RPC_S_OK)
{
// shoot, we failed to abort the call, something is really wrong here;
// all we can do is to raise an exception
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcAsyncAbortCall()",
RpcStatus);
::RpcRaiseException(RpcStatus);
}
return;
}
// note: the validation of pcwszUrl, and pAutoProxyResult is deferred
// to the WinHttpGetProxyForUrl() call
BOOL fRet = FALSE;
BOOL fImpersonating = FALSE;
BOOL fCallCancelled = FALSE;
BOOL fExitCritSec = FALSE;
LPBOOL pfImpersonate = &fImpersonating; // allow the callback frunction to modify this variable by reference
WINHTTP_PROXY_INFO ProxyInfo;
ProxyInfo.dwAccessType = 0;
ProxyInfo.lpszProxy = NULL;
ProxyInfo.lpszProxyBypass = NULL;
PENDING_CALL* pClientCall = NULL;
// we will be touching global states of AUTOPROXY_RPC_SERVER (e.g. _hSession, pending call list, and etc)
// so we acquire a critsec here.
if (LockSerializedList(&_PendingRequestList) == FALSE)
{
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_FAILED_ALLOCATE_RESOURCE);
*pdwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
fExitCritSec = TRUE;
_fServiceIdle = FALSE;
// since we are now in a critsec, we try-except the operations inside this critsec so that
// if one client call fails unexpectedly, other calls can still go thru.
HINTERNET* phSession = NULL;
RpcTryExcept
{
if (!_fInService)
{
LOG_EVENT(AP_WARNING, MSG_WINHTTP_AUTOPROXY_SVC_NOT_IN_SERVICE);
*pdwLastError = ERROR_WINHTTP_OPERATION_CANCELLED;
goto exit;
}
pClientCall = new PENDING_CALL;
if (pClientCall == NULL)
{
LOG_EVENT(AP_WARNING, MSG_WINHTTP_AUTOPROXY_SVC_FAILED_ALLOCATE_RESOURCE);
*pdwLastError = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
if (pAutoProxyOptions->fAutoLogonIfChallenged)
{
// the client is asking auto-logon, we must impersonate to use client's logon/default
// credential. However, by impersonating we also elevate the privileges of the auo-proxy
// service, so we are only impersonating only to download the auto-proxy resource file.
// once the wpad file is downloaded and before executing the java script, we will revert
// to self (LocalService)
RpcStatus = ::RpcImpersonateClient(NULL);
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcImpersonateClient()",
RpcStatus);
}
else
{
// LOG_DEBUG_EVENT(AP_WARNING, "[debug] L-RPC: GetProxyForUrl() now impersonating");
fImpersonating = TRUE;
}
}
// we maintain a per-call session handle for each impersonating client, because their states can not be shared.
// And for non-impersonating client, they share one global session.
phSession = fImpersonating ? &(pClientCall->hSession) : &_hSession;
if (*phSession == NULL)
{
*phSession = ::WinHttpOpen(L"WinHttp-Autoproxy-Service/5.1",
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (*phSession == NULL)
{
*pdwLastError = ::GetLastError();
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"WinHttpOpen()",
*pdwLastError);
goto exit;
}
::WinHttpSetTimeouts(*phSession,
pSessionOptions->nResolveTimeout,
pSessionOptions->nConnectTimeout,
pSessionOptions->nSendTimeout,
pSessionOptions->nReceiveTimeout);
::WinHttpSetOption(*phSession,
WINHTTP_OPTION_CONNECT_RETRIES,
&pSessionOptions->nConnectRetries,
sizeof(DWORD));
if (fImpersonating)
{
// we are impersonating, we need to setup a callback function indicating a proxy script
// is to be run, upon which we must revert impersonation.
AP_ASSERT((phSession == &(pClientCall->hSession)));
::WinHttpSetStatusCallback(*phSession,
WinHttpStatusCallback,
WINHTTP_CALLBACK_FLAG_BEGIN_PROXY_SCRIPT_RUN,
NULL);
::WinHttpSetOption(*phSession,
WINHTTP_OPTION_CONTEXT_VALUE,
&pfImpersonate,
sizeof(DWORD_PTR));
}
}
pClientCall->hAsyncRequest = GetProxyForUrl_AsyncHandle;
pClientCall->hBinding = hBinding;
pClientCall->pdwLastError = pdwLastError; // so that the SCM thread can cancel the call w/ LastError set
}
RpcExcept(1)
{
WCHAR wExceptCode[16] = {0};
::swprintf(wExceptCode, L"%d", ::RpcExceptionCode());
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WINHTTP_EXCEPTED,
wExceptCode);
*pdwLastError = ERROR_WINHTTP_INTERNAL_ERROR;
goto exit;
}
RpcEndExcept
UnlockSerializedList(&_PendingRequestList);
fExitCritSec = FALSE;
// it's possible that the Pause/Stop function pre-empts this call to abort
// all pending requests while the current call will go thru. we can hang
// on to the lock past this point, however since we won't hold the lock
// while calling WinHttpGetProxyForUrl(), that possibility is always there,
// and it will also complicate the retry logic. It's will not be end of the
// world so we don't worry that too much here.
retry: // upon a critical power standby event, all requests will be abandoned and re-attemtepd
fRet = FALSE;
if (phSession == &(pClientCall->hSession)) // we need to impersonate...
{
if (!fImpersonating) // ...but we are not impersonating!...
{
// it must be the case that the WinHttpCallback function has reverted impersonation
// since we are retrying, we need to turn it back on
RpcStatus = ::RpcImpersonateClient(NULL);
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_WARNING,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcImpersonateClient()",
RpcStatus);
}
else
{
// LOG_DEBUG_EVENT(AP_WARNING, "[debug] L-RPC: GetProxyForUrl() now impersonating");
fImpersonating = TRUE;
}
}
}
// queue up the request
if (InsertAtHeadOfSerializedList(&_PendingRequestList, &pClientCall->List) == FALSE)
{
LOG_EVENT(AP_WARNING, MSG_WINHTTP_AUTOPROXY_SVC_FAILED_ALLOCATE_RESOURCE);
*pdwLastError = ERROR_WINHTTP_INTERNAL_ERROR;
goto exit;
}
if (ProxyInfo.lpszProxy)
{
::GlobalFree((HGLOBAL)ProxyInfo.lpszProxy);
ProxyInfo.lpszProxy = NULL;
}
if (ProxyInfo.lpszProxyBypass)
{
::GlobalFree((HGLOBAL)ProxyInfo.lpszProxyBypass);
ProxyInfo.lpszProxyBypass = NULL;
}
// the WINHTTP_AUTOPROXY_RUN_INPROCESS flag should not have been set, otherwise
// we won't be here the first place
AP_ASSERT(!(pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_INPROCESS));
// we must set this flag because we are the service
pAutoProxyOptions->dwFlags |= WINHTTP_AUTOPROXY_RUN_INPROCESS;
DWORD dwSvcOnlyFlagSaved = (pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY);
pAutoProxyOptions->dwFlags &= ~dwSvcOnlyFlagSaved; // remove the WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY flag if present
fRet = ::WinHttpGetProxyForUrl(*phSession,
pcwszUrl,
(WINHTTP_AUTOPROXY_OPTIONS*)pAutoProxyOptions,
&ProxyInfo);
#ifdef ENABLE_DEBUG
DWORD dwSvcDelay = 0;
DWORD dwRegVal;
DWORD dwValType;
DWORD dwValSize = sizeof(dwRegVal);
if (::RegQueryValueExW(g_hKeySvcParams,
L"SvcDelay",
NULL,
&dwValType,
(LPBYTE)&dwRegVal,
&dwValSize) == ERROR_SUCCESS)
{
if ((dwValType == REG_DWORD) && (dwRegVal != 0))
{
dwSvcDelay = dwRegVal; // the value unit from registry is milli-sec.
}
}
::Sleep(dwSvcDelay);
#endif
// restore the flag
pAutoProxyOptions->dwFlags &= ~WINHTTP_AUTOPROXY_RUN_INPROCESS;
pAutoProxyOptions->dwFlags |= dwSvcOnlyFlagSaved;
// this is only place that removes pending call from the list
RemoveFromSerializedList(&_PendingRequestList, &pClientCall->List);
if (_hExitEvent)
{
// the service is shuting down
AP_ASSERT(_fInService == FALSE);
// once is list is empty, it will remain empty since not more call
// will be accepted; so we don't need to worry about race here
if (IsSerializedListEmpty(&_PendingRequestList))
{
::SetEvent(_hExitEvent);
}
}
if (pClientCall->fCallCancelled == TRUE) // call's been cancelled by SCM
{
fCallCancelled = TRUE;
goto exit;
}
// the client may have cancelled the call, let's check that
RpcStatus = ::RpcServerTestCancel(/*hBinding*/NULL);
if (RpcStatus == RPC_S_OK)
{
*pdwLastError = ERROR_WINHTTP_OPERATION_CANCELLED;
fRet = FALSE;
goto exit;
}
// also the Svc Control may have told us to discard the current result
// due to a critical power standby
if (pClientCall->fDiscardAndRetry)
{
LOG_EVENT(AP_INFO, MSG_WINHTTP_AUTOPROXY_SVC_RETRY_REQUEST);
pClientCall->fDiscardAndRetry = FALSE;
goto retry;
}
if (fRet == TRUE)
{
pAutoProxyResult->dwAccessType = ProxyInfo.dwAccessType;
// these two are [in,out,unique] pointer, so RpcAsyncCompleteCall() will
// make a copy of the strings and return to the client. so we need to
// delete the two strings to prevent memory leaks
pAutoProxyResult->lpszProxy = ProxyInfo.lpszProxy;
ProxyInfo.lpszProxy = NULL; // ownership transferred to RPC
pAutoProxyResult->lpszProxyBypass = ProxyInfo.lpszProxyBypass;
ProxyInfo.lpszProxyBypass = NULL; // ownership transferred to RPC
// LOG_DEBUG_EVENT(AP_INFO, "[debug] L-RPC: GetProxyForUrl() returning; proxy=%wq", pAutoProxyResult->lpszProxy);
}
else
{
*pdwLastError = ::GetLastError();
#ifdef ENABLE_DEBUG
//LOG_DEBUG_EVENT(AP_WARNING,
// "[debug] L-RPC: GetProxyForUrl(): WinHttpGetProxyForUrl() faled; error = %d",
// *pdwLastError);
#endif
}
exit:
if (fExitCritSec)
{
UnlockSerializedList(&_PendingRequestList);
}
if (pClientCall)
{
delete pClientCall;
pClientCall = NULL;
}
if (!fCallCancelled)
{
RpcStatus = ::RpcAsyncCompleteCall(GetProxyForUrl_AsyncHandle, &fRet);
if (RpcStatus != RPC_S_OK)
{
// we failed to complete the call; log an error and return. Not much we can do
// here
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcAsyncCompleteCall()",
RpcStatus);
}
}
if (fImpersonating)
{
RpcStatus = ::RpcRevertToSelf();
if (RpcStatus != RPC_S_OK)
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcRevertToSelf()",
RpcStatus);
}
else
{
// LOG_DEBUG_EVENT(AP_WARNING, "[debug] L-RPC: GetProxyForUrl() now reverted impersonating");
fImpersonating = FALSE;
}
}
if (ProxyInfo.lpszProxy)
{
::GlobalFree((HGLOBAL)ProxyInfo.lpszProxy);
ProxyInfo.lpszProxy = NULL;
}
if (ProxyInfo.lpszProxyBypass)
{
::GlobalFree((HGLOBAL)ProxyInfo.lpszProxyBypass);
ProxyInfo.lpszProxyBypass = NULL;
}
// if we don't have any requests pending, start up the idle timer; upon certain idle period
// the service will be shutdown
if (LockSerializedList(&_PendingRequestList))
{
if (IsSerializedListEmpty(&_PendingRequestList))
{
_fServiceIdle = TRUE;
_dwIdleTimeStamp = ::GetTickCount();
}
UnlockSerializedList(&_PendingRequestList);
}
}
BOOL AUTOPROXY_RPC_SERVER::IsIdle(DWORD dwMilliSeconds)
{
BOOL fRet = FALSE;
if (LockSerializedList(&_PendingRequestList))
{
if (_fServiceIdle)
{
DWORD dwElapsedTime = ::GetTickCount() - _dwIdleTimeStamp;
if (dwElapsedTime > dwMilliSeconds)
{
fRet = TRUE;
}
AP_ASSERT(IsSerializedListEmpty(&_PendingRequestList));
}
UnlockSerializedList(&_PendingRequestList);
}
return fRet;
}
/*
The Pause() function marks the service unavailable, abort call on going WinHttpGetProxyForUrl calls(), and then complete
all pending RPC client requests as OPERATION_CANCELLED.
*/
BOOL AUTOPROXY_RPC_SERVER::Pause(VOID)
{
BOOL fRet = FALSE;
if (_fInService)
{
if (LockSerializedList(&_PendingRequestList))
{
// no need to check _fInService again because this is the only thread that will set it to FALSE
_fInService = FALSE;
LOG_EVENT(AP_INFO, MSG_WINHTTP_AUTOPROXY_SVC_SUSPEND_OPERATION);
if (_hSession)
{
::WinHttpCloseHandle(_hSession); // close the global session, which will cause all anonymous calls to abort
_hSession = NULL;
}
PLIST_ENTRY pEntry;
for (pEntry = HeadOfSerializedList(&_PendingRequestList);
pEntry != (PLIST_ENTRY)SlSelf(&_PendingRequestList);
pEntry = pEntry->Flink)
{
PENDING_CALL* pPendingCall = (PENDING_CALL*)pEntry;
AP_ASSERT(pPendingCall != NULL);
AP_ASSERT(pPendingCall->pdwLastError != NULL);
*(pPendingCall->pdwLastError) = ERROR_WINHTTP_OPERATION_CANCELLED;
if (pPendingCall->hSession)
{
::WinHttpCloseHandle(pPendingCall->hSession); // abort his impersonating call
pPendingCall->hSession = NULL;
}
BOOL fRpcRet = FALSE;
AP_ASSERT(pPendingCall->hAsyncRequest != NULL);
RPC_STATUS RpcStatus = ::RpcAsyncCompleteCall(pPendingCall->hAsyncRequest, &fRpcRet);
if ((RpcStatus == RPC_S_OK) || (RpcStatus == RPC_S_CALL_CANCELLED))
{
pPendingCall->fCallCancelled = TRUE;
}
else
{
LOG_EVENT(AP_ERROR,
MSG_WINHTTP_AUTOPROXY_SVC_WIN32_ERROR,
L"RpcAsyncCompleteCall()",
RpcStatus);
}
}
UnlockSerializedList(&_PendingRequestList);
fRet = TRUE;
}
}
else
{
fRet = TRUE;
}
return fRet;
}
BOOL AUTOPROXY_RPC_SERVER::Resume(VOID)
{
_fInService = TRUE;
LOG_EVENT(AP_INFO, MSG_WINHTTP_AUTOPROXY_SVC_RESUME_OPERATION);
return TRUE;
}
/*
The Refresh() function marks all pending requests to "discard-and-retry", later when they are completed normally,
their results will be discarded and operations retried. This function is called after resuming from a critical
power event.
*/
BOOL AUTOPROXY_RPC_SERVER::Refresh(VOID)
{
BOOL fRet = FALSE;
if (_fInService)
{
if (LockSerializedList(&_PendingRequestList))
{
// no need to check _fInService again because this is the only thread that will set it to FALSE
PLIST_ENTRY pEntry;
for (pEntry = HeadOfSerializedList(&_PendingRequestList);
pEntry != (PLIST_ENTRY)SlSelf(&_PendingRequestList);
pEntry = pEntry->Flink)
{
PENDING_CALL* pPendingCall = (PENDING_CALL*)pEntry;
AP_ASSERT(pPendingCall != NULL);
pPendingCall->fDiscardAndRetry = TRUE;
}
UnlockSerializedList(&_PendingRequestList);
fRet = TRUE;
}
}
else
{
fRet = TRUE;
}
return fRet;
}
/******************************************************/
/* MIDL allocate and free */
/******************************************************/
void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
return(GlobalAlloc(GPTR, len));
}
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
GlobalFree(ptr);
}