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.
5052 lines
127 KiB
5052 lines
127 KiB
/*++
|
|
|
|
Copyright (c) 1995-1997 Microsoft Corporation
|
|
|
|
Module Name :
|
|
wamreq.cxx
|
|
|
|
Abstract:
|
|
This module implements the WAM_REQUEST object
|
|
|
|
Author:
|
|
David Kaplan ( DaveK ) 26-Feb-1997
|
|
|
|
Environment:
|
|
User Mode - Win32
|
|
|
|
Project:
|
|
W3 services DLL
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
#include <w3p.hxx>
|
|
#include "wamreq.hxx"
|
|
#include "iwr_i.c"
|
|
#include "WrcStIds.hxx" // wamreq core string id's
|
|
#include "WrcFixed.hxx"
|
|
#include "WamExec.hxx"
|
|
#include "lonsi.hxx" // IISDuplicateHandleEx
|
|
#include <tokenacl.hxx>
|
|
#include <malloc.h>
|
|
|
|
|
|
/*******************************************
|
|
* *
|
|
* WAM_REQUEST allocation cache *
|
|
* *
|
|
* *
|
|
*******************************************/
|
|
|
|
ALLOC_CACHE_HANDLER * WAM_REQUEST::sm_pachWamRequest;
|
|
# define WAM_REQUEST_CACHE_THRESHOLD (400) // UNDONE: Empirically vary
|
|
|
|
#if DBG
|
|
extern PTRACE_LOG g_pDbgCCRefTraceLog;
|
|
PTRACE_LOG WAM_REQUEST::sm_pDbgRefTraceLog;
|
|
#endif
|
|
|
|
DWORD WAM_REQUEST::sm_dwRequestID;
|
|
|
|
inline BOOL
|
|
DupTokenWithSameImpersonationLevel
|
|
(
|
|
HANDLE hExistingToken,
|
|
DWORD dwDesiredAccess,
|
|
TOKEN_TYPE TokenType,
|
|
PHANDLE phNewToken
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Duplicate an impersionation token using the same ImpersonationLevel.
|
|
|
|
Arguments:
|
|
|
|
hExistingToken - a handle to a valid impersionation token
|
|
dwDesiredAccess - the access level to the new token (see DuplicateTokenEx)
|
|
phNewToken - ptr to the new token handle, client must CloseHandle.
|
|
|
|
Return Value:
|
|
|
|
Return value of DuplicateTokenEx
|
|
|
|
--*/
|
|
{
|
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|
DWORD dwBytesReturned;
|
|
|
|
if( !GetTokenInformation( hExistingToken,
|
|
TokenImpersonationLevel,
|
|
&ImpersonationLevel,
|
|
sizeof(ImpersonationLevel),
|
|
&dwBytesReturned
|
|
) )
|
|
{
|
|
DBGERROR(( DBG_CONTEXT,
|
|
"GetTokenInformation - failed to get TokenImpersonationLevel "
|
|
"LastError=%d, using SecurityImpersonation\n",
|
|
GetLastError()
|
|
));
|
|
|
|
ImpersonationLevel = SecurityImpersonation;
|
|
}
|
|
|
|
return DuplicateTokenEx( hExistingToken,
|
|
dwDesiredAccess,
|
|
NULL,
|
|
ImpersonationLevel,
|
|
TokenType,
|
|
phNewToken
|
|
);
|
|
}
|
|
|
|
|
|
|
|
BOOL IsStringTerminated( LPCSTR lpSz, DWORD cchMax )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies that the range of memory specified by the
|
|
input parameters can be read by the calling process and that
|
|
the string is zero-terminated.
|
|
|
|
Note that since Win32 is a pre-emptive multi-tasking environment,
|
|
the results of this test are only meaningful if the other threads in
|
|
the process do not manipulate the range of memory being tested by
|
|
this call. Even after a pointer validation, an application should
|
|
use the structured exception handling capabilities present in the
|
|
system to gaurd access through pointers that it does not control.
|
|
|
|
Arguments:
|
|
|
|
lpsz - Supplies the base address of the memory that is to be checked
|
|
for read access and termination.
|
|
|
|
cchMax - Supplies the length in bytes to be checked (including terminator).
|
|
|
|
Return Value:
|
|
|
|
TRUE - All bytes in the specified range are readable and the string is
|
|
zero-terminated (or NULL pointer)
|
|
|
|
FALSE - Either memory is not readable or there is no zero terminator.
|
|
|
|
--*/
|
|
{
|
|
BOOL fSuccess = TRUE;
|
|
DWORD i;
|
|
|
|
//
|
|
// We consider NULL pointer to be terminated
|
|
//
|
|
|
|
if( lpSz == NULL || cchMax == 0) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Any non-NULL pointer we interrogate
|
|
//
|
|
|
|
__try {
|
|
|
|
for( i = 0; i < cchMax && lpSz[i]; i++ ) {
|
|
}
|
|
|
|
return (i < cchMax);
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*******************************************
|
|
* *
|
|
* Local WAM_REQUEST methods *
|
|
* *
|
|
* *
|
|
*******************************************/
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::WAM_REQUEST
|
|
Constructor.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
*/
|
|
WAM_REQUEST::WAM_REQUEST(
|
|
HTTP_REQUEST * pHttpRequest
|
|
, EXEC_DESCRIPTOR * pExec
|
|
, CWamInfo * pWamInfo
|
|
)
|
|
:
|
|
m_dwSignature ( WAM_REQUEST_SIGNATURE )
|
|
, m_pHttpRequest ( pHttpRequest )
|
|
, m_pWamInfo ( pWamInfo )
|
|
, m_dwWamVersion ( 0 )
|
|
, m_pWamExecInfo ( NULL )
|
|
, m_pExec ( pExec )
|
|
, m_cRefs ( 0 )
|
|
, m_hFileTfi ( INVALID_HANDLE_VALUE )
|
|
, m_fWriteHeaders ( TRUE )
|
|
, m_fFinishWamRequest ( FALSE )
|
|
, m_pUnkFTM (NULL)
|
|
, m_pszStatusTfi (NULL)
|
|
, m_pszTailTfi (NULL)
|
|
, m_pszHeadTfi (NULL)
|
|
, m_fExpectCleanup (FALSE)
|
|
, m_fPrepCleanupCalled (FALSE)
|
|
, m_fCleanupCalled (FALSE)
|
|
, m_pbAsyncReadOopBuffer(NULL)
|
|
{
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pWamInfo );
|
|
|
|
|
|
//
|
|
// init list pointers
|
|
//
|
|
|
|
InitializeListHead(&m_leOOP);
|
|
|
|
//
|
|
// bind to our httpreq
|
|
//
|
|
|
|
BindHttpRequest();
|
|
|
|
#if DBG
|
|
//
|
|
// produce precise request ID for debbuging/perf work
|
|
//
|
|
m_dwRequestID = InterlockedIncrement( (LONG *) &sm_dwRequestID );
|
|
#else
|
|
//
|
|
// we can live with approximately correct result
|
|
// (we don't want to pay the price of InterlockedIncrement)
|
|
//
|
|
m_dwRequestID = sm_dwRequestID++;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::~WAM_REQUEST
|
|
Destructor.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
*/
|
|
WAM_REQUEST::~WAM_REQUEST(
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
//
|
|
// If waminfo is oop, then this call removes this from the active
|
|
// list of wamreqests.
|
|
//
|
|
m_pWamInfo->LeaveOOPZone(this, TRUE);
|
|
|
|
m_pWamInfo->Dereference();
|
|
|
|
//
|
|
// if not a child request, take cleanup actions
|
|
//
|
|
// NOTE we don't take cleanup actions on a child request
|
|
// because these actions are inherently 'connection-oriented',
|
|
// i.e. they should only be done once per client connection
|
|
// (for example, could result in disconnecting the client, etc)
|
|
//
|
|
|
|
if( DoCleanupOnDestroy() && !IsChild() )
|
|
{
|
|
//
|
|
// Write log record.
|
|
//
|
|
|
|
m_pHttpRequest->WriteLogRecord();
|
|
|
|
|
|
//
|
|
// We must later call FinishWamRequest() from destructor
|
|
// if we are in non-error state - set m_fFinishWamRequest
|
|
// to tell us if we need to do this.
|
|
//
|
|
// See bug 109159.
|
|
//
|
|
// NOTE only here (and NOT in PrepCleanupWamRequest), do we
|
|
// potentially set flag true. This is because mainline thread,
|
|
// which only calls PrepCleanupWamRequest, has its own restart
|
|
// logic, hence should never call FinishWamRequest().
|
|
//
|
|
|
|
|
|
m_fFinishWamRequest = (m_pHttpRequest->QueryIOStatus() == NO_ERROR);
|
|
|
|
|
|
IF_DEBUG( ERROR ) {
|
|
|
|
if ( !m_fFinishWamRequest ){
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::CleanupWamRequest "
|
|
"async i/o failed "
|
|
"m_pHttpRequest[%p] "
|
|
"pClientConn[%p] "
|
|
"m_pHttpRequest->QueryIOStatus()(%d) "
|
|
"\n"
|
|
, this
|
|
, m_pHttpRequest
|
|
, m_pHttpRequest->QueryClientConn()
|
|
, m_pHttpRequest->QueryIOStatus()
|
|
));
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// ********* THREAD RACE ALERT ************************************
|
|
//
|
|
// We do our final cleanup in this EXACT order:
|
|
//
|
|
// 1) unbind that which must be unbound before we can finish
|
|
// (i.e. that which would cause a race on the httpreq)
|
|
//
|
|
// 2) if required, finish the request
|
|
//
|
|
// 3) unbind that which was required to finish the request
|
|
//
|
|
// ********* ANY OTHER USE VOIDS WARRANTY *************************
|
|
//
|
|
|
|
UnbindBeforeFinish();
|
|
|
|
if ( m_fFinishWamRequest ) {
|
|
|
|
FinishWamRequest();
|
|
}
|
|
|
|
UnbindAfterFinish();
|
|
|
|
|
|
//
|
|
// Close file handle used for TransmitFile ops
|
|
// NOTE normally, handle will be closed (and invalid) by now.
|
|
// We do one last check to cover potential error cases.
|
|
//
|
|
|
|
if ( m_hFileTfi != INVALID_HANDLE_VALUE ) {
|
|
|
|
DBG_ASSERT( !(m_pWamInfo->FInProcess()) );
|
|
CloseHandle( m_hFileTfi );
|
|
m_hFileTfi = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Ditto for TFI strings (if any)
|
|
//
|
|
|
|
if ( m_pszStatusTfi != NULL )
|
|
{
|
|
LocalFree( m_pszStatusTfi );
|
|
m_pszStatusTfi = NULL;
|
|
}
|
|
|
|
if ( m_pszTailTfi != NULL )
|
|
{
|
|
LocalFree( m_pszTailTfi );
|
|
m_pszTailTfi = NULL;
|
|
}
|
|
|
|
if ( m_pszHeadTfi != NULL )
|
|
{
|
|
LocalFree( m_pszHeadTfi );
|
|
m_pszHeadTfi = NULL;
|
|
}
|
|
|
|
//
|
|
// At last possible moment, right before we free the memory,
|
|
// set various object state that is useful for debugging
|
|
//
|
|
|
|
// set this object's signature to its 'free' value
|
|
m_dwSignature = WAM_REQUEST_SIGNATURE_FREE;
|
|
|
|
#if DBG
|
|
// write thread id into second dword of this object's memory
|
|
// (alloc cache stores next-ptr in 1st dword, so we use 2nd slot)
|
|
*( (DWORD *)this + 1 ) = GetCurrentThreadId();
|
|
|
|
#endif
|
|
if (m_pUnkFTM != NULL)
|
|
{
|
|
m_pUnkFTM->Release();
|
|
}
|
|
|
|
m_leOOP.Flink = NULL;
|
|
m_leOOP.Blink = NULL;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::InitWamRequest
|
|
Initializes WAM_REQUEST object.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::InitWamRequest(
|
|
const STR * pstrPath
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = CoCreateFreeThreadedMarshaler(this, &m_pUnkFTM);
|
|
|
|
if ( FAILED(hr) ) {
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// copy dll path and path-trans
|
|
//
|
|
|
|
if( !m_strISADllPath.Copy( *pstrPath ) ) {
|
|
|
|
return HresultFromBool( FALSE );
|
|
}
|
|
|
|
|
|
if( !SetPathTrans() ) {
|
|
|
|
return HresultFromBool( FALSE );
|
|
}
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
if( !m_pHttpRequest->GetInfo(
|
|
"HTTP_USER_AGENT",
|
|
&m_strUserAgent
|
|
) )
|
|
{
|
|
m_strUserAgent.Reset();
|
|
}
|
|
|
|
if( !m_pHttpRequest->GetInfo(
|
|
"HTTP_COOKIE",
|
|
&m_strCookie
|
|
) )
|
|
{
|
|
m_strCookie.Reset();
|
|
}
|
|
|
|
// build the ExpireHeader
|
|
|
|
LPSTR lpszExpires = NULL;
|
|
CHAR achTime[128];
|
|
|
|
// obtain a pointer to the MetaData object
|
|
|
|
PW3_METADATA pMetaData = m_pHttpRequest->QueryMetaData();
|
|
ASSERT(pMetaData);
|
|
|
|
// switch on the mode. One of STATIC or DYNAMIC
|
|
|
|
switch(pMetaData->QueryExpireMode()) {
|
|
|
|
// if STATIC, the ExpireHeader is already built up
|
|
|
|
case EXPIRE_MODE_STATIC :
|
|
|
|
lpszExpires = m_pHttpRequest->QueryExpireHeader();
|
|
break;
|
|
|
|
// if DYNAMIC, then the ExpireHeader must be built here.
|
|
// The MetaData has the ExpireDelta which is added to the
|
|
// current time and built up into a full Expires header.
|
|
|
|
case EXPIRE_MODE_DYNAMIC :
|
|
|
|
DWORD dwDelta = pMetaData->QueryExpireDelta();
|
|
SYSTEMTIME SysTime;
|
|
char *p = achTime;
|
|
|
|
::IISGetCurrentTimeAsSystemTime( &SysTime );
|
|
|
|
strcpy(p, "Expires: ");
|
|
|
|
p += (sizeof("Expires: ") - 1);
|
|
|
|
// if SystemTimeToGMTEx fails, silently fail leaving
|
|
// lpszExpires as NULL
|
|
|
|
if ( ::SystemTimeToGMTEx( SysTime,
|
|
p,
|
|
sizeof(achTime),
|
|
dwDelta ))
|
|
{
|
|
lpszExpires = achTime;
|
|
p += strlen(p);
|
|
strcpy(p, "\r\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( lpszExpires ) {
|
|
m_strExpires.Copy( lpszExpires );
|
|
} else {
|
|
m_strExpires.Reset();
|
|
}
|
|
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::BindHttpRequest
|
|
"Binds" this wamreq to its associated httpreq and refs the httpreq.
|
|
Bind ==
|
|
1) ref httpreq
|
|
2) set this wamreq's pointer into httpreq
|
|
3) increment httpreq stats counter
|
|
|
|
NOTE we invert this function in two phases, with UnbindBeforeFinish
|
|
and UnbindAfterFinish.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
*/
|
|
VOID
|
|
WAM_REQUEST::BindHttpRequest(
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
DBG_ASSERT( m_pExec );
|
|
|
|
|
|
//
|
|
// set httpreq's wamreq ptr to this wamreq
|
|
// and ref httpreq (which actually refs client-conn)
|
|
//
|
|
// NOTE we set wamreq ptr first so that it will show
|
|
// up in client-conn's ref trace log.
|
|
//
|
|
|
|
DBG_ASSERT( m_pHttpRequest->QueryWamRequest() == NULL );
|
|
m_pHttpRequest->SetWamRequest( this );
|
|
|
|
m_pHttpRequest->Reference();
|
|
|
|
|
|
//
|
|
// Increment stats counter.
|
|
//
|
|
|
|
m_pHttpRequest->QueryW3StatsObj()->IncrBGIRequests();
|
|
|
|
|
|
//
|
|
// The child request completion event is set in Unbind().
|
|
// Here in Bind() the fact that we got this far means that
|
|
// Unbind() will get executed, and thus we mark the child
|
|
// compeletion as 'must wait for'.
|
|
//
|
|
|
|
if (IsChild()) {
|
|
|
|
m_pExec->SetMustWaitForChildEvent();
|
|
}
|
|
|
|
|
|
} // WAM_REQUEST::BindHttpRequest
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::UnbindBeforeFinish
|
|
"Unbinds" the parts of the httpreq and its embedded exec descriptor
|
|
which ***MUST*** be done before attempting to finish the request.
|
|
|
|
NOTE include in this function any operation which would cause a race
|
|
on the httpreq if we did it after another thread gets the httpreq.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
VOID
|
|
WAM_REQUEST::UnbindBeforeFinish(
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest != NULL );
|
|
DBG_ASSERT( m_pExec != NULL );
|
|
|
|
//
|
|
// null out the wamreq ptr stored in httpreq,
|
|
//
|
|
|
|
DBG_ASSERT( m_pHttpRequest->QueryWamRequest() == this );
|
|
m_pHttpRequest->SetWamRequest( NULL );
|
|
|
|
|
|
return;
|
|
|
|
} // WAM_REQUEST::UnbindBeforeFinish()
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::UnbindAfterFinish
|
|
"Unbinds" the parts of the httpreq and its embedded exec descriptor
|
|
which are required to finish the request, and which hence
|
|
may not be done until after attempting to finish the request.
|
|
|
|
NOTE ***DO NOT*** include in this function any operation which
|
|
would cause a race on the httpreq if we did it after another thread
|
|
gets the httpreq.
|
|
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
*/
|
|
VOID
|
|
WAM_REQUEST::UnbindAfterFinish(
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest != NULL );
|
|
|
|
//
|
|
// Decrement stats counter.
|
|
//
|
|
|
|
m_pHttpRequest->QueryW3StatsObj()->DecrCurrentBGIRequests();
|
|
|
|
|
|
//
|
|
// If this is a child request, set its event,
|
|
// which allows parent request to continue processing
|
|
//
|
|
|
|
if ( IsChild() ) {
|
|
|
|
m_pExec->SetChildEvent();
|
|
}
|
|
|
|
|
|
//
|
|
// Now that we are done with exec, null out our ptr
|
|
//
|
|
|
|
m_pExec = NULL;
|
|
|
|
|
|
DBG_ASSERT( m_pWamInfo != NULL );
|
|
|
|
//
|
|
// If this is a crashed oop request, disconnect
|
|
//
|
|
// NOTE crashed ==> current wam version differs from
|
|
// the one we cached in this wamreq when it was created
|
|
//
|
|
|
|
if ( m_pWamInfo->FInProcess() == FALSE ) {
|
|
|
|
if ( ((CWamInfoOutProc *)m_pWamInfo)
|
|
->GetWamVersion() != m_dwWamVersion ) {
|
|
|
|
//
|
|
// NOTE this is safe even if we have already called Disconnect()
|
|
// because Disconnect() no-ops if it was already called
|
|
//
|
|
|
|
m_pHttpRequest->Disconnect(
|
|
0
|
|
, NO_ERROR
|
|
, TRUE // fShutdown
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now that we are done with httpreq, deref it and null out our ptr
|
|
//
|
|
|
|
DereferenceConn( m_pHttpRequest->QueryClientConn() );
|
|
m_pHttpRequest = NULL;
|
|
|
|
} // WAM_REQUEST::UnbindAfterFinish
|
|
|
|
VOID
|
|
WAM_REQUEST::DisconnectOnServerError(
|
|
DWORD dwHTHeader,
|
|
DWORD dwError
|
|
)
|
|
{
|
|
m_pHttpRequest->Disconnect(dwHTHeader, dwError);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::FinishWamRequest
|
|
|
|
Description:
|
|
Finishes this wam request - should only be call from destructor,
|
|
and only if cleanup was done by isapi callback thread.
|
|
|
|
ANY OTHER USE VOIDS WARRANTY.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Nothing
|
|
|
|
*/
|
|
VOID
|
|
WAM_REQUEST::FinishWamRequest(
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
BOOL fDoAgain = FALSE; // do we need to post completion status?
|
|
|
|
|
|
if ( !m_pHttpRequest->IsKeepConnSet() ) {
|
|
|
|
//
|
|
// If keep-alive is not set, disconnect.
|
|
//
|
|
|
|
m_pHttpRequest->Disconnect( 0, NO_ERROR, TRUE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If keep-alive is set, start up a new session
|
|
//
|
|
|
|
if ( !m_pHttpRequest->QueryClientConn()->OnSessionStartup(
|
|
&fDoAgain ) ) {
|
|
|
|
//
|
|
// Disconnect with error if startup failed
|
|
//
|
|
|
|
m_pHttpRequest->Disconnect(
|
|
HT_SERVER_ERROR
|
|
, GetLastError()
|
|
, TRUE
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If OnSessionStartup returned do-again TRUE,
|
|
// post completion status.
|
|
//
|
|
|
|
if ( fDoAgain ) {
|
|
|
|
if ( !m_pHttpRequest->QueryClientConn()->PostCompletionStatus(
|
|
m_pHttpRequest->QueryBytesWritten() ) ) {
|
|
|
|
//
|
|
// Disconnect with error if post failed
|
|
//
|
|
|
|
m_pHttpRequest->Disconnect(
|
|
HT_SERVER_ERROR
|
|
, GetLastError()
|
|
, TRUE
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} // WAM_REQUEST::FinishWamRequest()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::QueryInterface
|
|
COM implementation
|
|
|
|
Arguments:
|
|
The usual ones.
|
|
|
|
Returns:
|
|
HRESULT
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::QueryInterface
|
|
(
|
|
REFIID riid,
|
|
void __RPC_FAR *__RPC_FAR *ppvObject
|
|
)
|
|
{
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
DBG_ASSERT( ppvObject );
|
|
|
|
*ppvObject = NULL;
|
|
|
|
IF_DEBUG( IID )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST::QueryInterface looking for ... ( " GUID_FORMAT " )\n",
|
|
GUID_EXPAND( &riid) ));
|
|
}
|
|
|
|
if( riid == IID_IWamRequest )
|
|
{
|
|
*ppvObject = static_cast<IWamRequest *>( this );
|
|
}
|
|
else if ( riid == IID_IMarshal )
|
|
{
|
|
if (m_pUnkFTM == NULL)
|
|
{
|
|
DBG_ASSERT(FALSE);
|
|
return E_NOINTERFACE;
|
|
}
|
|
else
|
|
{
|
|
return m_pUnkFTM->QueryInterface(riid, ppvObject);
|
|
}
|
|
}
|
|
else if( riid == IID_IUnknown )
|
|
{
|
|
*ppvObject = static_cast<IWamRequest *>( this );
|
|
}
|
|
else if (m_pUnkFTM != NULL)
|
|
{
|
|
return m_pUnkFTM->QueryInterface(riid, ppvObject);
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
DBG_ASSERT( *ppvObject );
|
|
((IUnknown *)*ppvObject)->AddRef();
|
|
|
|
IF_DEBUG( IID )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST::QueryInterface found ( " GUID_FORMAT ", %p )\n",
|
|
GUID_EXPAND( &riid),
|
|
*ppvObject ));
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*
|
|
|
|
Support for debug ref trace logging
|
|
*/
|
|
|
|
#define WR_LOG_REF_COUNT( cRefs ) \
|
|
\
|
|
if( sm_pDbgRefTraceLog != NULL ) { \
|
|
\
|
|
WriteRefTraceLogEx( \
|
|
sm_pDbgRefTraceLog \
|
|
, cRefs \
|
|
, (PVOID) this \
|
|
, m_pHttpRequest \
|
|
, m_pExec \
|
|
, m_pHttpRequest \
|
|
? m_pHttpRequest->QueryClientConn() \
|
|
: NULL \
|
|
); \
|
|
} \
|
|
|
|
|
|
#define WR_SHARED_LOG_REF_COUNT( cRefs ) \
|
|
\
|
|
SHARED_LOG_REF_COUNT( \
|
|
cRefs \
|
|
, m_pHttpRequest \
|
|
? m_pHttpRequest->QueryClientConn() \
|
|
: NULL \
|
|
, m_pHttpRequest \
|
|
, this \
|
|
, m_pHttpRequest \
|
|
? m_pHttpRequest->QueryState() \
|
|
: NULL \
|
|
); \
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::AddRef
|
|
Addrefs wamreq
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Refcount
|
|
*/
|
|
ULONG
|
|
WAM_REQUEST::AddRef( )
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( BGI ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST(%p) AddRef: %d -> %d\n"
|
|
, this
|
|
, m_cRefs
|
|
, m_cRefs + 1
|
|
));
|
|
}
|
|
|
|
|
|
LONG cRefs = InterlockedIncrement( &m_cRefs );
|
|
|
|
|
|
#if DBG
|
|
//
|
|
// Write to both wamreq trace log and shared trace log
|
|
//
|
|
|
|
WR_LOG_REF_COUNT( cRefs );
|
|
WR_SHARED_LOG_REF_COUNT( cRefs );
|
|
|
|
#endif
|
|
|
|
return cRefs;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::Release
|
|
Releases wamreq
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Refcount
|
|
*/
|
|
ULONG
|
|
WAM_REQUEST::Release( )
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( BGI ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST(%p) Release: %d -> %d\n"
|
|
, this
|
|
, m_cRefs
|
|
, m_cRefs - 1
|
|
));
|
|
}
|
|
|
|
#if DBG
|
|
|
|
LONG cRefsAfter = m_cRefs - 1;
|
|
|
|
//
|
|
// Write to both wamreq trace log and shared trace log
|
|
//
|
|
// NOTE write the trace log BEFORE the decrement operation :(
|
|
// If we write it after the decrement, we will run into potential
|
|
// race conditions in this object getting freed up accidentally
|
|
// by another thread
|
|
//
|
|
// NOTE we write ref count AFTER decrement happens
|
|
//
|
|
|
|
WR_LOG_REF_COUNT( cRefsAfter );
|
|
WR_SHARED_LOG_REF_COUNT( cRefsAfter );
|
|
|
|
#endif
|
|
|
|
LONG cRefs = InterlockedDecrement( &m_cRefs );
|
|
|
|
|
|
if( cRefs == 0 ) {
|
|
|
|
IF_DEBUG( BGI ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST(%p) dying when ref = %d.\n"
|
|
, this
|
|
, cRefs
|
|
));
|
|
}
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return cRefs;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*
|
|
// Interlocked Increment only if non zero
|
|
//
|
|
// Returns 0 or value after increment
|
|
//
|
|
*/
|
|
LONG
|
|
WAM_REQUEST::InterlockedNonZeroAddRef(VOID)
|
|
{
|
|
while (TRUE)
|
|
{
|
|
LONG l = m_cRefs;
|
|
|
|
if (l == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (g_pfnInterlockedCompareExchange(&m_cRefs, l+1, l) == l)
|
|
{
|
|
return l+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::WriteLogInfo
|
|
Writes information to the IIS log.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
BOOL
|
|
WAM_REQUEST::WriteLogInfo(
|
|
CHAR * szLogMessage
|
|
, DWORD dwLogHttpResponse
|
|
, DWORD dwLogWinError
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
if ( m_pHttpRequest == NULL ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if ( !m_pHttpRequest->AppendLogParameter( szLogMessage ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
m_pHttpRequest->SetLogStatus( dwLogHttpResponse,dwLogWinError );
|
|
|
|
|
|
return m_pHttpRequest->WriteLogRecord();
|
|
|
|
} // WAM_REQUEST::WriteLogInfo()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
|
|
NOTE on PrepCleanupWamRequest and CleanupWamRequest:
|
|
|
|
Our cleanup policy depends on whether the request gets cleaned up by
|
|
the mainline thread or the isapi callback thread.
|
|
|
|
If the mainline thread does cleanup, it calls PrepCleanupWamRequest
|
|
and relies on its higher-level callers (DoWork et al) to handle the
|
|
remaining cleanup tasks.
|
|
|
|
If the isapi callback thread does cleanup, it calls CleanupWamRequest,
|
|
which in turn calls PrepCleanupWamRequest, and does all cleanup tasks
|
|
itself. (In this case, of course, there are no higher-level callers
|
|
to rely upon).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::PrepCleanupWamRequest
|
|
|
|
Description:
|
|
Preps for CleanupWamRequest
|
|
|
|
Arguments:
|
|
Below
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::PrepCleanupWamRequest(
|
|
unsigned char * szLogData // log data buffer
|
|
, DWORD cbLogData // size of log data buffer
|
|
, DWORD dwHttpStatusCode // HTTP Status code from ecb
|
|
, DWORD dwIsaKeepConn // keep/close/no-change connection?
|
|
)
|
|
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_fFinishWamRequest == FALSE );
|
|
|
|
m_fPrepCleanupCalled = TRUE;
|
|
|
|
//
|
|
// Append log info to logfile parameters and set log status
|
|
//
|
|
// UNDONE This is a little bit bogus, we're not translating
|
|
// the win32 error code based on the HTTP status code
|
|
//
|
|
|
|
|
|
//
|
|
// String should be zero-terminated (#157805)
|
|
//
|
|
|
|
if( IsStringTerminated( (LPCSTR) szLogData, cbLogData ) ) {
|
|
|
|
m_pHttpRequest->AppendLogParameter( (char*) szLogData );
|
|
}
|
|
|
|
m_pHttpRequest->SetLogStatus( dwHttpStatusCode, NO_ERROR );
|
|
|
|
|
|
/*
|
|
|
|
Keep-conn logic works like this:
|
|
|
|
If ISA asked us NOT to keep-conn, or if this is an old ISA
|
|
(which implicitly assumed we would close the connection)
|
|
set client keep-conn false.
|
|
Else, leave client keep-conn unchanged.
|
|
|
|
NOTE: At the end of the day we want the connection kept open
|
|
if and only if BOTH the client and the ISA said to keep it open;
|
|
and, the connection will be kept open or closed based on the
|
|
state of the client's keep-conn flag when cleanup runs later on.
|
|
|
|
This function accomplishes the goal as follows (Yes == keep-conn):
|
|
|
|
Client ISA This function End result
|
|
------ --- ------------- ----------
|
|
Yes Yes Does nothing Connection will be kept open
|
|
Yes No Sets client flag false Connection will be closed
|
|
No Yes Does nothing Connection will be closed
|
|
No No Sets client flag false Connection will be closed
|
|
|
|
*/
|
|
|
|
if ( dwIsaKeepConn == KEEPCONN_FALSE
|
|
|| dwIsaKeepConn == KEEPCONN_OLD_ISAPI ) {
|
|
|
|
SetKeepConn( FALSE );
|
|
}
|
|
|
|
|
|
return NOERROR;
|
|
|
|
} // WAM_REQUEST::PrepCleanupWamRequest
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::CleanupWamRequest
|
|
|
|
Description:
|
|
Cleans up wamreq.
|
|
|
|
NOTE this function replaces cleanup code which in IIS 3.0
|
|
resided in ServerSupportFunction and should be called
|
|
ONLY from ServerSupportFunction on isapi callback thread.
|
|
See comments in PrepCleanupWamRequest for more details.
|
|
|
|
ANY OTHER USE VOIDS WARRANTY.
|
|
|
|
[When mainline thread does cleanup, it happens elsewhere.]
|
|
|
|
Arguments:
|
|
Below
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::CleanupWamRequest(
|
|
unsigned char * szLogData // log data buffer
|
|
, DWORD cbLogData // size of log data buffer
|
|
, DWORD dwHttpStatusCode // HTTP Status code from ecb
|
|
, DWORD dwIsaKeepConn // keep/close/no-change connection?
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
DBG_ASSERT( m_pExec );
|
|
|
|
|
|
#if CC_REF_TRACKING
|
|
//
|
|
// log to our various ref logs
|
|
//
|
|
// NOTE negative indicates no change to ref count
|
|
//
|
|
|
|
#if DBG
|
|
|
|
WR_LOG_REF_COUNT( -m_cRefs );
|
|
WR_SHARED_LOG_REF_COUNT( -m_cRefs );
|
|
|
|
#endif // DBG
|
|
|
|
//
|
|
// log to local (per-object) CLIENT_CONN log
|
|
//
|
|
LogRefCountCCLocal(
|
|
- m_cRefs
|
|
, m_pHttpRequest
|
|
? m_pHttpRequest->QueryClientConn()
|
|
: NULL
|
|
, m_pHttpRequest
|
|
, this
|
|
, m_pHttpRequest
|
|
? m_pHttpRequest->QueryState()
|
|
: NULL
|
|
);
|
|
#endif // CC_REF_TRACKING
|
|
|
|
m_fCleanupCalled = TRUE;
|
|
|
|
PrepCleanupWamRequest(
|
|
szLogData
|
|
, cbLogData
|
|
, dwHttpStatusCode
|
|
, dwIsaKeepConn
|
|
);
|
|
|
|
return NOERROR;
|
|
|
|
|
|
} // WAM_REQUEST::CleanupWamRequest
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::IsChild
|
|
Is this a child request?
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
BOOL
|
|
|
|
*/
|
|
BOOL
|
|
WAM_REQUEST::IsChild( VOID ) const
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
return m_pExec && m_pExec->IsChild();
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::QueryExecMetaData
|
|
Metadata pointer from m_pExec member
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
PW3_METADATA
|
|
|
|
*/
|
|
PW3_METADATA
|
|
WAM_REQUEST::QueryExecMetaData( VOID ) const
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
return m_pExec ? m_pExec->QueryMetaData() : NULL;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SetPathTrans
|
|
Sets path-trans member
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
BOOL
|
|
|
|
*/
|
|
BOOL
|
|
WAM_REQUEST::SetPathTrans( )
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pExec->_pstrPathInfo );
|
|
DBG_ASSERT( m_pExec->_pstrPathInfo->QueryStr() );
|
|
|
|
//
|
|
// If path-info is identical to the URL, as will be the case when script
|
|
// mapping is used (i.e. the image name is not in the URL),
|
|
// then path-tran is simply physical path, verbatim.
|
|
// Else we need to get path-tran from vroot lookup.
|
|
//
|
|
|
|
DWORD cchURL = m_pHttpRequest->QueryURLStr().QueryCCH();
|
|
|
|
if( ( m_pExec->_pstrPathInfo->QueryCCH() == cchURL )
|
|
&& !memcmp( m_pExec->_pstrPathInfo->QueryStr(),
|
|
m_pHttpRequest->QueryURLStr().QueryStr(), cchURL ) )
|
|
{
|
|
|
|
if( !m_strPathTrans.Copy( m_pHttpRequest->QueryPhysicalPathStr() ) )
|
|
{
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::SetPathTrans failed "
|
|
"due to STR::Copy failure\n"
|
|
, this
|
|
));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// ISAPI Specifies that the pathTrans will contain the
|
|
// Path xlated for the path specified in 'PathInfo'
|
|
// Do the Virtual Root Lookup now.
|
|
//
|
|
// NOTE since we are passing in metadata ptrs,
|
|
// callee will not free, so we need to do this ourselves
|
|
// (we free in destructor)
|
|
//
|
|
|
|
if( !m_pHttpRequest->
|
|
LookupVirtualRoot( &m_strPathTrans,
|
|
m_pExec->_pstrPathInfo->QueryStr(),
|
|
m_pExec->_pstrPathInfo->QueryCCH(),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
&m_pExec->_pPathInfoMetaData,
|
|
&m_pExec->_pPathInfoURIBlob ) )
|
|
{
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::SetPathTrans failed "
|
|
"due to LookupVirtualRoot failure\n"
|
|
, this
|
|
));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // WAM_REQUEST::SetPathTrans()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::CbWrcStrings
|
|
Returns count of bytes required for wamreq core strings
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Count of bytes required for wamreq core strings, including null-terminators
|
|
|
|
*/
|
|
DWORD
|
|
WAM_REQUEST::CbWrcStrings
|
|
(
|
|
BOOL fInProcess
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
/* sync WRC_STRINGS */
|
|
DWORD cbRet = 1 + m_pExec->_pstrPathInfo->QueryCCH()
|
|
+ 1 + m_strPathTrans.QueryCCH()
|
|
+ 1 + m_pHttpRequest->QueryMethodStr().QueryCCH()
|
|
+ 1 + m_pHttpRequest->QueryContentTypeStr().QueryCCH()
|
|
+ 1 + m_pHttpRequest->QueryURLStr().QueryCCH()
|
|
+ 1 + m_strISADllPath.QueryCCH()
|
|
+ 1 + m_pExec->_pstrURLParams->QueryCCH()
|
|
+ 1 + m_pHttpRequest->GetWAMMetaData()->QueryAppPath()->QueryCCH()
|
|
+ 1 + m_strUserAgent.QueryCCH()
|
|
+ 1 + m_strCookie.QueryCCH()
|
|
+ 1 + m_strExpires.QueryCCH()
|
|
;
|
|
|
|
/* sync WRC_STRINGS */
|
|
if( !fInProcess )
|
|
{
|
|
// if out-of-proc, allow for entity body in buffer
|
|
// NOTE no null terminator
|
|
cbRet += m_pHttpRequest->QueryEntityBodyCB();
|
|
|
|
|
|
}
|
|
|
|
return cbRet;
|
|
}
|
|
|
|
DWORD
|
|
WAM_REQUEST::CbCachedSVStrings(
|
|
IN SV_CACHE_LIST::BUFFER_ITEM * pCacheItems,
|
|
IN DWORD cItems
|
|
)
|
|
{
|
|
DWORD dwTotalBytesRequired = 0;
|
|
|
|
for( DWORD i = 0; i < cItems; ++i )
|
|
{
|
|
//
|
|
// We store the number of characters required for each string
|
|
// in dwOffset before we get the actual values. Once the values
|
|
// are retrieved, dwOffset contains the offset into the value
|
|
// buffer or an error code that should be returned to the client.
|
|
//
|
|
LPDWORD pcchRequired = &(pCacheItems[i].dwOffset);
|
|
DWORD svid = pCacheItems[i].svid;
|
|
LPCSTR pszVariableName =
|
|
g_pWamDictator->QueryServerVariableMap().FindName(svid);
|
|
|
|
BOOL fSuccess = m_pHttpRequest->GetInfoForName( pszVariableName,
|
|
NULL,
|
|
pcchRequired
|
|
);
|
|
|
|
if( !fSuccess )
|
|
{
|
|
// This is really all debug code. Should remove...
|
|
// But it will probably all optimize away.
|
|
|
|
DWORD dwError = GetLastError();
|
|
if( dwError != ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
// This will happen for HTTP_ variables that were
|
|
// not sent by the client, not a problem. We'll
|
|
// handle in WAM_REQUEST::GetCachedSVStrings
|
|
DBG_ASSERT( *pcchRequired == 0 );
|
|
}
|
|
else
|
|
{
|
|
// Normal case
|
|
DBG_ASSERT( *pcchRequired > 0 );
|
|
}
|
|
}
|
|
|
|
dwTotalBytesRequired += *pcchRequired;
|
|
}
|
|
return dwTotalBytesRequired;
|
|
}
|
|
|
|
HRESULT
|
|
WAM_REQUEST::GetCachedSVStrings(
|
|
IN OUT unsigned char * pbServerVariables,
|
|
IN DWORD cchAvailable,
|
|
IN SV_CACHE_LIST::BUFFER_ITEM * pCacheItems,
|
|
IN DWORD cCacheItems
|
|
)
|
|
{
|
|
DBG_ASSERT( pbServerVariables );
|
|
DBG_ASSERT( pCacheItems );
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
DWORD cchRemainingBuffer;
|
|
DWORD cchValue;
|
|
DWORD dwOffset = 0;
|
|
|
|
for( DWORD i = 0; i < cCacheItems; ++i )
|
|
{
|
|
DWORD svid = pCacheItems[i].svid;
|
|
LPCSTR pszVariableName =
|
|
g_pWamDictator->QueryServerVariableMap().FindName(svid);
|
|
|
|
// Test <= because the last server variable may be one that
|
|
// produces an error. In that case cchRemainingBuffer will
|
|
// be 0 which will still generate the correct result.
|
|
if( dwOffset <= cchAvailable )
|
|
{
|
|
cchRemainingBuffer = cchAvailable - dwOffset;
|
|
cchValue = pCacheItems[i].dwOffset;
|
|
|
|
DBG_ASSERT( cchRemainingBuffer >= cchValue );
|
|
|
|
if( m_pHttpRequest->GetInfoForName( pszVariableName,
|
|
(LPSTR)pbServerVariables + dwOffset,
|
|
&cchValue
|
|
) )
|
|
{
|
|
pCacheItems[i].dwOffset = dwOffset;
|
|
dwOffset += cchValue;
|
|
|
|
// Buffer over run.
|
|
DBG_ASSERT( dwOffset <= cchAvailable );
|
|
}
|
|
else
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
DBG_ASSERT( dwError != ERROR_SUCCESS );
|
|
switch( dwError )
|
|
{
|
|
// Could collapse these into two cases...
|
|
|
|
case ERROR_INVALID_INDEX:
|
|
//
|
|
// This is expected if the server variable
|
|
// is unknown. This should only happen
|
|
// for HTTP_ server variables that were not
|
|
// sent by the client. Set the error, so we
|
|
// can return this to the client
|
|
//
|
|
pCacheItems[i].dwOffset = HRESULT_FROM_WIN32(dwError);
|
|
break;
|
|
case ERROR_INSUFFICIENT_BUFFER:
|
|
//
|
|
// This shouldn't happen.
|
|
// Mark this as not cached.
|
|
//
|
|
DBG_ASSERT(FALSE);
|
|
pCacheItems[i].dwOffset = SV_DATA_INVALID_OFFSET;
|
|
break;
|
|
case ERROR_NO_DATA:
|
|
//
|
|
// According to the documentation, but not the code,
|
|
// we can return this value.
|
|
//
|
|
pCacheItems[i].dwOffset = HRESULT_FROM_WIN32(dwError);
|
|
break;
|
|
case ERROR_INVALID_PARAMETER:
|
|
default:
|
|
//
|
|
// Anything else is bogus.
|
|
//
|
|
DBG_ASSERT(FALSE);
|
|
pCacheItems[i].dwOffset = SV_DATA_INVALID_OFFSET;
|
|
break;
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetCachedSVStrings - Failed on "
|
|
"m_pHttpRequest->GetInfoForName( %s, ,%d ) : %08x\n",
|
|
this,
|
|
pszVariableName,
|
|
cchValue,
|
|
dwError
|
|
));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This should never happen
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetCachedSVStrings() - "
|
|
"Insufficient buffer - cchAvailable(%d) dwOffset(%d)\n",
|
|
this,
|
|
cchAvailable,
|
|
dwOffset
|
|
));
|
|
DBG_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::ProcessAsyncGatewayIO
|
|
Processes async i/o for a pending wam request
|
|
|
|
Arguments:
|
|
dwStatus - i/o status
|
|
cbWritten - count of byte written
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::ProcessAsyncGatewayIO(
|
|
DWORD dwStatus
|
|
, DWORD cbWritten
|
|
)
|
|
{
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
DBG_ASSERT( m_cRefs > 0);
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::ProcessAsyncGatewayIO\n"
|
|
, this
|
|
));
|
|
}
|
|
|
|
DBG_ASSERT( m_pWamExecInfo );
|
|
|
|
if( !m_fAsyncWrite && m_pHttpRequest->IsChunked() && cbWritten ) {
|
|
|
|
//
|
|
// decode chunked data
|
|
//
|
|
|
|
DWORD cbToDecode = cbWritten;
|
|
|
|
if( m_pHttpRequest->DecodeChunkedBytes( m_pAsyncReadBuffer, &cbWritten ) ) {
|
|
|
|
if( cbWritten == 0 ) {
|
|
|
|
if( !m_pHttpRequest->IsChunkedReadComplete() ) {
|
|
|
|
//
|
|
// All the bytes we've read were headers or footers.
|
|
// We need to restart reading...
|
|
//
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_GATEWAY_ASYNC_IO,
|
|
m_pHttpRequest->QueryLogHttpResponse(),
|
|
m_pHttpRequest->QueryLogWinError() );
|
|
|
|
if( m_pHttpRequest->ReadFile(
|
|
m_pAsyncReadBuffer
|
|
, m_dwAsyncReadBufferSize
|
|
, NULL
|
|
, IO_FLAG_ASYNC
|
|
) )
|
|
{
|
|
//
|
|
// Successfuly restarted reading.
|
|
//
|
|
|
|
return NOERROR;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Failed to restart reading -- undo the state change
|
|
//
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_DOVERB,
|
|
m_pHttpRequest->QueryLogHttpResponse(),
|
|
ERROR_INVALID_PARAMETER
|
|
);
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::Failed to restart reading\n", this
|
|
));
|
|
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Error decoding chunked data.
|
|
//
|
|
m_pHttpRequest->SetState(
|
|
HTR_DOVERB,
|
|
m_pHttpRequest->QueryLogHttpResponse(),
|
|
ERROR_INVALID_PARAMETER
|
|
);
|
|
dwStatus = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is a read, let HTTP_REQUEST know how much we read for
|
|
// logging purposes.
|
|
//
|
|
|
|
if( !m_fAsyncWrite && dwStatus == STATUS_SUCCESS )
|
|
{
|
|
m_pHttpRequest->AddTotalEntityBodyCB( cbWritten );
|
|
}
|
|
|
|
//
|
|
// If we used file handle for async i/o, close it now.
|
|
//
|
|
|
|
if ( m_hFileTfi != INVALID_HANDLE_VALUE ) {
|
|
|
|
DBG_ASSERT( !(m_pWamInfo->FInProcess()) );
|
|
|
|
CloseHandle( m_hFileTfi );
|
|
m_hFileTfi = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Free TFI strings (if any)
|
|
//
|
|
|
|
if ( m_pszStatusTfi != NULL )
|
|
{
|
|
LocalFree( m_pszStatusTfi );
|
|
m_pszStatusTfi = NULL;
|
|
}
|
|
|
|
if ( m_pszTailTfi != NULL )
|
|
{
|
|
LocalFree( m_pszTailTfi );
|
|
m_pszTailTfi = NULL;
|
|
}
|
|
|
|
if ( m_pszHeadTfi != NULL )
|
|
{
|
|
LocalFree( m_pszHeadTfi );
|
|
m_pszHeadTfi = NULL;
|
|
}
|
|
|
|
if( m_pbAsyncReadOopBuffer != NULL )
|
|
{
|
|
// Doing an out of process async read
|
|
DBG_ASSERT( !m_fAsyncWrite );
|
|
|
|
// Save a local copy of the read buffer before making callback.
|
|
// ISAPI may queue another async read.
|
|
|
|
LPBYTE pbTemp = m_pbAsyncReadOopBuffer;
|
|
m_pbAsyncReadOopBuffer = NULL;
|
|
|
|
hr = m_pWamInfo->ProcessAsyncIO(
|
|
this,
|
|
#ifdef _WIN64
|
|
(UINT64) m_pWamExecInfo,
|
|
#else
|
|
(ULONG_PTR) m_pWamExecInfo,
|
|
#endif
|
|
dwStatus,
|
|
cbWritten,
|
|
pbTemp
|
|
);
|
|
|
|
LocalFree( pbTemp );
|
|
}
|
|
else
|
|
{
|
|
hr = m_pWamInfo->ProcessAsyncIO(
|
|
this,
|
|
#ifdef _WIN64
|
|
(UINT64) m_pWamExecInfo,
|
|
#else
|
|
(ULONG_PTR) m_pWamExecInfo,
|
|
#endif
|
|
dwStatus,
|
|
cbWritten );
|
|
}
|
|
|
|
|
|
//
|
|
// Deref upon completing async i/o operation.
|
|
//
|
|
// NOTE balances ref which must precede any async i/o operation.
|
|
//
|
|
// NOTE this ref/deref scheme fixes 97842, wherein inetinfo crashed
|
|
// after oop isapi submited async readcli and then crashed
|
|
//
|
|
|
|
Release();
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SetDeniedFlags
|
|
|
|
Cover function
|
|
|
|
|
|
|
|
*/
|
|
VOID
|
|
WAM_REQUEST::SetDeniedFlags
|
|
(
|
|
DWORD dwDeniedFlags
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
m_pHttpRequest->SetDeniedFlags( dwDeniedFlags );
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::SendEntireResponseFast
|
|
|
|
Description:
|
|
Sends an entire response (headers and body) as fast as possible
|
|
by calling WSASend.
|
|
|
|
Arguments:
|
|
pHseResponseInfo - custom struct, see iisext.x
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::SendEntireResponseFast(
|
|
HSE_SEND_ENTIRE_RESPONSE_INFO * pHseResponseInfo
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
//
|
|
// write status and header into buffer, but suppress actual send
|
|
// (by first setting m_fWriteHeaders = FALSE)
|
|
//
|
|
// NOTE it is semi-hokey to use a member BOOL for this
|
|
// instead of passing an arg, but it saves cluttering the idl file
|
|
//
|
|
|
|
m_fWriteHeaders = FALSE;
|
|
|
|
if ( FAILED( hrRet =
|
|
SendHeader(
|
|
(unsigned char *) pHseResponseInfo->HeaderInfo.pszStatus
|
|
, pHseResponseInfo->HeaderInfo.cchStatus
|
|
, (unsigned char *) pHseResponseInfo->HeaderInfo.pszHeader
|
|
, pHseResponseInfo->HeaderInfo.cchHeader
|
|
, pHseResponseInfo->HeaderInfo.fKeepConn
|
|
) ) ) {
|
|
|
|
return hrRet;
|
|
}
|
|
|
|
m_fWriteHeaders = TRUE;
|
|
|
|
|
|
//
|
|
// NOTE: Caller must have allocated N+1 buffers
|
|
// and filled buffers 1 through N with its data buffers.
|
|
// We now fill the extra buffer (buffer 0) with header info,
|
|
// generated by above SendHeader call.
|
|
//
|
|
// NOTE we assert that empty array slot is 0'ed out.
|
|
// This is valid as long as this api stays private
|
|
// and all of its callers comply.
|
|
//
|
|
|
|
DBG_ASSERT( pHseResponseInfo->rgWsaBuf[0].len == 0 );
|
|
DBG_ASSERT( pHseResponseInfo->rgWsaBuf[0].buf == NULL );
|
|
|
|
pHseResponseInfo->rgWsaBuf[0].len = m_pHttpRequest->QueryRespBufCB();
|
|
pHseResponseInfo->rgWsaBuf[0].buf = m_pHttpRequest->QueryRespBufPtr();
|
|
|
|
|
|
//
|
|
// write wsa-buffer array:
|
|
//
|
|
|
|
return HresultFromBool(
|
|
m_pHttpRequest->SyncWsaSend(
|
|
pHseResponseInfo->rgWsaBuf
|
|
, pHseResponseInfo->cWsaBuf
|
|
, &pHseResponseInfo->cbWritten
|
|
) );
|
|
|
|
|
|
} // WAM_REQUEST::SendEntireResponseFast()
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::SendEntireResponseNormal
|
|
|
|
Description:
|
|
Sends an entire response (headers and body) by surface mail.
|
|
|
|
Arguments:
|
|
pHseResponseInfo - custom struct, see iisext.x
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
WAM_REQUEST::SendEntireResponseNormal(
|
|
HSE_SEND_ENTIRE_RESPONSE_INFO * pHseResponseInfo
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
DWORD cbWritten = 0;
|
|
UINT i;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
//
|
|
// init bytes-written count to 0 - we keep a running tally below
|
|
//
|
|
|
|
pHseResponseInfo->cbWritten = 0;
|
|
|
|
|
|
//
|
|
// send headers
|
|
//
|
|
// UNDONE need to get bytes-written back from SendHeader
|
|
//
|
|
|
|
DBG_ASSERT( m_fWriteHeaders );
|
|
|
|
if ( FAILED( hrRet =
|
|
SendHeader(
|
|
(unsigned char *) pHseResponseInfo->HeaderInfo.pszStatus
|
|
, pHseResponseInfo->HeaderInfo.cchStatus
|
|
, (unsigned char *) pHseResponseInfo->HeaderInfo.pszHeader
|
|
, pHseResponseInfo->HeaderInfo.cchHeader
|
|
, pHseResponseInfo->HeaderInfo.fKeepConn
|
|
/* , &cbWritten */
|
|
) ) ) {
|
|
|
|
goto LExit;
|
|
}
|
|
|
|
|
|
pHseResponseInfo->cbWritten += cbWritten;
|
|
|
|
|
|
//
|
|
// Send body (data buffers)
|
|
//
|
|
// NOTE: Caller must have allocated N+1 buffers
|
|
// and filled buffers 1 through N with its data buffers.
|
|
// We ignore buffer 0 (unused in this case) and send
|
|
// each data buffer by normal means.
|
|
//
|
|
|
|
for ( i = 1; i < pHseResponseInfo->cWsaBuf; i++ ) {
|
|
|
|
if ( FAILED( hrRet =
|
|
SyncWriteClient(
|
|
pHseResponseInfo->rgWsaBuf[i].len
|
|
, (unsigned char *) pHseResponseInfo->rgWsaBuf[i].buf
|
|
, &cbWritten,
|
|
0
|
|
) ) ) {
|
|
|
|
goto LExit;
|
|
}
|
|
|
|
pHseResponseInfo->cbWritten += cbWritten;
|
|
}
|
|
|
|
|
|
LExit:
|
|
return hrRet;
|
|
|
|
} // WAM_REQUEST::SendEntireResponseNormal()
|
|
|
|
|
|
|
|
/*******************************************
|
|
* *
|
|
* IWamRequest interface methods *
|
|
* *
|
|
* *
|
|
*******************************************/
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
Support for WAM_REQUEST::GetCoreState
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
HGetOopImpersonationToken
|
|
|
|
Description
|
|
|
|
Dup the handle for use in the hWam process.
|
|
NOTE the WAM must release the handle after it is done with it.
|
|
|
|
Arguments
|
|
HANDLE hImpersonationToken - the impersonation token in this process
|
|
HANDLE hWam - handle to wam process
|
|
HANDLE *phOopImpersonationToken - Returned handle for use in the remote
|
|
process
|
|
|
|
Returns
|
|
HRESULT
|
|
|
|
*/
|
|
HRESULT
|
|
HGetOopImpersonationToken(
|
|
IN HANDLE hImpersonationToken,
|
|
IN HANDLE hWam,
|
|
OUT HANDLE *phOopImpersonationToken
|
|
)
|
|
{
|
|
HANDLE hImpTokInChildProcessAddressSpace = NULL;
|
|
HANDLE hDuplicateToken = NULL;
|
|
HRESULT hr = NOERROR;
|
|
BOOL fSuccess;
|
|
|
|
DBG_ASSERT( hImpersonationToken != (HANDLE)0 );
|
|
DBG_ASSERT( phOopImpersonationToken );
|
|
|
|
*phOopImpersonationToken = NULL;
|
|
|
|
do
|
|
{
|
|
fSuccess =
|
|
DupTokenWithSameImpersonationLevel( hImpersonationToken,
|
|
TOKEN_ALL_ACCESS,
|
|
TokenImpersonation,
|
|
&hDuplicateToken
|
|
);
|
|
|
|
if( !fSuccess )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,"Error %d on DuplicateTokenEx\n",
|
|
GetLastError()
|
|
));
|
|
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
hr = GrantAllAccessToToken( hDuplicateToken );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,"Error %08x on GrantAllAccessToToken\n",
|
|
hr
|
|
));
|
|
DBG_ASSERT( SUCCEEDED(hr) );
|
|
break;
|
|
}
|
|
|
|
fSuccess = DuplicateHandle(
|
|
g_pWamDictator->HW3SvcProcess(), // Handle of W3Svc process
|
|
hDuplicateToken, // Handle to duplicate to remote process
|
|
hWam, // Handle of Wam process
|
|
&hImpTokInChildProcessAddressSpace, // Handle to token in remote process
|
|
0, // ignored when DUPLICATE_SAME_ACCESS is passed
|
|
FALSE, // inheritance flag
|
|
DUPLICATE_SAME_ACCESS // duplicate same access permissions as original
|
|
);
|
|
|
|
if( !fSuccess )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,"Error %d on DuplicateHandle\n",
|
|
GetLastError()
|
|
));
|
|
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
if( hDuplicateToken )
|
|
{
|
|
CloseHandle( hDuplicateToken );
|
|
}
|
|
|
|
*phOopImpersonationToken = hImpTokInChildProcessAddressSpace;
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
#define APPEND_WRC_STRING( iString, pstr ) \
|
|
DBG_ASSERT( pbWrcData ); \
|
|
DBG_ASSERT( iString < WRC_C_STRINGS ); \
|
|
DBG_ASSERT( (pstr) ); \
|
|
cch = rgcchWrcStrings[ iString ] = (pstr)->QueryCCH(); \
|
|
CopyMemory( pchCur, (pstr)->QueryStr(), cch+1 ); \
|
|
rgcbWrcOffsets[ iString ] = DIFF(pchCur - pbWrcData); \
|
|
pchCur += cch+1;
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetCoreState
|
|
Fills a caller-supplied buffer with wamreq core strings
|
|
|
|
Arguments:
|
|
See below
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetCoreState(
|
|
DWORD cbWrcData // size of wamreq core data buffer
|
|
, unsigned char * pbWrcData // ptr to address of wamreq core data buffer
|
|
, DWORD cbWRCF // size of wamreq core fixed-length struct
|
|
, unsigned char * pbWRCF // ptr to address of struct - WAM_REQ_CORE_FIXED
|
|
)
|
|
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::GetCoreState\n"
|
|
, this
|
|
));
|
|
}
|
|
|
|
DBG_ASSERT( pbWrcData );
|
|
DBG_ASSERT( pbWRCF );
|
|
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
BOOL fInProcess = m_pWamInfo->FInProcess();
|
|
WAM_REQ_CORE_FIXED * pWamReqCoreFixed = reinterpret_cast< WAM_REQ_CORE_FIXED * >( pbWRCF );
|
|
|
|
//
|
|
// make sure the buffers are large enough (#157823)
|
|
//
|
|
if( cbWrcData < ( WRC_CB_FIXED_ARRAYS + CbWrcStrings( fInProcess ) )
|
|
|| cbWRCF < sizeof( WAM_REQ_CORE_FIXED) )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
/********************************************
|
|
* Get variable-length (string) data
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
Init string offsets array and string lengths array
|
|
|
|
NOTE offsets to strings are stored at start of data buffer
|
|
NOTE string lengths are stored immediately after offsets in data buffer
|
|
|
|
sync WRC_DATA_LAYOUT
|
|
*/
|
|
|
|
DWORD * rgcbWrcOffsets = (DWORD *) pbWrcData;
|
|
DWORD * rgcchWrcStrings = ((DWORD *) pbWrcData) + WRC_C_STRINGS;
|
|
|
|
// Init current copy ptr to start of strings section of buffer
|
|
// sync WRC_DATA_LAYOUT
|
|
unsigned char * pchCur = pbWrcData + WRC_CB_FIXED_ARRAYS;
|
|
|
|
|
|
/* sync WRC_STRINGS */
|
|
// NOTE append order MUST match WRC_I_ index order
|
|
|
|
DWORD cch = 0;
|
|
APPEND_WRC_STRING( WRC_I_PATHINFO, m_pExec->_pstrPathInfo )
|
|
APPEND_WRC_STRING( WRC_I_PATHTRANS, &m_strPathTrans )
|
|
APPEND_WRC_STRING( WRC_I_METHOD, (STR *) &m_pHttpRequest->QueryMethodStr() )
|
|
APPEND_WRC_STRING( WRC_I_CONTENTTYPE, (STR *) &m_pHttpRequest->QueryContentTypeStr() )
|
|
APPEND_WRC_STRING( WRC_I_URL, (STR *) &m_pHttpRequest->QueryURLStr() )
|
|
APPEND_WRC_STRING( WRC_I_ISADLLPATH, &m_strISADllPath )
|
|
APPEND_WRC_STRING( WRC_I_QUERY, m_pExec->_pstrURLParams )
|
|
APPEND_WRC_STRING( WRC_I_APPLMDPATH, m_pHttpRequest->GetWAMMetaData()->QueryAppPath() )
|
|
APPEND_WRC_STRING( WRC_I_USERAGENT, &m_strUserAgent )
|
|
APPEND_WRC_STRING( WRC_I_COOKIE, &m_strCookie )
|
|
APPEND_WRC_STRING( WRC_I_EXPIRES, &m_strExpires )
|
|
|
|
/********************************************
|
|
* Get fixed-length data
|
|
*
|
|
*/
|
|
|
|
pWamReqCoreFixed->m_fAnonymous = m_pHttpRequest->IsAnonymous();
|
|
pWamReqCoreFixed->m_cbEntityBody = m_pHttpRequest->QueryEntityBodyCB();
|
|
pWamReqCoreFixed->m_cbClientContent = m_pHttpRequest->QueryClientContentLength();
|
|
pWamReqCoreFixed->m_fCacheISAPIApps = m_pHttpRequest->QueryMetaData()->QueryCacheISAPIApps();
|
|
pWamReqCoreFixed->m_dwChildExecFlags = m_pExec->_dwExecFlags;
|
|
pWamReqCoreFixed->m_dwHttpVersion = (m_pHttpRequest->QueryVersionMajor() << 16) |
|
|
m_pHttpRequest->QueryVersionMinor();
|
|
pWamReqCoreFixed->m_dwInstanceId = m_pHttpRequest->QueryW3Instance()->QueryInstanceId();
|
|
|
|
|
|
/********************************************
|
|
* Get oop-dependent stuff
|
|
*
|
|
*/
|
|
|
|
if( fInProcess ) {
|
|
|
|
//
|
|
// In-Proc: return the handle we already have
|
|
//
|
|
|
|
pWamReqCoreFixed->m_hUserToken = m_pExec->QueryImpersonationHandle();
|
|
|
|
} else {
|
|
|
|
//
|
|
// Out-Proc: duplicate and return a process-valid handle
|
|
// from the handle we already have
|
|
//
|
|
|
|
HRESULT hrTemp = HGetOopImpersonationToken(
|
|
m_pExec->QueryImpersonationHandle(),
|
|
m_pWamInfo->HWamProcess(),
|
|
&(pWamReqCoreFixed->m_hUserToken)
|
|
);
|
|
|
|
if( FAILED(hrTemp) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p] HGetOopImpersonationToken() FAILED hr=%08x",
|
|
this,
|
|
hrTemp
|
|
));
|
|
return hrTemp;
|
|
}
|
|
|
|
//
|
|
// Out-Proc: append entity body to end of strings
|
|
//
|
|
// NOTE must do this here because it depends on
|
|
// pWamReqCoreFixed->m_cbEntityBody, which we fill above
|
|
//
|
|
|
|
DWORD cb = pWamReqCoreFixed->m_cbEntityBody;
|
|
|
|
rgcchWrcStrings[ WRC_I_ENTITYBODY ] = cb;
|
|
|
|
//
|
|
// copy entity body into buffer
|
|
// NOTE no null terminator
|
|
//
|
|
|
|
CopyMemory( pchCur, m_pHttpRequest->QueryEntityBody(), cb );
|
|
|
|
//
|
|
// set the offset from start of strings buffer to string we copied
|
|
/* sync WRC_DATA_LAYOUT */
|
|
//
|
|
|
|
rgcbWrcOffsets[ WRC_I_ENTITYBODY ] = DIFF(pchCur - pbWrcData);
|
|
|
|
}
|
|
|
|
|
|
return NOERROR;
|
|
|
|
} // WAM_REQUEST::GetCoreState
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::QueryEntityBody
|
|
|
|
Description
|
|
Cover function
|
|
|
|
Arguments
|
|
|
|
|
|
Returns
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::QueryEntityBody
|
|
(
|
|
unsigned char ** ppbEntityBody
|
|
)
|
|
{
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::QueryEntityBody\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
*ppbEntityBody = m_pHttpRequest->QueryEntityBody();
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SetKeepConn
|
|
|
|
Cover function
|
|
|
|
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SetKeepConn( int fKeepConn )
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::SetKeepConn\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
m_pHttpRequest->SetKeepConn( fKeepConn );
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::IsKeepConnSet
|
|
|
|
Cover function
|
|
|
|
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::IsKeepConnSet
|
|
(
|
|
BOOL * pfKeepConn
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::IsKeepConnSet\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
*pfKeepConn = m_pHttpRequest->IsKeepConnSet();
|
|
return NOERROR;
|
|
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetInfoForName
|
|
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetInfoForName
|
|
(
|
|
const unsigned char * szVarName,
|
|
unsigned char * pchBuffer,
|
|
DWORD cchBuffer,
|
|
DWORD * pcchRequired
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetInfoForName(%s)\n",
|
|
this,
|
|
szVarName
|
|
));
|
|
}
|
|
|
|
|
|
BOOL fReturn = FALSE;
|
|
|
|
DBG_ASSERT( m_pWamInfo );
|
|
m_pWamInfo->NotifyGetInfoForName( (LPCSTR)szVarName );
|
|
|
|
//
|
|
// set required buffer size to actual incoming size
|
|
// and do the look-up
|
|
//
|
|
|
|
*pcchRequired = cchBuffer;
|
|
|
|
|
|
fReturn = m_pHttpRequest->GetInfoForName( (const CHAR *) szVarName,
|
|
(CHAR *) pchBuffer,
|
|
pcchRequired );
|
|
|
|
|
|
//
|
|
// bail if buffer too small
|
|
//
|
|
|
|
if ( *pcchRequired > cchBuffer ) {
|
|
|
|
return ( HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) );
|
|
}
|
|
|
|
|
|
return HresultFromBool( fReturn );
|
|
|
|
} // WAM_REQUEST::GetInfoForName()
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::AppendLogParameter
|
|
|
|
Cover function
|
|
|
|
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::AppendLogParameter
|
|
(
|
|
unsigned char * pszParam
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::AppendLogParameter\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
return HresultFromBool( m_pHttpRequest->AppendLogParameter(
|
|
(CHAR *) pszParam ) );
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::LookupVirtualRoot
|
|
|
|
Returns path-translated of a URL.
|
|
|
|
Arguments
|
|
pchBuffer - [in, out] contains URL coming in, path-tran going out
|
|
cchBuffer - [in] size of buffer
|
|
pcchRequired - [out] required size for path-tran
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::LookupVirtualRoot
|
|
(
|
|
unsigned char * pchBuffer,
|
|
DWORD cchBuffer,
|
|
DWORD * pcchRequired
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::LookupVirtualRoot\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
CanonURL((char *)pchBuffer, g_pInetSvc->IsSystemDBCS());
|
|
|
|
// NOTE we pass buffer as both source and dest
|
|
return LookupVirtualRootEx( pchBuffer,
|
|
pchBuffer,
|
|
cchBuffer,
|
|
pcchRequired,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::LookupVirtualRootEx
|
|
|
|
Returns path-translated of a URL plus additional info
|
|
|
|
Arguments
|
|
szURL - [in] URL string
|
|
pchBuffer - [out] buffer for returned path-tran
|
|
cchBuffer - [in] size of path-tran buffer as passed
|
|
pcchRequired - [out] required size for path-tran buffer
|
|
pcchMatchingPath- [out] number of matching chars in phys path - NULL to ignore
|
|
pcchMatchingURL - [out] number of matching chars in URL - pass NULL to ignore
|
|
pdwFlags - [out] vroot attribute flags - pass NULL to ignore
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::LookupVirtualRootEx
|
|
(
|
|
unsigned char * szURL,
|
|
unsigned char * pchBuffer,
|
|
DWORD cchBuffer,
|
|
DWORD * pcchRequired,
|
|
DWORD * pcchMatchingPath,
|
|
DWORD * pcchMatchingURL,
|
|
DWORD * pdwFlags
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::LookupVirtualRootEx\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
|
|
DBG_ASSERT( szURL );
|
|
DBG_ASSERT( pchBuffer );
|
|
DBG_ASSERT( pcchRequired );
|
|
|
|
STACK_STR( strPathTran, MAX_PATH);
|
|
BOOL fReturn = FALSE;
|
|
|
|
fReturn = ( m_pHttpRequest->LookupVirtualRoot(
|
|
&strPathTran,
|
|
(const char *) szURL,
|
|
strlen((const char *) szURL),
|
|
pcchMatchingPath, // pcchDirRoot,
|
|
pcchMatchingURL, // pcchVRoot,
|
|
pdwFlags, // pdwMask,
|
|
NULL, // BOOL * pfFinished,
|
|
FALSE, // BOOL fGetAcl,
|
|
NULL, // PW3_METADATA * ppMetaData,
|
|
NULL // PW3_URI_INFO * ppURIBlob
|
|
) );
|
|
|
|
|
|
if ( fReturn )
|
|
{
|
|
|
|
//
|
|
// Include one byte for the null terminator
|
|
//
|
|
*pcchRequired = strPathTran.QueryCB() + 1;
|
|
|
|
if ( *pcchRequired <= cchBuffer )
|
|
{
|
|
|
|
// we have enough room in buffer so copy the str
|
|
CopyMemory( pchBuffer, strPathTran.QueryStr(), *pcchRequired );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// we don't have enough room in buffer
|
|
// in normal case, we fail
|
|
// in 'extended' case, we copy as much as buffer will hold
|
|
//
|
|
|
|
//
|
|
// CHEESE ALERT
|
|
// We check null-ness of the 'extended' dword ptrs
|
|
// to determine whether we are in 'extended' or normal case
|
|
// (cheaper than adding a BOOL to the interface)
|
|
//
|
|
// 'extended' <==> all ptrs are non-null
|
|
//
|
|
|
|
if ( !( pcchMatchingPath && pcchMatchingURL && pdwFlags ) )
|
|
{
|
|
|
|
// normal case - set error and bail
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
fReturn = FALSE;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// 'extended' case - copy as much as buffer will hold
|
|
DWORD cch = min( *pcchRequired, cchBuffer );
|
|
|
|
CopyMemory( pchBuffer, strPathTran.QueryStr(), cch );
|
|
pchBuffer[cch - 1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return HresultFromBool( fReturn );
|
|
|
|
} // WAM_REQUEST::LookupVirtualRootEx
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetVirtualPathToken
|
|
|
|
Returns an impersonation token for the specified virtual path.
|
|
|
|
WARNING: the token should be CloseHandle()d by caller.
|
|
|
|
Arguments
|
|
See below
|
|
|
|
Returns:
|
|
Nothing
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetVirtualPathToken(
|
|
IN unsigned char * szURL, // virtual root
|
|
#ifdef _WIN64
|
|
OUT UINT64 * phToken // points to token (handle) placeholder
|
|
#else
|
|
OUT ULONG_PTR * phToken // points to token (handle) placeholder
|
|
#endif
|
|
)
|
|
{
|
|
PW3_METADATA pMD = NULL;
|
|
BOOL fSuccess = FALSE;
|
|
STACK_STR( strPathTran, MAX_PATH);
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
DBG_ASSERT( szURL );
|
|
DBG_ASSERT( phToken );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetVirtualPathToken(%s)\n"
|
|
,
|
|
this,
|
|
szURL
|
|
));
|
|
}
|
|
|
|
|
|
//
|
|
// Get metabase data item pointer
|
|
//
|
|
|
|
fSuccess = m_pHttpRequest->LookupVirtualRoot(
|
|
&strPathTran,
|
|
(const char *) szURL,
|
|
strlen((const char *) szURL),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
TRUE, // BOOL fGetAcl,
|
|
&pMD, // PW3_METADATA * ppMetaData,
|
|
NULL
|
|
);
|
|
if(fSuccess) {
|
|
|
|
//
|
|
// We know this virtual root. Even if it may not have a token, we
|
|
// could return TRUE to caller. Make sure they get a meaningful token.
|
|
//
|
|
|
|
*phToken = NULL;
|
|
|
|
//
|
|
// try to get access token for the specified URL
|
|
//
|
|
|
|
HANDLE hToken = pMD->QueryVrAccessToken();
|
|
|
|
//
|
|
// if we have it, make a duplicate. This is very expensive,
|
|
// but we want to preserve transparency of inproc/out-of-proc,
|
|
//
|
|
|
|
if(hToken)
|
|
{
|
|
// Client closes the handle we return
|
|
|
|
HANDLE hTokenLocalDuplicate = NULL;
|
|
|
|
fSuccess = DupTokenWithSameImpersonationLevel(
|
|
hToken,
|
|
MAXIMUM_ALLOWED,
|
|
TokenPrimary,
|
|
&hTokenLocalDuplicate
|
|
);
|
|
|
|
if( fSuccess )
|
|
{
|
|
if( m_pWamInfo->FInProcess() )
|
|
{
|
|
#ifdef _WIN64
|
|
*phToken = (UINT64)hTokenLocalDuplicate;
|
|
#else
|
|
*phToken = (ULONG_PTR)hTokenLocalDuplicate;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// In the oop case, duplicate the handle to the
|
|
// remote process.
|
|
HANDLE hTokenRemote = NULL;
|
|
|
|
fSuccess = DuplicateHandle(
|
|
g_pWamDictator->HW3SvcProcess(),
|
|
hTokenLocalDuplicate,
|
|
m_pWamInfo->HWamProcess(),
|
|
&hTokenRemote,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
CloseHandle(hTokenLocalDuplicate);
|
|
#ifdef _WIN64
|
|
*phToken = (UINT64)hTokenRemote;
|
|
#else
|
|
*phToken = (ULONG_PTR)hTokenRemote;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return HresultFromBool( fSuccess );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetPrivatePtr
|
|
Returns a 'private' ptr
|
|
WARNING only for knowledgeable IN-PROC callers (ssinc, httpodbc, et al)
|
|
|
|
Arguments
|
|
See below
|
|
|
|
Returns:
|
|
Nothing
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetPrivatePtr
|
|
(
|
|
DWORD dwHSERequest, // type of ServerSupportFunction request
|
|
unsigned char ** ppData // [out] returned ptr
|
|
)
|
|
{
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetPrivatePtr\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
|
|
switch( dwHSERequest ) {
|
|
|
|
case HSE_PRIV_REQ_TSVCINFO: {
|
|
*((PIIS_SERVICE *)*ppData) = g_pInetSvc;
|
|
break;
|
|
}
|
|
|
|
case HSE_PRIV_REQ_HTTP_REQUEST: {
|
|
*((HTTP_REQUEST **)*ppData) = m_pHttpRequest;
|
|
break;
|
|
}
|
|
|
|
case HSE_PRIV_REQ_VROOT_TABLE: {
|
|
|
|
//
|
|
// UNDONE we think no one uses this ???
|
|
// REMOVE this case if so
|
|
//
|
|
|
|
DBG_ASSERT( FALSE );
|
|
*ppData = NULL;
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_FUNCTION );
|
|
|
|
*((PIIS_VROOT_TABLE *)*ppData) =
|
|
m_pHttpRequest->QueryW3Instance()->QueryVrootTable();
|
|
break;
|
|
}
|
|
|
|
case HSE_PRIV_REQ_TSVC_CACHE: {
|
|
*((TSVC_CACHE **)*ppData) =
|
|
&m_pHttpRequest->QueryW3Instance()->GetTsvcCache();
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::AsyncReadClientExt
|
|
This function exists simply to avoid cross-process calls.
|
|
|
|
Arguments:
|
|
|
|
NOTE pWamExecInfo is passed as DWORD to fool the marshaller.
|
|
We simply hold it in WAM_REQUEST::m_pWamExecInfo, do nothing with it,
|
|
then pass it back to wam on i/o completion callback.
|
|
|
|
Returns:
|
|
BOOL
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::AsyncReadClientExt(
|
|
#ifdef _WIN64
|
|
IN UINT64 pWamExecInfo
|
|
#else
|
|
IN ULONG_PTR pWamExecInfo
|
|
#endif
|
|
, OUT unsigned char * lpBuffer
|
|
, IN DWORD nBytesToRead
|
|
)
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::AsyncReadClientExt\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
|
|
m_pWamExecInfo = (WAM_EXEC_INFO *) pWamExecInfo;
|
|
DBG_ASSERT( m_pWamExecInfo );
|
|
|
|
|
|
//
|
|
// If chunked read already complete, call notification routine
|
|
// and return success
|
|
//
|
|
if(m_pHttpRequest->IsChunked() && m_pHttpRequest->IsChunkedReadComplete()) {
|
|
#ifdef _WIN64
|
|
m_pWamInfo->ProcessAsyncIO( this, (UINT64) m_pWamExecInfo, 0, 0 );
|
|
#else
|
|
m_pWamInfo->ProcessAsyncIO( this, (ULONG_PTR) m_pWamExecInfo, 0, 0 );
|
|
#endif
|
|
return HresultFromBool( TRUE );
|
|
}
|
|
|
|
m_pHttpRequest->SetState( HTR_GATEWAY_ASYNC_IO, HT_DONT_LOG, NO_ERROR );
|
|
|
|
|
|
|
|
//
|
|
// Ref before starting async i/o operation
|
|
//
|
|
// NOTE this is balanced by deref in ProcessAsyncGatewayIO
|
|
//
|
|
|
|
AddRef();
|
|
|
|
//
|
|
// Save buffer pointer and size in case we need to
|
|
// restart chunk-encoded transfer
|
|
// (when the decoded data has 0 bytes)
|
|
//
|
|
|
|
m_pAsyncReadBuffer = lpBuffer;
|
|
m_dwAsyncReadBufferSize = nBytesToRead;
|
|
|
|
//
|
|
// Let the completion routine know that we do need to decode
|
|
//
|
|
|
|
m_fAsyncWrite = FALSE;
|
|
|
|
fReturn = m_pHttpRequest->ReadFile(
|
|
lpBuffer
|
|
, nBytesToRead
|
|
, NULL
|
|
, IO_FLAG_ASYNC
|
|
);
|
|
|
|
|
|
if ( !fReturn ) {
|
|
//
|
|
// Deref if async i/o operation failed (balances above ref)
|
|
//
|
|
|
|
Release();
|
|
|
|
m_pHttpRequest->SetState( HTR_DOVERB, HT_DONT_LOG, NO_ERROR );
|
|
}
|
|
return HresultFromBool( fReturn );
|
|
}
|
|
|
|
STDMETHODIMP
|
|
WAM_REQUEST::AsyncReadClientOop(
|
|
#ifdef _WIN64
|
|
IN UINT64 pWamExecInfo
|
|
#else
|
|
IN ULONG_PTR pWamExecInfo
|
|
#endif
|
|
, IN DWORD nBytesToRead
|
|
)
|
|
{
|
|
DBG_ASSERT( nBytesToRead > 0 );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::AsyncReadClientOop\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
m_pbAsyncReadOopBuffer = (LPBYTE)LocalAlloc( LPTR, nBytesToRead );
|
|
if( !m_pbAsyncReadOopBuffer )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = AsyncReadClientExt( pWamExecInfo, m_pbAsyncReadOopBuffer, nBytesToRead );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// If this call fails then the async callback should never be made.
|
|
DBG_ASSERT( m_pbAsyncReadOopBuffer );
|
|
|
|
LocalFree( m_pbAsyncReadOopBuffer );
|
|
m_pbAsyncReadOopBuffer = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::AsyncWriteClient
|
|
|
|
Arguments:
|
|
|
|
NOTE pWamExecInfo is passed as ULONG_PTR to fool the marshaller.
|
|
We simply hold it in WAM_REQUEST::m_pWamExecInfo, do nothing with it,
|
|
then pass it back to wam on i/o completion callback.
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::AsyncWriteClient(
|
|
#ifdef _WIN64
|
|
UINT64 pWamExecInfo
|
|
#else
|
|
ULONG_PTR pWamExecInfo
|
|
#endif
|
|
, unsigned char * lpBuffer
|
|
, DWORD nBytesToWrite
|
|
, DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::AsyncWriteClient\n"
|
|
, this
|
|
));
|
|
|
|
}
|
|
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_GATEWAY_ASYNC_IO
|
|
, HT_DONT_LOG
|
|
, NO_ERROR
|
|
);
|
|
|
|
|
|
m_pWamExecInfo = (WAM_EXEC_INFO *) pWamExecInfo;
|
|
DBG_ASSERT( m_pWamExecInfo );
|
|
|
|
|
|
//
|
|
// Ref before starting async i/o operation
|
|
//
|
|
// NOTE this is balanced by deref in ProcessAsyncGatewayIO
|
|
//
|
|
|
|
AddRef();
|
|
|
|
//
|
|
// Mark the operation as "WRITE" so if we are in the process of
|
|
// decoding chunked upload, we won't attempt to decode on
|
|
// a completion of THIS async write
|
|
//
|
|
|
|
m_fAsyncWrite = TRUE;
|
|
|
|
//
|
|
// Strip off undefined flags
|
|
//
|
|
|
|
dwFlags &= IO_FLAG_NO_DELAY;
|
|
|
|
BOOL fReturn = m_pHttpRequest->WriteFile(
|
|
(LPVOID) lpBuffer
|
|
, nBytesToWrite
|
|
, NULL
|
|
, IO_FLAG_ASYNC | dwFlags
|
|
);
|
|
|
|
|
|
if ( !fReturn ) {
|
|
|
|
//
|
|
// Deref if async i/o operation failed (balances above ref)
|
|
//
|
|
|
|
Release();
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_DOVERB
|
|
, HT_DONT_LOG
|
|
, NO_ERROR
|
|
);
|
|
|
|
}
|
|
|
|
|
|
return HresultFromBool( fReturn );
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::TransmitFileInProc
|
|
Interface to TransmitFile which works in-proc only
|
|
|
|
Arguments:
|
|
pWamExecInfo - ptr to this wamreq's wamexecinfo (in wam process)
|
|
pHseTfIn - ptr to transmit-file-info struct
|
|
|
|
NOTE pWamExecInfo is passed as ULONG_PTR to fool the marshaller.
|
|
We simply hold it in WAM_REQUEST::m_pWamExecInfo, do nothing with it,
|
|
then pass it back to wam on i/o completion callback.
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::TransmitFileInProc(
|
|
#ifdef _WIN64
|
|
IN UINT64 pWamExecInfo
|
|
#else
|
|
IN ULONG_PTR pWamExecInfo
|
|
#endif
|
|
, IN unsigned char * pHseTfIn
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
HSE_TF_INFO * pHseTfi = reinterpret_cast<HSE_TF_INFO *>(pHseTfIn);
|
|
DWORD dwFlags = IO_FLAG_ASYNC;
|
|
PVOID pHead = NULL;
|
|
DWORD cbHead = 0;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::TransmitFileInProc\n"
|
|
, this
|
|
));
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Don't disconnect the socket after the transmit if we need
|
|
// to notify a filter about the end of a request
|
|
//
|
|
|
|
if ( !m_pHttpRequest->QueryFilter()->IsNotificationNeeded(
|
|
SF_NOTIFY_END_OF_REQUEST
|
|
, m_pHttpRequest->IsSecurePort()
|
|
)
|
|
&& (pHseTfi->dwFlags & HSE_IO_DISCONNECT_AFTER_SEND) ) {
|
|
|
|
// suggests fast close & reuse of data socket
|
|
dwFlags |= (TF_DISCONNECT | TF_REUSE_SOCKET);
|
|
}
|
|
|
|
//
|
|
// Honor the HSE_IO_NODELAY flag. HTTP_REQ_BASE::WriteClient will
|
|
// handle this flag for WriteClient. Note: Both of these calls will
|
|
// have the effect of leaving the flag set or unset on the socket.
|
|
// This shouldn't really be an issue as it is not likely that the
|
|
// user would want same client connection to change settings.
|
|
//
|
|
AtqSetSocketOption( m_pHttpRequest->QueryClientConn()->QueryAtqContext(),
|
|
TCP_NODELAY,
|
|
(pHseTfi->dwFlags & HSE_IO_NODELAY) ? 1 : 0
|
|
);
|
|
|
|
//
|
|
// Check if ISAPI requests us to send headers
|
|
// If we need to send headers, we will call upon BuildHttpHeader()
|
|
// to construct a custom header for the client.
|
|
// It is the application's responsibility not to use
|
|
// HSE_REQ_SEND_RESOPNSE_HEADER if it chooses to use
|
|
// the header option HSE_IO_SEND_HEADERS.
|
|
//
|
|
|
|
if ( pHseTfi->dwFlags & HSE_IO_SEND_HEADERS) {
|
|
|
|
BOOL fFinished = FALSE;
|
|
|
|
//
|
|
// Format the header using the pszStatusCode and
|
|
// extra headers specified in pHseTfi->pHead.
|
|
//
|
|
|
|
if ( pHseTfi->pszStatusCode
|
|
&& ( (!strncmp( (char *) pHseTfi->pszStatusCode, "401 ", sizeof("401 ")-1 ) ) ||
|
|
(!strncmp( (char *) pHseTfi->pszStatusCode, "407 ", sizeof("407 ")-1 ) ) )
|
|
) {
|
|
|
|
m_pHttpRequest->SetDeniedFlags( SF_DENIED_APPLICATION );
|
|
m_pHttpRequest->SetAuthenticationRequested( TRUE );
|
|
}
|
|
|
|
//
|
|
// BuildHttpHeader doesn't provide the \r\n final terminator for
|
|
// the header block. If pHead contains nothing, then the response
|
|
// will be malformed.
|
|
//
|
|
if ( !m_pHttpRequest->BuildHttpHeader(
|
|
&fFinished
|
|
, (CHAR * ) pHseTfi->pszStatusCode
|
|
, (CHAR * ) pHseTfi->pHead
|
|
) ) {
|
|
|
|
hrRet = E_FAIL; // UNDONE something besides E_FAIL???
|
|
goto LExit;
|
|
}
|
|
|
|
pHead = m_pHttpRequest->QueryRespBufPtr();
|
|
cbHead = m_pHttpRequest->QueryRespBufCB();
|
|
|
|
//
|
|
// Check if any filters are to be notified about the headers
|
|
//
|
|
if ( m_pHttpRequest->QueryFilter()->IsNotificationNeeded( SF_NOTIFY_SEND_RESPONSE,
|
|
m_pHttpRequest->IsSecurePort() ) )
|
|
{
|
|
BOOL fFinished = FALSE;
|
|
BOOL fAnyChanges = FALSE;
|
|
|
|
if ( !m_pHttpRequest->QueryFilter()->NotifySendHeaders( (const CHAR*) pHead,
|
|
&fFinished,
|
|
&fAnyChanges,
|
|
m_pHttpRequest->QueryRespBuf() ) )
|
|
{
|
|
hrRet = E_FAIL;
|
|
goto LExit;
|
|
}
|
|
|
|
pHead = m_pHttpRequest->QueryRespBufPtr();
|
|
cbHead = m_pHttpRequest->QueryRespBufCB();
|
|
}
|
|
|
|
} else {
|
|
|
|
pHead = pHseTfi->pHead;
|
|
cbHead = pHseTfi->HeadLength;
|
|
}
|
|
|
|
//
|
|
// Setup stage for and execute TransmitFile operation
|
|
//
|
|
|
|
//
|
|
// 1. Set Request state to be async IO from ISAPI client
|
|
// 2. Cache wamexec info ptr in member
|
|
// 3. Submit Async IOP
|
|
// 4. return to the ISAPI application
|
|
//
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_GATEWAY_ASYNC_IO
|
|
, HT_DONT_LOG
|
|
, NO_ERROR
|
|
);
|
|
|
|
m_pWamExecInfo = (WAM_EXEC_INFO *) pWamExecInfo;
|
|
DBG_ASSERT( m_pWamExecInfo );
|
|
|
|
|
|
//
|
|
// Ref before starting async i/o operation
|
|
//
|
|
// NOTE this is balanced by deref in ProcessAsyncGatewayIO
|
|
//
|
|
|
|
AddRef();
|
|
|
|
//
|
|
// Mark the operation as "WRITE" so if we are in the process of
|
|
// decoding chunked upload, we won't attempt to decode on
|
|
// a completion of THIS async write
|
|
//
|
|
m_fAsyncWrite = TRUE;
|
|
|
|
hrRet = HresultFromBool( m_pHttpRequest->TransmitFile(
|
|
NULL,
|
|
pHseTfi->hFile
|
|
, pHseTfi->Offset
|
|
, pHseTfi->BytesToWrite
|
|
, dwFlags | IO_FLAG_NO_RECV
|
|
, (PVOID) pHead
|
|
, cbHead
|
|
, (PVOID) pHseTfi->pTail
|
|
, pHseTfi->TailLength
|
|
) );
|
|
|
|
|
|
if ( FAILED( hrRet ) ) {
|
|
|
|
//
|
|
// Deref if async i/o operation failed (balances above ref)
|
|
//
|
|
|
|
Release();
|
|
|
|
m_pHttpRequest->SetState(
|
|
HTR_DOVERB
|
|
, HT_DONT_LOG
|
|
, NO_ERROR
|
|
);
|
|
}
|
|
|
|
|
|
LExit:
|
|
return hrRet;
|
|
|
|
} // WAM_REQUEST::TransmitFileInProc
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::TransmitFileOutProc
|
|
Interface to TransmitFile, recommended for out-of-proc only
|
|
(works in-proc, but is slower than TransmitFileInProc).
|
|
|
|
This function is a simple cover over TransmitFileInProc.
|
|
It does the following:
|
|
- dups the file handle into this process
|
|
- creates a local TransmitFile-info struct from the in-args
|
|
- calls TransmitFileInProc to do the real work
|
|
|
|
Arguments:
|
|
pWamExecInfo - ptr to this wamreq's wamexecinfo (in wam process)
|
|
other params - transmit-file-info struct, as individual args
|
|
|
|
NOTE pWamExecInfo is passed as DWORD to fool the marshaller.
|
|
We simply hold it in WAM_REQUEST::m_pWamExecInfo, do nothing with it,
|
|
then pass it back to wam on i/o completion callback.
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::TransmitFileOutProc(
|
|
#ifdef _WIN64
|
|
IN UINT64 pWamExecInfo
|
|
, IN UINT64 hFile // file handle valid in WAM process
|
|
#else
|
|
IN ULONG_PTR pWamExecInfo
|
|
, IN ULONG_PTR hFile // file handle valid in WAM process
|
|
#endif
|
|
, IN unsigned char * pszStatusCode
|
|
, IN DWORD cbStatusCode
|
|
, IN DWORD BytesToWrite
|
|
, IN DWORD Offset
|
|
, IN unsigned char * pHead
|
|
, IN DWORD HeadLength
|
|
, IN unsigned char * pTail
|
|
, IN DWORD TailLength
|
|
, IN DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
HSE_TF_INFO HseTfi; // TransmitFile-info struct
|
|
HRESULT hr;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::TransmitFileOutProc\n"
|
|
, this
|
|
));
|
|
|
|
}
|
|
|
|
|
|
if( hFile != NULL )
|
|
{
|
|
//
|
|
// Dup file handle into member to keep it around
|
|
// throughout async i/o operation.
|
|
//
|
|
// NOTE we close file handle elsewhere, once we know
|
|
// it is no longer needed
|
|
//
|
|
// CONSIDER don't dup handle when not needed
|
|
// Is handle not needed when BytesToWrite > 0 ???
|
|
//
|
|
|
|
if ( !DuplicateHandle(
|
|
m_pWamInfo->HWamProcess() // source process handle
|
|
, (HANDLE) hFile // handle to duplicate
|
|
, g_pWamDictator->HW3SvcProcess() // target process handle
|
|
, &m_hFileTfi // ptr to duplicate handle
|
|
, 0 // dwDesiredAccess - ignored with DUPLICATE_SAME_ACCESS
|
|
, FALSE // non-inheritable
|
|
, DUPLICATE_SAME_ACCESS // optional actions
|
|
) ) {
|
|
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE );
|
|
}
|
|
|
|
|
|
DBG_ASSERT( m_hFileTfi != INVALID_HANDLE_VALUE );
|
|
}
|
|
else
|
|
{
|
|
// No file handle only the head and tail buffers will be
|
|
// sent.
|
|
|
|
DBG_ASSERT( BytesToWrite == 0 );
|
|
DBG_ASSERT( Offset == 0 );
|
|
}
|
|
|
|
|
|
//
|
|
// Copy in-args into TransmitFile-info struct,
|
|
// making sure that any custom heads and tails are zero-terminated
|
|
//
|
|
|
|
if( pszStatusCode != NULL ) {
|
|
if( cbStatusCode != 0 ) {
|
|
if( pszStatusCode[cbStatusCode - 1] != '\0') {
|
|
m_pszStatusTfi = (unsigned char *)
|
|
LocalAlloc( LMEM_FIXED, cbStatusCode + 1 );
|
|
if ( m_pszStatusTfi ) {
|
|
memcpy( m_pszStatusTfi, pszStatusCode, cbStatusCode );
|
|
m_pszStatusTfi[cbStatusCode] = '\0';
|
|
pszStatusCode = m_pszStatusTfi;
|
|
}
|
|
}
|
|
} else {
|
|
// there is a pointer, but 0 length -- sanitize it
|
|
pszStatusCode = (unsigned char *) "";
|
|
}
|
|
}
|
|
|
|
if( pHead != NULL ) {
|
|
if( HeadLength != 0 ) {
|
|
if ( pHead[HeadLength - 1] != '\0' ) {
|
|
m_pszHeadTfi = (unsigned char *)
|
|
LocalAlloc( LMEM_FIXED, HeadLength + 1 );
|
|
if( m_pszHeadTfi ) {
|
|
memcpy( m_pszHeadTfi, pHead, HeadLength );
|
|
m_pszHeadTfi[HeadLength] = '\0';
|
|
pHead = m_pszHeadTfi;
|
|
}
|
|
}
|
|
} else {
|
|
pHead = (unsigned char *) "";
|
|
}
|
|
}
|
|
|
|
if( pTail != NULL ) {
|
|
if ( TailLength != 0 ) {
|
|
if( pTail[TailLength - 1] != '\0') {
|
|
m_pszTailTfi = (unsigned char *)
|
|
LocalAlloc( LMEM_FIXED, TailLength + 1 );
|
|
if( m_pszTailTfi ) {
|
|
memcpy( m_pszTailTfi, pTail, TailLength );
|
|
m_pszTailTfi[TailLength] = '\0';
|
|
pTail = m_pszTailTfi;
|
|
}
|
|
}
|
|
} else {
|
|
pTail = (unsigned char *)"";
|
|
}
|
|
}
|
|
|
|
HseTfi.hFile = ( hFile ) ? (HANDLE) m_hFileTfi : NULL;
|
|
HseTfi.pszStatusCode = (const char *) pszStatusCode;
|
|
HseTfi.BytesToWrite = BytesToWrite;
|
|
HseTfi.Offset = Offset;
|
|
HseTfi.pHead = pHead;
|
|
HseTfi.HeadLength = HeadLength;
|
|
HseTfi.pTail = pTail;
|
|
HseTfi.TailLength = TailLength;
|
|
HseTfi.dwFlags = dwFlags;
|
|
|
|
|
|
//
|
|
// Invoke in-proc function to do the real work
|
|
//
|
|
|
|
hr = TransmitFileInProc(
|
|
pWamExecInfo
|
|
, (unsigned char *) &HseTfi
|
|
);
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
if( m_pszStatusTfi )
|
|
{
|
|
LocalFree( m_pszStatusTfi );
|
|
m_pszStatusTfi = NULL;
|
|
}
|
|
|
|
if( m_pszHeadTfi )
|
|
{
|
|
LocalFree( m_pszHeadTfi );
|
|
m_pszHeadTfi = NULL;
|
|
}
|
|
|
|
if( m_pszTailTfi )
|
|
{
|
|
LocalFree( m_pszTailTfi );
|
|
m_pszTailTfi = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
|
|
} // WAM_REQUEST::TransmitFileOutProc
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SyncReadClient
|
|
|
|
Description:
|
|
This function is a wrapper for the sychronous read from the client.
|
|
It is separated from the async case because no async state changes
|
|
need to be done here => cleaner function
|
|
|
|
Arguments:
|
|
lpBuffer - pointer to buffer into which the contents have to be read-in
|
|
nBytesToRead - number of bytes the buffer can accomodate
|
|
pnBytesRead - pointer to location that will contain the # of bytes returned
|
|
|
|
Returns:
|
|
TRUE for success and FALSE for failure
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SyncReadClient
|
|
(
|
|
unsigned char * lpBuffer, // LPVOID lpBuffer,
|
|
DWORD nBytesToRead,
|
|
DWORD * pnBytesRead
|
|
)
|
|
{
|
|
BOOL fSuccess;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
DBG_ASSERT( m_pHttpRequest->QueryClientConn()->CheckSignature() );
|
|
|
|
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "WAM_REQUEST[%p]::"
|
|
"SyncReadClient( %p, %d, %p)\n",
|
|
this, lpBuffer, nBytesToRead, pnBytesRead));
|
|
}
|
|
|
|
retry:
|
|
|
|
//
|
|
// Handle the case when Chunked read was complete.
|
|
//
|
|
|
|
if( m_pHttpRequest->IsChunked() && m_pHttpRequest->IsChunkedReadComplete() ) {
|
|
*pnBytesRead = 0;
|
|
fSuccess = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
fSuccess = m_pHttpRequest->ReadFile( lpBuffer,
|
|
nBytesToRead,
|
|
pnBytesRead,
|
|
IO_FLAG_SYNC );
|
|
|
|
if(fSuccess && m_pHttpRequest->IsChunked() && *pnBytesRead) {
|
|
|
|
//
|
|
// Decode read bytes
|
|
//
|
|
|
|
DWORD cbToDecode = *pnBytesRead;
|
|
|
|
if( m_pHttpRequest->DecodeChunkedBytes( lpBuffer, pnBytesRead ) ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "WAM_REQUEST[%p]::"
|
|
"DecodeChunkedBytes got %d out of %d bytes\n",
|
|
this, *pnBytesRead, cbToDecode ));
|
|
|
|
|
|
if( *pnBytesRead == 0 && !m_pHttpRequest->IsChunkedReadComplete()) {
|
|
|
|
//
|
|
// We decoded zero bytes. This means that all bytes
|
|
// that we've read were chunk headers or footers.
|
|
//
|
|
// We can't return THAT to the caller, because it will
|
|
// think that we are done with reading.
|
|
//
|
|
// We'll have to initiate another read.
|
|
//
|
|
|
|
goto retry;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Increment the count of request entity body bytes read. This
|
|
// is used in logging how many bytes the client sent, etc.
|
|
//
|
|
|
|
m_pHttpRequest->AddTotalEntityBodyCB( *pnBytesRead );
|
|
|
|
done:
|
|
return HresultFromBool( fSuccess );
|
|
|
|
} // WAM_REQUEST::SyncReadClient()
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SyncWriteClient
|
|
|
|
Description:
|
|
This function is a wrapper for the sychronous write to the client.
|
|
It is separated from the async case because no async state changes
|
|
need to be done here => cleaner function
|
|
|
|
Arguments:
|
|
lpBuffer - pointer to buffer that has contents to be written out
|
|
nBytesToWrite - number of bytes to write to client
|
|
pnBytesWritten - pointer to number of bytes written to client
|
|
|
|
Returns:
|
|
TRUE for success and FALSE for failure
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SyncWriteClient
|
|
(
|
|
DWORD nBytesToWrite,
|
|
unsigned char * lpBuffer, // LPVOID lpBuffer,
|
|
DWORD * pnBytesWritten,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
DBG_ASSERT( m_pHttpRequest->QueryClientConn()->CheckSignature() );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "WAM_REQUEST[%p]::"
|
|
"SyncWriteClient( %p, %d)\n",
|
|
this, lpBuffer, nBytesToWrite));
|
|
}
|
|
|
|
// strip off undefined flags
|
|
dwFlags &= IO_FLAG_NO_DELAY;
|
|
|
|
return HresultFromBool( m_pHttpRequest->WriteFile( lpBuffer,
|
|
nBytesToWrite,
|
|
pnBytesWritten,
|
|
IO_FLAG_SYNC | dwFlags ) );
|
|
|
|
} // WAM_REQUEST::SyncWriteClient()
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::SendHeader
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
szStatus - status string
|
|
cchStatus - length of status string
|
|
szHeader - header string
|
|
cchHeader - length of header string
|
|
dwIsaKeepConn - keep/close/no-change connection?
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SendHeader(
|
|
IN unsigned char * szStatus
|
|
, IN DWORD cchStatus
|
|
, IN unsigned char * szHeader
|
|
, IN DWORD cchHeader
|
|
, IN DWORD dwIsaKeepConn
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
//
|
|
// Make sure the strings are zero-terminated (#157805)
|
|
//
|
|
|
|
if( !IsStringTerminated( (LPCSTR) szStatus, cchStatus ) ||
|
|
!IsStringTerminated( (LPCSTR) szHeader, cchHeader ) )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::SendHeader( %s, %s)"
|
|
"\n"
|
|
, this
|
|
, szStatus
|
|
, szHeader
|
|
));
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST::SendHeader: "
|
|
"Intended keep-conn = %d "
|
|
"\n"
|
|
, dwIsaKeepConn
|
|
));
|
|
|
|
}
|
|
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
|
|
//
|
|
// If this is a child ISA, we may not want to send any headers
|
|
//
|
|
|
|
if ( m_pExec->NoHeaders() || m_pExec->RedirectOnly() ) {
|
|
|
|
DWORD cbSent;
|
|
BYTE * pbTextToSend;
|
|
|
|
//
|
|
// If no headers needed, just send everything past the
|
|
// "\r\n\r\n"
|
|
//
|
|
|
|
pbTextToSend = ScanForTerminator( (char *) szHeader );
|
|
pbTextToSend = ( pbTextToSend == NULL )
|
|
? (BYTE * ) szHeader
|
|
: pbTextToSend;
|
|
|
|
cbSent = lstrlen( (CHAR*) pbTextToSend );
|
|
hrRet = SyncWriteClient( cbSent, pbTextToSend, &cbSent,
|
|
IO_FLAG_NO_DELAY );
|
|
|
|
goto LExit;
|
|
|
|
} else {
|
|
|
|
BOOL fFinished;
|
|
|
|
//
|
|
// If ISA specified a keep-conn change:
|
|
// Set request's keep-conn state to AND of ISA setting
|
|
// and client setting (which is request's current setting)
|
|
//
|
|
// NOTE this means that if ISA said KEEPCONN_TRUE or
|
|
// KEEPCONN_OLD_ISAPI we no-op.
|
|
//
|
|
// NOTE we do this before building headers to ensure
|
|
// correct keep-alive behavior.
|
|
//
|
|
|
|
if ( dwIsaKeepConn == KEEPCONN_FALSE
|
|
|| dwIsaKeepConn == KEEPCONN_OLD_ISAPI ) {
|
|
|
|
m_pHttpRequest->SetKeepConn( FALSE );
|
|
}
|
|
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST::SendHeader: "
|
|
"Actual keep-conn = %d "
|
|
"\n"
|
|
, m_pHttpRequest->IsKeepConnSet()
|
|
));
|
|
}
|
|
|
|
|
|
//
|
|
// Build the typical server response headers for the extension DLL
|
|
//
|
|
|
|
if ( szStatus
|
|
&& ( (!strncmp( (char *) szStatus, "401 ", sizeof("401 ")-1 ) ) ||
|
|
(!strncmp( (char *) szStatus, "407 ", sizeof("407 ")-1 ) ) )
|
|
) {
|
|
|
|
m_pHttpRequest->SetDeniedFlags( SF_DENIED_APPLICATION );
|
|
m_pHttpRequest->SetAuthenticationRequested( TRUE );
|
|
}
|
|
|
|
|
|
if ( szHeader ) {
|
|
|
|
// NYI: Ugly cast of const char * to "char *"
|
|
m_pHttpRequest->CheckForBasicAuthenticationHeader(
|
|
(char * ) szHeader
|
|
);
|
|
}
|
|
|
|
|
|
// NYI: Ugly cast of const char * to "char *"
|
|
hrRet = HresultFromBool( m_pHttpRequest->SendHeader(
|
|
(char * ) szStatus
|
|
, (char * ) (( szHeader)
|
|
? ( szHeader)
|
|
: (unsigned char * ) "\r\n")
|
|
, IO_FLAG_SYNC
|
|
, &fFinished
|
|
, 0 // dwOptions
|
|
, m_fWriteHeaders
|
|
));
|
|
|
|
|
|
// NYI: I need to figure out what this fFinished was there for
|
|
// borrowed from ext\isplocal.cxx::ServerSupportFunction()
|
|
DBG_ASSERT( !fFinished );
|
|
|
|
}
|
|
|
|
|
|
LExit:
|
|
return hrRet;
|
|
|
|
} // WAM_REQUEST::SendHeader()
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::SendEntireResponse
|
|
|
|
Description:
|
|
Sends an entire response (headers and body).
|
|
|
|
NOTE this is a private api provided as a performance optimization
|
|
for ASP
|
|
|
|
NOTE Works only in-process.
|
|
[would be LOTS of work to support oop - must marshal all buffers]
|
|
|
|
Arguments:
|
|
pvHseResponseInfo - custom struct, see iisext.x
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SendEntireResponse(
|
|
unsigned char * pvHseResponseInfo // HSE_SEND_ENTIRE_RESPONSE_INFO *
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
//
|
|
// figure out whether we need to invoke filter(s) or not.
|
|
// we need to invoke filter(s) if either the raw-data-notify
|
|
// or headers-notify flag is set.
|
|
//
|
|
// if yes, we use normal code path to invoke filter(s).
|
|
//
|
|
// if no, we use fastpath.
|
|
//
|
|
// CONSIDER support filters in fastpath code as well
|
|
//
|
|
|
|
BOOL fMustUseFilter =
|
|
m_pHttpRequest->QueryFilter()->IsNotificationNeeded(
|
|
SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_SEND_RESPONSE
|
|
, m_pHttpRequest->IsSecurePort()
|
|
);
|
|
|
|
|
|
if ( fMustUseFilter ) {
|
|
|
|
hrRet = SendEntireResponseNormal(
|
|
reinterpret_cast
|
|
<HSE_SEND_ENTIRE_RESPONSE_INFO *>
|
|
(pvHseResponseInfo)
|
|
);
|
|
|
|
} else {
|
|
|
|
hrRet = SendEntireResponseFast(
|
|
reinterpret_cast
|
|
<HSE_SEND_ENTIRE_RESPONSE_INFO *>
|
|
(pvHseResponseInfo)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
return hrRet;
|
|
|
|
|
|
} // WAM_REQUEST::SendEntireResponse()
|
|
|
|
/*++
|
|
WAM_REQUEST::SendEntireResponseAndCleanup
|
|
|
|
Description:
|
|
|
|
This routine is designed to provide the same functionality as
|
|
the SendEntireResponse method for oop applications but will do
|
|
the CleanupWamRequest call, so that no additional RPC calls are
|
|
needed.
|
|
|
|
This will enable oop asp to send the request and cleanup in
|
|
a single RPC call. Currently oop asp requests use multiple
|
|
RPC calls that do synchronous IO:
|
|
|
|
1. SendHeaders
|
|
2. WriteClient * number of response buffers
|
|
3. CleanupWamRequest
|
|
4. Release
|
|
|
|
This interface will collapse this to two RPC calls.
|
|
|
|
Possible additional work:
|
|
|
|
1. Do writes asyncronously.
|
|
2. Use shared memory handles to pass the data.
|
|
|
|
|
|
Other Notes:
|
|
|
|
Possible ISAP race conditions. The _FirstThread synch
|
|
method was removed for ease of implementation on the
|
|
WAM side. This may need to be put into place and that
|
|
code reworked if there are regressions.
|
|
|
|
Figure out what paramter types make sense thoughout
|
|
the code path. Right now I'm constantly casting from
|
|
char * to unsigned char * and back. Ick.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SendEntireResponseAndCleanup
|
|
(
|
|
IN unsigned char * szStatus
|
|
, IN DWORD cbStatus
|
|
, IN unsigned char * szHeader
|
|
, IN DWORD cbHeader
|
|
, IN OOP_RESPONSE_INFO * pOopResponseInfo
|
|
, IN unsigned char * szLogData
|
|
, IN DWORD cbLogData
|
|
, IN DWORD dwIsaKeepConn
|
|
, OUT BOOL * pfDisconnected
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
DBG_ASSERT( m_pHttpRequest->QueryClientConn()->CheckSignature() );
|
|
|
|
DBG_ASSERT( szStatus );
|
|
DBG_ASSERT( szHeader );
|
|
DBG_ASSERT( szLogData );
|
|
DBG_ASSERT( pOopResponseInfo );
|
|
DBG_ASSERT( pfDisconnected );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::SendEntireResponseAndCleanup()\n"
|
|
"szStatus: %s; cbHeader: %x;\n",
|
|
this,
|
|
szStatus,
|
|
cbHeader
|
|
));
|
|
}
|
|
|
|
// Assume that we will not do the cleanup inline
|
|
*pfDisconnected = FALSE;
|
|
|
|
//
|
|
// The most obvious thing to do with the send entire response
|
|
// for out of process is to repack the marshalled data into
|
|
// the format used by in the in process call and use that
|
|
// call.
|
|
//
|
|
|
|
HSE_SEND_ENTIRE_RESPONSE_INFO hseResponseInfo;
|
|
|
|
// populate struct with header info
|
|
hseResponseInfo.HeaderInfo.pszStatus = (LPCSTR)szStatus;
|
|
hseResponseInfo.HeaderInfo.cchStatus = cbStatus;
|
|
hseResponseInfo.HeaderInfo.pszHeader = (LPCSTR)szHeader;
|
|
hseResponseInfo.HeaderInfo.cchHeader = cbHeader;
|
|
hseResponseInfo.HeaderInfo.fKeepConn =
|
|
(dwIsaKeepConn == KEEPCONN_TRUE) ? TRUE : FALSE;
|
|
|
|
|
|
// populate the wsa buffers
|
|
hseResponseInfo.cWsaBuf = 1 + pOopResponseInfo->cBuffers;
|
|
hseResponseInfo.rgWsaBuf =
|
|
(WSABUF *)(_alloca(hseResponseInfo.cWsaBuf * sizeof(WSABUF)));
|
|
|
|
// first buffer is empty
|
|
hseResponseInfo.rgWsaBuf[0].len = 0;
|
|
hseResponseInfo.rgWsaBuf[0].buf = NULL;
|
|
|
|
for ( DWORD i = 0; i < pOopResponseInfo->cBuffers; i++ )
|
|
{
|
|
hseResponseInfo.rgWsaBuf[i+1].len = pOopResponseInfo->rgBuffers[i].cbBuffer;
|
|
hseResponseInfo.rgWsaBuf[i+1].buf = (LPSTR)pOopResponseInfo->rgBuffers[i].pbBuffer;
|
|
}
|
|
|
|
hr = SendEntireResponse( (LPBYTE)&hseResponseInfo );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
*pfDisconnected = TRUE;
|
|
|
|
DWORD dwStatus = atol( (LPSTR)szStatus );
|
|
|
|
// We want to only do the cleanup on success and send back a failure
|
|
// indication to the WAM_EXEC_INFO this would allow normal
|
|
// termination in the event that the client is disconnected, etc.
|
|
HRESULT hrCleanup = CleanupWamRequest( szLogData,
|
|
cbLogData,
|
|
dwStatus,
|
|
dwIsaKeepConn
|
|
);
|
|
DBG_ASSERT(SUCCEEDED(hrCleanup));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SendURLRedirectResponse
|
|
|
|
Descrption:
|
|
Send an URL redirect message to the browser client
|
|
|
|
Input:
|
|
pData - pointer to buffer that contains the location to
|
|
redirect the client to.
|
|
|
|
Return:
|
|
BOOL
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SendURLRedirectResponse
|
|
(
|
|
unsigned char * pData
|
|
)
|
|
{
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::SendURLRedirectResponse\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
DBG_ASSERT( m_pExec );
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
if ( m_pExec->RedirectOnly() )
|
|
{
|
|
|
|
DWORD cbLen;
|
|
STACK_STR( strMessageString, 256 );
|
|
STACK_STR( strOutputString, 512 );
|
|
|
|
cbLen = LoadString( GetModuleHandle( W3_MODULE_NAME ),
|
|
IDS_URL_MOVED,
|
|
strMessageString.QueryStr(),
|
|
256 );
|
|
if ( !cbLen )
|
|
{
|
|
hrRet = E_FAIL; // UNDONE can we be more explicit than E_FAIL
|
|
goto LExit;
|
|
}
|
|
|
|
// NYI: Check for overflows!
|
|
cbLen = wsprintf( strOutputString.QueryStr(),
|
|
strMessageString.QueryStr(),
|
|
(CHAR*) pData );
|
|
|
|
hrRet = SyncWriteClient(
|
|
cbLen,
|
|
(unsigned char *) strOutputString.QueryStr(),
|
|
&cbLen,
|
|
0 );
|
|
}
|
|
else
|
|
{
|
|
hrRet = SendRedirectMessage( (unsigned char *) pData);
|
|
}
|
|
|
|
LExit:
|
|
return hrRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SendRedirectMessage
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SendRedirectMessage
|
|
(
|
|
unsigned char * szRedirect // LPCSTR pszRedirect
|
|
)
|
|
{
|
|
STACK_STR( strURL, 512);
|
|
DWORD cb;
|
|
BOOL fFinished = FALSE;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::SendRedirectMessage( %s)\n",
|
|
this, szRedirect));
|
|
}
|
|
|
|
//
|
|
// Construct a redirect message and send it synchronously
|
|
//
|
|
|
|
// UNDONE cleanup
|
|
if ( strURL.Copy( (LPCSTR) szRedirect ) &&
|
|
m_pHttpRequest->
|
|
BuildURLMovedResponse( m_pHttpRequest->QueryRespBuf(),
|
|
&strURL,
|
|
HT_REDIRECT ) &&
|
|
m_pHttpRequest->SendHeader( m_pHttpRequest->QueryRespBufPtr(),
|
|
m_pHttpRequest->QueryRespBufCB(),
|
|
IO_FLAG_SYNC,
|
|
&fFinished ) ) {
|
|
return NOERROR;
|
|
}
|
|
|
|
return E_FAIL; // UNDONE can we say more than E_FAIL?
|
|
|
|
} // WAM_REQUEST::SendRedirectMessage()
|
|
|
|
|
|
|
|
// UNDONE remove unused ???
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetSslCtxt
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetSslCtxt
|
|
(
|
|
DWORD cbCtxtHandle,
|
|
unsigned char * pbCtxtHandle // PBYTE pbCtxtHandle
|
|
)
|
|
{
|
|
HRESULT hrRet = NOERROR; // UNDONE not reset anywhere???
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "WAM_REQUEST[%p]::GetSslCtxt( %d, %p)\n",
|
|
this, cbCtxtHandle, pbCtxtHandle));
|
|
}
|
|
|
|
//
|
|
// QuerySslCtxtHandle() returns a handle to certificate info.
|
|
// This handle will be copied to the caller if cert is available
|
|
// otherwise a NULL handle will be returned ( this is not considered
|
|
// an error)
|
|
//
|
|
|
|
if ( m_pHttpRequest->QueryAuthenticationObj()->QuerySslCtxtHandle() ) {
|
|
|
|
memcpy( pbCtxtHandle,
|
|
m_pHttpRequest->QueryAuthenticationObj()->QuerySslCtxtHandle(),
|
|
sizeof( CtxtHandle ));
|
|
} else {
|
|
// UNDONE: I need to send/set proper error code
|
|
memset( pbCtxtHandle, 0, sizeof( CtxtHandle ));
|
|
}
|
|
|
|
return hrRet;
|
|
|
|
} // WAM_REQUEST::GetSslCtxt()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetClientCertInfoEx
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetClientCertInfoEx
|
|
(
|
|
IN DWORD cbAllocated,
|
|
OUT DWORD * pdwCertEncodingType,
|
|
OUT unsigned char * pbCertEncoded,
|
|
OUT DWORD * pcbCertEncoded,
|
|
OUT DWORD * pdwCertificateFlags
|
|
)
|
|
{
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
DBG_ASSERT( m_dwSignature == WAM_REQUEST_SIGNATURE );
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetClientCertInfoEx( %d, %p)\n",
|
|
this, cbAllocated, pbCertEncoded));
|
|
}
|
|
|
|
|
|
if ( !m_pHttpRequest->QueryAuthenticationObj()->GetClientCertBlob(
|
|
cbAllocated,
|
|
pdwCertEncodingType,
|
|
pbCertEncoded,
|
|
pcbCertEncoded,
|
|
pdwCertificateFlags ) ) {
|
|
|
|
//
|
|
// if get call failed, return last error (set by callee)
|
|
//
|
|
|
|
hrRet = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
|
|
return hrRet;
|
|
|
|
} // WAM_REQUEST::GetClientCertInfoEx()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::GetSspiInfo
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetSspiInfo
|
|
(
|
|
DWORD cbCtxtHandle,
|
|
unsigned char * pbCtxtHandle, // PBYTE pbCtxtHandle
|
|
DWORD cbCredHandle,
|
|
unsigned char * pbCredHandle // PBYTE pbCredHandle
|
|
)
|
|
{
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::GetSspiInfo( %d, %p, %d, %d)\n",
|
|
this, cbCtxtHandle, pbCtxtHandle,
|
|
cbCredHandle, pbCredHandle));
|
|
}
|
|
|
|
if ( m_pHttpRequest->IsClearTextPassword() ) {
|
|
|
|
hrRet = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
goto LExit;
|
|
}
|
|
|
|
DBG_ASSERT( cbCtxtHandle == sizeof( CtxtHandle));
|
|
|
|
if ( m_pHttpRequest->QueryAuthenticationObj()->QueryCtxtHandle() ) {
|
|
|
|
memcpy( pbCtxtHandle,
|
|
m_pHttpRequest->QueryAuthenticationObj()->QueryCtxtHandle(),
|
|
sizeof( CtxtHandle ));
|
|
} else {
|
|
|
|
memset( pbCtxtHandle, 0, sizeof( CtxtHandle ));
|
|
}
|
|
|
|
DBG_ASSERT( cbCredHandle == sizeof( CredHandle));
|
|
if ( m_pHttpRequest->QueryAuthenticationObj()->QueryCredHandle() ) {
|
|
|
|
memcpy( pbCredHandle,
|
|
m_pHttpRequest->QueryAuthenticationObj()->QueryCredHandle(),
|
|
sizeof( CredHandle ));
|
|
} else {
|
|
memset( pbCredHandle, 0, sizeof( CredHandle ));
|
|
}
|
|
|
|
LExit:
|
|
return hrRet;
|
|
} // WAM_REQUEST::GetSspiInfo()
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::RequestAbortiveClose
|
|
|
|
Cover function
|
|
|
|
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::RequestAbortiveClose( VOID )
|
|
{
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::RequestAbortiveClose\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
return HresultFromBool( m_pHttpRequest->RequestAbortiveClose() );
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::CloseConnection
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::CloseConnection( VOID )
|
|
{
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::CloseConnection\n",
|
|
this
|
|
));
|
|
}
|
|
|
|
return HresultFromBool( m_pHttpRequest->CloseConnection() );
|
|
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::LogEvent
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::LogEvent( DWORD dwEventId, unsigned char * szText )
|
|
{
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
szText ?
|
|
"WAM_REQUEST[%p]::LogEvent: %d %s\n" :
|
|
"WAM_REQUEST[%p]::LogEvent: %d\n",
|
|
this,
|
|
dwEventId,
|
|
szText
|
|
));
|
|
}
|
|
|
|
|
|
//
|
|
// per KB Q129126, only SYSTEM can write to event log
|
|
// temporarily revert to self.
|
|
//
|
|
|
|
HANDLE hToken = 0;
|
|
OpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken );
|
|
|
|
|
|
//
|
|
// Drop to SYSTEM context, but only if we have user token to return to
|
|
//
|
|
|
|
if( hToken ) {
|
|
RevertToSelf( );
|
|
}
|
|
|
|
//
|
|
// We don't instantiate event log until needed - do it now
|
|
//
|
|
|
|
if( g_pWamEventLog == NULL ) {
|
|
|
|
g_pWamEventLog = new EVENT_LOG( "WAM" );
|
|
if( (g_pWamEventLog == NULL) || !g_pWamEventLog->Success() ) {
|
|
DWORD dwError = g_pWamEventLog ?
|
|
g_pWamEventLog->GetErrorCode() : ERROR_NOT_ENOUGH_MEMORY;
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failure to init WAM event log (%d)\n",
|
|
dwError
|
|
));
|
|
if(g_pWamEventLog) {
|
|
delete g_pWamEventLog;
|
|
g_pWamEventLog = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( g_pWamEventLog != NULL ) {
|
|
|
|
//
|
|
// We let EVENT_LOG object handle any problems that may occur here
|
|
//
|
|
|
|
const CHAR * apsz[1];
|
|
apsz[0] = (const CHAR *)szText;
|
|
g_pWamEventLog->LogEvent( dwEventId, 1, apsz, 0 );
|
|
}
|
|
|
|
if( hToken ) {
|
|
SetThreadToken(NULL, hToken);
|
|
}
|
|
|
|
//
|
|
// There is really no point in returning any error from here --
|
|
// our callers have enough problems already
|
|
//
|
|
|
|
return HresultFromBool( TRUE );
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
WAM_REQUEST::SSIncExec
|
|
|
|
Descrption:
|
|
Executes SSInc #exec
|
|
|
|
Input:
|
|
szCommand - command
|
|
dwExecFlags - HSE_EXEC_???
|
|
pszVerb - verb
|
|
|
|
Return:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::SSIncExec
|
|
(
|
|
unsigned char *szCommand,
|
|
DWORD dwExecFlags,
|
|
unsigned char *szVerb
|
|
)
|
|
{
|
|
|
|
HRESULT hrRet = NOERROR;
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"WAM_REQUEST[%p]::SSIncExec\n"
|
|
,
|
|
this
|
|
));
|
|
}
|
|
|
|
//
|
|
// Ref before child exec operation, in case child exec
|
|
// introduces an asynchronicity.
|
|
//
|
|
|
|
AddRef();
|
|
|
|
//
|
|
// temporarily reset the binding of the WAM_REQUEST to HTTP_REQUEST
|
|
// this will ensure that we do not tramp on rebinding a new WAM_REQUEST
|
|
// to the same HTTP_REQUEST
|
|
//
|
|
|
|
DBG_ASSERT( m_pHttpRequest->QueryWamRequest() == this );
|
|
m_pHttpRequest->SetWamRequest( NULL );
|
|
|
|
|
|
if ( dwExecFlags & HSE_EXEC_COMMAND ) {
|
|
|
|
hrRet = HresultFromBool( m_pHttpRequest->ExecuteChildCommand(
|
|
(char *)szCommand,
|
|
dwExecFlags ));
|
|
|
|
} else {
|
|
|
|
hrRet = HresultFromBool( m_pHttpRequest->ExecuteChildCGIBGI(
|
|
(char *)szCommand,
|
|
dwExecFlags,
|
|
(char *)szVerb ));
|
|
|
|
}
|
|
|
|
//
|
|
// Restore the binding back.
|
|
//
|
|
|
|
DBG_ASSERT( m_pHttpRequest->QueryWamRequest() == NULL );
|
|
m_pHttpRequest->SetWamRequest( this );
|
|
|
|
//
|
|
// Deref after child exec operation
|
|
//
|
|
|
|
Release();
|
|
|
|
return hrRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::GetAspMDAllData
|
|
|
|
Descrption:
|
|
Private api for ASP that returns asp metadata in a buffer. Equivalent
|
|
of MD GetAllData.
|
|
|
|
Parameters:
|
|
|
|
Return:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetAspMDAllData(
|
|
IN unsigned char * pszMDPath
|
|
, IN DWORD dwMDUserType
|
|
, IN DWORD dwDefaultBufferSize
|
|
, OUT unsigned char * pBuffer
|
|
, OUT DWORD * pdwRequiredBufferSize
|
|
, OUT DWORD * pdwNumDataEntries
|
|
)
|
|
{
|
|
|
|
HRESULT hr = NOERROR, hrT;
|
|
BOOL fT;
|
|
METADATA_GETALL_RECORD *pMDGetAllRec = (METADATA_GETALL_RECORD *)pBuffer;
|
|
DWORD dwDataSetNumber = 0;
|
|
IMDCOM* pMetabase = g_pWamDictator->PMetabase()->QueryPMDCOM();
|
|
METADATA_HANDLE hMetabase = NULL;
|
|
const DWORD dwMDDefaultTimeOut = 2000;
|
|
|
|
DBG_ASSERT(pMetabase != NULL);
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::GetAspMDAllData\n"
|
|
, this
|
|
));
|
|
}
|
|
|
|
// Only allow the return of the data that ASP needs. Dont allow anything else
|
|
if (dwMDUserType != IIS_MD_UT_WAM && dwMDUserType != ASP_MD_UT_APP)
|
|
return(E_ACCESSDENIED);
|
|
|
|
// Open the metabase key
|
|
hr = pMetabase->ComMDOpenMetaObjectA(METADATA_MASTER_ROOT_HANDLE, pszMDPath,
|
|
METADATA_PERMISSION_READ, dwMDDefaultTimeOut, &hMetabase);
|
|
if (FAILED(hr)) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::Metadata open failed %x\n"
|
|
, this
|
|
, hr
|
|
));
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
DBG_ASSERT( hMetabase );
|
|
|
|
hr = pMetabase->ComMDGetAllMetaDataW( hMetabase,
|
|
L"",
|
|
METADATA_INHERIT,
|
|
dwMDUserType,
|
|
ALL_METADATA,
|
|
pdwNumDataEntries,
|
|
&dwDataSetNumber,
|
|
dwDefaultBufferSize,
|
|
(unsigned char *)pBuffer,
|
|
pdwRequiredBufferSize
|
|
);
|
|
|
|
hrT = pMetabase->ComMDCloseMetaObject(hMetabase);
|
|
DBG_ASSERT(SUCCEEDED(hrT));
|
|
|
|
return hr;
|
|
|
|
} // WAM_REQUEST::GetAspMDAllData
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::GetAspMDData
|
|
|
|
Descrption:
|
|
Private api for ASP that returns asp metadata in a buffer. Equivalent
|
|
of MD GetAllData.
|
|
|
|
Parameters:
|
|
|
|
Return:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetAspMDData(
|
|
IN unsigned char * pszMDPath
|
|
, IN DWORD dwMDIdentifier
|
|
, IN DWORD dwMDAttributes
|
|
, IN DWORD dwMDUserType
|
|
, IN DWORD dwMDDataType
|
|
, IN DWORD dwMDDataLen
|
|
, IN DWORD dwMDDataTag
|
|
, OUT unsigned char * pbMDData
|
|
, OUT DWORD * pdwRequiredBufferSize
|
|
)
|
|
{
|
|
|
|
HRESULT hr = NOERROR, hrT;
|
|
BOOL fT;
|
|
IMDCOM* pMetabase = g_pWamDictator->PMetabase()->QueryPMDCOM();
|
|
METADATA_HANDLE hMetabase = NULL;
|
|
const DWORD dwMDDefaultTimeOut = 2000;
|
|
METADATA_RECORD MDRec;
|
|
|
|
DBG_ASSERT(pMetabase != NULL);
|
|
|
|
IF_DEBUG( WAM_ISA_CALLS ) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::GetAspMDData\n"
|
|
, this
|
|
));
|
|
}
|
|
|
|
// Only allow the return of the data that ASP needs. Dont allow anything else
|
|
if (dwMDIdentifier != MD_SERVER_COMMENT && // used by ASP debugger
|
|
dwMDIdentifier != MD_APP_FRIENDLY_NAME && // used by ASP debugger
|
|
dwMDIdentifier != MD_APP_WAM_CLSID &&
|
|
dwMDIdentifier != MD_APP_ISOLATED)
|
|
return(E_ACCESSDENIED);
|
|
|
|
// Open the metabase key
|
|
hr = pMetabase->ComMDOpenMetaObjectA(METADATA_MASTER_ROOT_HANDLE, pszMDPath,
|
|
METADATA_PERMISSION_READ, dwMDDefaultTimeOut, &hMetabase);
|
|
if (FAILED(hr)) {
|
|
|
|
DBGPRINTF((
|
|
DBG_CONTEXT
|
|
, "WAM_REQUEST[%p]::Metadata open failed %x\n"
|
|
, this
|
|
, hr
|
|
));
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
DBG_ASSERT( hMetabase );
|
|
|
|
MDRec.dwMDIdentifier = dwMDIdentifier;
|
|
MDRec.dwMDAttributes = dwMDAttributes;
|
|
MDRec.dwMDUserType = dwMDUserType;
|
|
MDRec.dwMDDataType = dwMDDataType;
|
|
MDRec.dwMDDataLen = dwMDDataLen;
|
|
MDRec.pbMDData = pbMDData;
|
|
MDRec.dwMDDataTag = dwMDDataTag;
|
|
|
|
hr = pMetabase->ComMDGetMetaDataW( hMetabase,
|
|
L"",
|
|
&MDRec,
|
|
pdwRequiredBufferSize
|
|
);
|
|
|
|
hrT = pMetabase->ComMDCloseMetaObject(hMetabase);
|
|
DBG_ASSERT(SUCCEEDED(hrT));
|
|
|
|
return hr;
|
|
|
|
} // WAM_REQUEST::GetAspMDAllData
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::GetCustomError
|
|
|
|
Description:
|
|
Private API for ASP that returns custom error.
|
|
|
|
Parameters:
|
|
dwError error code
|
|
dwSubError sub error code
|
|
dwBufferSize supplied buffer size for URL/file
|
|
pbBuffer supplied buffer for URL/file
|
|
in case of file, mime type gets
|
|
concatinated to the file path
|
|
pdwRequiredBufferSize [out] required buffer size
|
|
pfIsFileError [out] flag: is file? (not URL)
|
|
|
|
Return:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::GetCustomError
|
|
(
|
|
DWORD dwError,
|
|
DWORD dwSubError,
|
|
DWORD dwBufferSize,
|
|
unsigned char *pbBuffer,
|
|
DWORD *pdwRequiredBufferSize,
|
|
BOOL *pfIsFileError
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
PCUSTOM_ERROR_ENTRY pceError;
|
|
|
|
DBG_ASSERT( m_pHttpRequest );
|
|
|
|
pceError = m_pHttpRequest->GetWAMMetaData()->LookupCustomError(
|
|
dwError,
|
|
dwSubError
|
|
);
|
|
if ( pceError) {
|
|
|
|
CHAR *szError = NULL; // URL or file
|
|
DWORD cchError = 0; // count of chars
|
|
|
|
STR strMimeType; // file mime type STR
|
|
CHAR *szMimeType = NULL; // file mime type CHAR*
|
|
DWORD cchMimeType = 0; // count of chars
|
|
|
|
if ( pceError->IsFileError()) {
|
|
*pfIsFileError = TRUE;
|
|
szError = pceError->QueryErrorFileName();
|
|
|
|
// get the mimetype
|
|
if ( SelectMimeMapping( &strMimeType,
|
|
szError,
|
|
m_pHttpRequest->GetWAMMetaData(),
|
|
MIMEMAP_MIME_TYPE)) {
|
|
|
|
szMimeType = strMimeType.QueryStr();
|
|
cchMimeType = strMimeType.QueryCCH();
|
|
}
|
|
|
|
// mime type is required -- no mime type = bad custom error
|
|
if ( NULL == szMimeType) {
|
|
szError = NULL;
|
|
}
|
|
}
|
|
else {
|
|
// URL
|
|
*pfIsFileError = FALSE;
|
|
szError = pceError->QueryErrorURL();
|
|
}
|
|
|
|
if ( szError) {
|
|
|
|
cchError = strlen( szError);
|
|
*pdwRequiredBufferSize = cchError + 1 + cchMimeType + 1;
|
|
|
|
if ( dwBufferSize >= *pdwRequiredBufferSize) {
|
|
|
|
memcpy( pbBuffer, szError, cchError+1);
|
|
|
|
if ( szMimeType) {
|
|
memcpy( pbBuffer+cchError+1, szMimeType, cchMimeType+1);
|
|
}
|
|
else {
|
|
pbBuffer[cchError+1] = '\0';
|
|
}
|
|
}
|
|
else {
|
|
|
|
// doesn't fit into supplied buffer
|
|
hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
else {
|
|
|
|
// malformed custom error
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
// custom error not found
|
|
hr = TYPE_E_ELEMENTNOTFOUND;
|
|
}
|
|
|
|
return hr;
|
|
|
|
} // WAM_REQUEST::GetCustomError
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
WAM_REQUEST::TestConnection
|
|
|
|
Description:
|
|
Private API for ASP that tests the IP connection to the client.
|
|
|
|
Parameters:
|
|
pfIsConnected [out] flag: is [probably] connected?
|
|
|
|
Return:
|
|
HRESULT
|
|
|
|
*/
|
|
STDMETHODIMP
|
|
WAM_REQUEST::TestConnection(
|
|
BOOL *pfIsConnected
|
|
)
|
|
{
|
|
|
|
*pfIsConnected = m_pHttpRequest->TestConnection();
|
|
|
|
return NOERROR;
|
|
|
|
} // WAM_REQUEST::TestConnection
|
|
|
|
STDMETHODIMP
|
|
WAM_REQUEST::ExtensionTrigger(
|
|
unsigned char * pvContext,
|
|
DWORD dwTriggerType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Invoke ISAPI filters waiting on SF_NOTIFY_EXTENSION_TRIGGER
|
|
|
|
Arguments:
|
|
|
|
pvContext - Trigger context pointer
|
|
dwTriggerType - Type of trigger
|
|
|
|
Returns:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
BOOL fRet;
|
|
|
|
fRet = m_pHttpRequest->QueryFilter()->NotifyExtensionTriggerFilters(
|
|
(PVOID)pvContext,
|
|
dwTriggerType );
|
|
|
|
return HresultFromBool( fRet );
|
|
}
|
|
|
|
|
|
/************************************************************
|
|
* Static Member Functions of WAM_REQUEST
|
|
************************************************************/
|
|
|
|
/*-----------------------------------------------------------------------------*
|
|
Support for WAM_REQUEST allocation cache
|
|
|
|
*/
|
|
|
|
BOOL
|
|
WAM_REQUEST::InitClass( VOID)
|
|
{
|
|
ALLOC_CACHE_CONFIGURATION acConfig = { 1, WAM_REQUEST_CACHE_THRESHOLD,
|
|
sizeof(WAM_REQUEST)};
|
|
|
|
if ( NULL != sm_pachWamRequest) {
|
|
|
|
// already initialized
|
|
return ( TRUE);
|
|
}
|
|
|
|
sm_pachWamRequest = new ALLOC_CACHE_HANDLER( "WamRequest",
|
|
&acConfig);
|
|
|
|
#if DBG
|
|
sm_pDbgRefTraceLog = CreateRefTraceLog( C_REFTRACES_GLOBAL, 0 );
|
|
#endif
|
|
|
|
//
|
|
// Initialize class static request ID
|
|
//
|
|
sm_dwRequestID = 0;
|
|
|
|
return ( NULL != sm_pachWamRequest);
|
|
} // WAM_REQUEST::InitClass()
|
|
|
|
|
|
VOID
|
|
WAM_REQUEST::CleanupClass( VOID)
|
|
{
|
|
if ( NULL != sm_pachWamRequest) {
|
|
|
|
delete sm_pachWamRequest;
|
|
sm_pachWamRequest = NULL;
|
|
}
|
|
|
|
#if DBG
|
|
DestroyRefTraceLog( sm_pDbgRefTraceLog );
|
|
#endif
|
|
|
|
return;
|
|
} // WAM_REQUEST::CleanupClass()
|
|
|
|
|
|
void *
|
|
WAM_REQUEST::operator new( size_t s)
|
|
{
|
|
DBG_ASSERT( s == sizeof( WAM_REQUEST));
|
|
|
|
// allocate from allocation cache.
|
|
DBG_ASSERT( NULL != sm_pachWamRequest);
|
|
return (sm_pachWamRequest->Alloc());
|
|
} // WAM_REQUEST::operator new()
|
|
|
|
void
|
|
WAM_REQUEST::operator delete( void * pwr)
|
|
{
|
|
DBG_ASSERT( NULL != pwr);
|
|
|
|
// free to the allocation pool
|
|
DBG_ASSERT( NULL != sm_pachWamRequest);
|
|
DBG_REQUIRE( sm_pachWamRequest->Free(pwr));
|
|
|
|
return;
|
|
} // WAM_REQUEST::operator delete()
|
|
|
|
/************************ End of File ***********************/
|