windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/ulw3/rawconnection.cxx
2020-09-26 16:20:57 +08:00

1944 lines
45 KiB
C++

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name :
rawconnection.cxx
Abstract:
ISAPI raw data filter support
Author:
Bilal Alam (balam) 10-Jan-2000
Environment:
Win32 - User Mode
Project:
ULW3.DLL
--*/
#include "precomp.hxx"
#include "rawconnection.hxx"
RAW_CONNECTION_HASH * RAW_CONNECTION::sm_pRawConnectionHash;
RAW_CONNECTION::RAW_CONNECTION(
CONNECTION_INFO * pConnectionInfo
)
{
_cRefs = 1;
_pMainContext = NULL;
_dwCurrentFilter = INVALID_DLL;
DBG_ASSERT( pConnectionInfo != NULL );
_hfc.cbSize = sizeof( _hfc );
_hfc.Revision = HTTP_FILTER_REVISION;
_hfc.ServerContext = (void *) this;
_hfc.ulReserved = 0;
_hfc.fIsSecurePort = pConnectionInfo->fIsSecure;
_hfc.pFilterContext = NULL;
_hfc.ServerSupportFunction = RawFilterServerSupportFunction;
_hfc.GetServerVariable = RawFilterGetServerVariable;
_hfc.AddResponseHeaders = RawFilterAddResponseHeaders;
_hfc.WriteClient = RawFilterWriteClient;
_hfc.AllocMem = RawFilterAllocateMemory;
ZeroMemory( &_rgContexts, sizeof( _rgContexts ) );
InitializeListHead( &_PoolHead );
_pfnSendDataBack = pConnectionInfo->pfnSendDataBack;
_pvStreamContext = pConnectionInfo->pvStreamContext;
_LocalPort = pConnectionInfo->LocalPort;
_LocalAddress = pConnectionInfo->LocalAddress;
_RemotePort = pConnectionInfo->RemotePort;
_RemoteAddress = pConnectionInfo->RemoteAddress;
_RawConnectionId = pConnectionInfo->RawConnectionId;
_dwSignature = RAW_CONNECTION_SIGNATURE;
}
RAW_CONNECTION::~RAW_CONNECTION()
{
FILTER_POOL_ITEM * pfpi;
_dwSignature = RAW_CONNECTION_SIGNATURE_FREE;
//
// Free pool items (is most cases there won't be any since they will
// have been migrated to the W3_FILTER_CONNECTION_CONTEXT)
//
while ( !IsListEmpty( &_PoolHead ) )
{
pfpi = CONTAINING_RECORD( _PoolHead.Flink,
FILTER_POOL_ITEM,
_ListEntry );
RemoveEntryList( &pfpi->_ListEntry );
delete pfpi;
}
//
// Disconnect raw connection from main context
//
if ( _pMainContext != NULL )
{
_pMainContext->DereferenceMainContext();
_pMainContext = NULL;
}
}
//static
HRESULT
RAW_CONNECTION::Initialize(
VOID
)
/*++
Routine Description:
Initialize ISAPI raw data filter crap
Arguments:
None
Return Value:
HRESULT
--*/
{
FILTER_LIST * pFilterList;
BOOL fSSLOnly = TRUE;
HRESULT hr;
STREAM_FILTER_CONFIG sfConfig;
DBG_ASSERT( g_pW3Server != NULL );
DBG_ASSERT( sm_pRawConnectionHash == NULL );
//
// Create a UL_RAW_CONNECTION_ID keyed hash table
//
sm_pRawConnectionHash = new RAW_CONNECTION_HASH;
if ( sm_pRawConnectionHash == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// Is there a read raw data filter enabled?
//
pFilterList = FILTER_LIST::QueryGlobalList();
if ( pFilterList != NULL )
{
if ( pFilterList->IsNotificationNeeded( SF_NOTIFY_READ_RAW_DATA,
FALSE ) )
{
fSSLOnly = FALSE;
}
}
//
// Now initialize stream filter DLL
//
sfConfig.fSslOnly = fSSLOnly;
sfConfig.pfnRawRead = RAW_CONNECTION::ProcessRawRead;
sfConfig.pfnRawWrite = RAW_CONNECTION::ProcessRawWrite;
sfConfig.pfnConnectionClose = RAW_CONNECTION::ProcessConnectionClose;
sfConfig.pfnNewConnection = RAW_CONNECTION::ProcessNewConnection;
hr = StreamFilterInitialize( &sfConfig );
if ( FAILED( hr ) )
{
delete sm_pRawConnectionHash;
sm_pRawConnectionHash = NULL;
return hr;
}
return NO_ERROR;
}
//static
VOID
RAW_CONNECTION::Terminate(
VOID
)
/*++
Routine Description:
Terminate raw connection hash table
Arguments:
None
Return Value:
None
--*/
{
StreamFilterTerminate();
if ( sm_pRawConnectionHash != NULL )
{
delete sm_pRawConnectionHash;
sm_pRawConnectionHash = NULL;
}
}
//static
HRESULT
RAW_CONNECTION::StopListening(
VOID
)
/*++
Routine Description:
Begin shutdown by preventing further raw stream messages from UL
Arguments:
None
Return Value:
HRESULT
--*/
{
StreamFilterStop();
return NO_ERROR;
}
//static
HRESULT
RAW_CONNECTION::StartListening(
VOID
)
/*++
Routine Description:
Start listening for stream messages from UL. Unlike UlAtqStartListen(),
this routine does NOT block and will return once the initial number
of outstanding UlFilterAccept() requests have been made
Arguments:
None
Return Value:
HRESULT
--*/
{
return StreamFilterStart();
}
FILTER_LIST *
RAW_CONNECTION::QueryFilterList(
VOID
)
/*++
Routine Description:
Return the appropriate filter list to notify. Before a W3_CONNECTION
is established, this list will simply be the global filter list. But
once the W3_CONNECTION is established, the list will be the appropriate
instance filter list
Arguments:
None
Return Value:
FILTER_LIST *
--*/
{
W3_FILTER_CONTEXT * pFilterContext = NULL;
FILTER_LIST * pFilterList = FILTER_LIST::QueryGlobalList();
if ( _pMainContext != NULL )
{
pFilterContext = _pMainContext->QueryFilterContext();
if ( pFilterContext != NULL )
{
pFilterList = pFilterContext->QueryFilterList();
DBG_ASSERT( pFilterList != NULL );
}
}
return pFilterList;
}
BOOL
RAW_CONNECTION::QueryNotificationChanged(
VOID
)
/*++
Routine Description:
Returns whether or not any notifications have been disabled on the fly
Arguments:
None
Return Value:
BOOL
--*/
{
W3_FILTER_CONTEXT * pFilterContext;
if ( _pMainContext == NULL )
{
//
// BUGBUG
//
// This isn't totally correct. I guess a read raw filter could
// disable any more notifications for itself before a
// W3_CONNECTION is created. This is really corner
//
return FALSE;
}
else
{
pFilterContext = _pMainContext->QueryFilterContext();
if ( pFilterContext == NULL )
{
return FALSE;
}
else
{
return pFilterContext->QueryNotificationChanged();
}
}
}
BOOL
RAW_CONNECTION::IsDisableNotificationNeeded(
DWORD dwFilter,
DWORD dwNotification
)
/*++
Routine Description:
If a notification was disabled on the fly, then this routine goes thru
the notification copy path to find whether the given notification is
indeed enabled
Arguments:
dwFilter - Filter number
dwNotification - Notification to check for
Return Value:
BOOL (TRUE is the notification is needed)
--*/
{
W3_FILTER_CONTEXT * pFilterContext;
//
// The only way we could be here is if we determined notifications were
// disabled. That can only happen if we found a W3_CONNECTION associated
//
DBG_ASSERT( _pMainContext != NULL );
pFilterContext = _pMainContext->QueryFilterContext();
DBG_ASSERT( pFilterContext != NULL );
return pFilterContext->IsDisableNotificationNeeded( dwFilter,
dwNotification );
}
PVOID
RAW_CONNECTION::QueryClientContext(
DWORD dwFilter
)
/*++
Routine Description:
Retrieve the filter client context for the given filter
Arguments:
dwFilter - Filter number
Return Value:
Context pointer
--*/
{
//
// If we have a main context associated, then use its merged context
// list
//
if ( _pMainContext == NULL )
{
return _rgContexts[ dwFilter ];
}
else
{
return _pMainContext->QueryFilterContext()->QueryClientContext( dwFilter );
}
}
VOID
RAW_CONNECTION::SetClientContext(
DWORD dwFilter,
PVOID pvContext
)
/*++
Routine Description:
Set client context for the given filter
Arguments:
dwFilter - Filter number
pvContext - Client context
Return Value:
None
--*/
{
//
// If we have a main context, use its merged context list
//
if ( _pMainContext == NULL )
{
_rgContexts[ dwFilter ] = pvContext;
}
else
{
_pMainContext->QueryFilterContext()->SetClientContext( dwFilter,
pvContext );
}
}
HRESULT
RAW_CONNECTION::GetLimitedServerVariables(
LPSTR pszVariableName,
PVOID pvBuffer,
PDWORD pdwSize
)
/*++
Routine Description:
Get the server variables which are possible given that we haven't parsed
the HTTP request yet
Arguments:
pszVariableName - Variable name
pvBuffer - Buffer to receive variable data
pdwSize - On input size of buffer, on output the size needed
Return Value:
HRESULT
--*/
{
STACK_STRA( strVariable, 256 );
HRESULT hr = NO_ERROR;
CHAR achNumber[ 64 ];
if ( strcmp( pszVariableName, "SERVER_PORT" ) == 0 ||
strcmp( pszVariableName, "REMOTE_PORT" ) == 0 )
{
_itoa( pszVariableName[ 0 ] == 'S' ?
_LocalPort : _RemotePort,
achNumber,
10 );
hr = strVariable.Copy( achNumber );
}
else if ( strcmp( pszVariableName, "REMOTE_ADDR" ) == 0 ||
strcmp( pszVariableName, "LOCAL_ADDR" ) == 0 )
{
DWORD dwAddr;
CHAR szAddr[16];
in_addr inAddr;
dwAddr = pszVariableName[ 0 ] == 'L' ?_LocalAddress : _RemoteAddress;
//
// The dwAddr is reverse order from in_addr...
//
inAddr.S_un.S_un_b.s_b1 = (u_char)(( dwAddr & 0xff000000 ) >> 24);
inAddr.S_un.S_un_b.s_b2 = (u_char)(( dwAddr & 0x00ff0000 ) >> 16);
inAddr.S_un.S_un_b.s_b3 = (u_char)(( dwAddr & 0x0000ff00 ) >> 8);
inAddr.S_un.S_un_b.s_b4 = (u_char) ( dwAddr & 0x000000ff );
LPSTR pszAddr = inet_ntoa( inAddr );
if ( pszAddr == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
hr = strVariable.Copy( pszAddr );
}
else
{
hr = strVariable.Copy( "" );
}
return strVariable.CopyToBuffer( (LPSTR) pvBuffer, pdwSize );
}
//static
BOOL
WINAPI
RAW_CONNECTION::RawFilterServerSupportFunction(
HTTP_FILTER_CONTEXT * pfc,
enum SF_REQ_TYPE SupportFunction,
void * pData,
ULONG_PTR ul,
ULONG_PTR ul2
)
/*++
Routine Description:
Stream filter SSF crap
Arguments:
pfc - Used to get back the W3_FILTER_CONTEXT and W3_MAIN_CONTEXT pointers
SupportFunction - SSF to invoke (see ISAPI docs)
pData, ul, ul2 - Function specific data
Return Value:
BOOL (use GetLastError() for error)
--*/
{
RAW_CONNECTION * pRawConnection;
HRESULT hr = NO_ERROR;
BOOL fRet;
//
// Primitive parameter validation
//
if ( pfc == NULL ||
pfc->ServerContext == NULL )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
pRawConnection = (RAW_CONNECTION*) pfc->ServerContext;
DBG_ASSERT( pRawConnection->CheckSignature() );
switch ( SupportFunction )
{
case SF_REQ_SEND_RESPONSE_HEADER:
hr = pRawConnection->SendResponseHeader( (CHAR*) pData,
(CHAR*) ul,
pfc );
break;
case SF_REQ_ADD_HEADERS_ON_DENIAL:
hr = pRawConnection->AddDenialHeaders( (CHAR*) pData );
break;
case SF_REQ_SET_NEXT_READ_SIZE:
pRawConnection->SetNextReadSize( (DWORD) ul );
break;
default:
DBG_ASSERT( FALSE );
hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
if ( FAILED( hr ) )
{
SetLastError( WIN32_FROM_HRESULT( hr ) );
return FALSE;
}
return TRUE;
}
//static
BOOL
WINAPI
RAW_CONNECTION::RawFilterGetServerVariable(
HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize
)
/*++
Routine Description:
Stream filter GetServerVariable() implementation
Arguments:
pfc - Filter context
lpszVariableName - Variable name
lpvBuffer - Buffer to receive the server variable
lpdwSize - On input, the size of the buffer, on output, the sized needed
Return Value:
BOOL (use GetLastError() for error).
ERROR_INSUFFICIENT_BUFFER if larger buffer needed
ERROR_INVALID_INDEX if the server variable name requested is invalid
--*/
{
HRESULT hr = NO_ERROR;
RAW_CONNECTION * pRawConnection = NULL;
W3_MAIN_CONTEXT * pMainContext;
//
// Primitive parameter validation
//
if ( pfc == NULL ||
pfc->ServerContext == NULL ||
lpdwSize == NULL )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
pRawConnection = (RAW_CONNECTION*) pfc->ServerContext;
DBG_ASSERT( pRawConnection->CheckSignature() );
//
// If we have a W3_CONNECTION associated, then use its context to
// get at server variables. Otherwise we can only serve the ones that
// make sense
//
pMainContext = pRawConnection->QueryMainContext();
if ( pMainContext != NULL )
{
hr = SERVER_VARIABLE_HASH::GetServerVariable( pMainContext,
lpszVariableName,
(CHAR*) lpvBuffer,
lpdwSize );
}
else
{
//
// We can supply only a few (since we haven't parsed the request yet)
//
hr = pRawConnection->GetLimitedServerVariables( lpszVariableName,
lpvBuffer,
lpdwSize );
}
if ( FAILED( hr ) )
{
SetLastError( WIN32_FROM_HRESULT( hr ) );
return FALSE;
}
return TRUE;
}
//static
BOOL
WINAPI
RAW_CONNECTION::RawFilterWriteClient(
HTTP_FILTER_CONTEXT * pfc,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwReserved
)
/*++
Routine Description:
Synchronous WriteClient() for stream filter
Arguments:
pfc - Filter context
Buffer - buffer to write to client
lpdwBytes - On input, the size of the input buffer. On output, the number
of bytes sent
dwReserved - Reserved
Return Value:
BOOL (use GetLastError() for error).
--*/
{
HRESULT hr;
RAW_CONNECTION * pRawConnection = NULL;
PVOID pvContext;
RAW_STREAM_INFO rawStreamInfo;
BOOL fComplete = FALSE;
//
// Primitive parameter validation
//
if ( pfc == NULL ||
pfc->ServerContext == NULL ||
Buffer == NULL )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
pRawConnection = (RAW_CONNECTION*) pfc->ServerContext;
DBG_ASSERT( pRawConnection->CheckSignature() );
//
// Remember the filter context since calling filters will overwrite it
//
pvContext = pfc->pFilterContext;
//
// We need to notify all write raw data filters which are a higher
// priority than the current filter
//
if ( pRawConnection->_dwCurrentFilter > 0 )
{
rawStreamInfo.pbBuffer = (BYTE*) Buffer;
rawStreamInfo.cbBuffer = *lpdwBytes;
rawStreamInfo.cbData = rawStreamInfo.cbBuffer;
hr = pRawConnection->NotifyRawWriteFilters( &rawStreamInfo,
&fComplete,
pRawConnection->_dwCurrentFilter - 1 );
if ( FAILED( hr ) )
{
goto Finished;
}
}
pfc->pFilterContext = pvContext;
//
// Now call back into the stream filter to send the data. In transmit
// SSL might do its thing with the data as well
//
hr = pRawConnection->_pfnSendDataBack( pRawConnection->_pvStreamContext,
&rawStreamInfo );
if ( FAILED( hr ) )
{
goto Finished;
}
return TRUE;
Finished:
if ( FAILED( hr ) )
{
SetLastError( WIN32_FROM_HRESULT( hr ) );
return FALSE;
}
return TRUE;
}
//static
VOID *
WINAPI
RAW_CONNECTION::RawFilterAllocateMemory(
HTTP_FILTER_CONTEXT * pfc,
DWORD cbSize,
DWORD dwReserved
)
/*++
Routine Description:
Used by filters to allocate memory freed on connection close
Arguments:
pfc - Filter context
cbSize - Amount to allocate
dwReserved - Reserved
Return Value:
A pointer to the allocated memory
--*/
{
RAW_CONNECTION * pRawConnection = NULL;
//
// Primitive parameter validation
//
if ( pfc == NULL ||
pfc->ServerContext == NULL )
{
SetLastError( ERROR_INVALID_PARAMETER );
return NULL;
}
pRawConnection = (RAW_CONNECTION*) pfc->ServerContext;
DBG_ASSERT( pRawConnection->CheckSignature() );
return pRawConnection->AllocateFilterMemory( cbSize );
}
//static
BOOL
WINAPI
RAW_CONNECTION::RawFilterAddResponseHeaders(
HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszHeaders,
DWORD dwReserved
)
/*++
Routine Description:
Add response headers to whatever response eventually gets sent
Arguments:
pfc - Filter context
lpszHeaders - Headers to send (\r\n delimited)
dwReserved - Reserved
Return Value:
BOOL (use GetLastError() for error).
--*/
{
HRESULT hr;
RAW_CONNECTION * pRawConnection;
W3_MAIN_CONTEXT * pMainContext = NULL;
W3_FILTER_CONTEXT * pFilterContext;
//
// Primitive parameter validation
//
if ( pfc == NULL ||
pfc->ServerContext == NULL ||
lpszHeaders == NULL )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
pRawConnection = (RAW_CONNECTION*) pfc->ServerContext;
DBG_ASSERT( pRawConnection->CheckSignature() );
pMainContext = pRawConnection->QueryMainContext();
if ( pMainContext != NULL )
{
pFilterContext = pMainContext->QueryFilterContext();
DBG_ASSERT( pFilterContext != NULL );
hr = pFilterContext->AddResponseHeaders( lpszHeaders );
}
else
{
hr = pRawConnection->AddResponseHeaders( lpszHeaders );
}
if ( FAILED( hr ) )
{
SetLastError( WIN32_FROM_HRESULT( hr ) );
return FALSE;
}
return TRUE;
}
//static
HRESULT
RAW_CONNECTION::ProcessNewConnection(
CONNECTION_INFO * pConnectionInfo,
PVOID * ppConnectionState
)
/*++
Routine Description:
Called for every new raw connection to server
Arguments:
pConnectionInfo - Information about the local/remote addresses
ppConnectionState - Connection state to be associated with raw connection
Return Value:
HRESULT
--*/
{
RAW_CONNECTION * pConnection = NULL;
LK_RETCODE lkrc;
if ( pConnectionInfo == NULL ||
ppConnectionState == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*ppConnectionState = NULL;
//
// Try to create and add the connection
//
pConnection = new RAW_CONNECTION( pConnectionInfo );
if ( pConnection == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
lkrc = sm_pRawConnectionHash->InsertRecord( pConnection );
if ( lkrc != LK_SUCCESS )
{
pConnection->DereferenceRawConnection();
pConnection = NULL;
return HRESULT_FROM_WIN32( lkrc );
}
*ppConnectionState = pConnection;
return NO_ERROR;
}
//static
HRESULT
RAW_CONNECTION::ProcessRawRead(
RAW_STREAM_INFO * pRawStreamInfo,
PVOID pContext,
BOOL * pfReadMore,
BOOL * pfComplete,
DWORD * pcbNextReadSize
)
/*++
Routine Description:
Notify ISAPI read raw data filters
Arguments:
pRawStreamInfo - The raw stream to muck with
pContext - Raw connection context
pfReadMore - Set to TRUE if we need to read more data
pfComplete - Set to TRUE if we want to disconnect client
pcbNextReadSize - Set to next read size (0 means use default size)
Return Value:
HRESULT
--*/
{
RAW_CONNECTION * pConnection = NULL;
HRESULT hr = NO_ERROR;
W3_MAIN_CONTEXT * pMainContext;
if ( pRawStreamInfo == NULL ||
pfReadMore == NULL ||
pfComplete == NULL ||
pContext == NULL ||
pcbNextReadSize == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfReadMore = FALSE;
*pfComplete = FALSE;
pConnection = (RAW_CONNECTION*) pContext;
DBG_ASSERT( pConnection->CheckSignature() );
pConnection->SetNextReadSize( 0 );
//
// Synchronize access to the filter to prevent raw notifications from
// occurring at the same time as regular worker process notifications
//
pMainContext = pConnection->QueryMainContext();
if ( pMainContext != NULL )
{
pMainContext->QueryFilterContext()->FilterLock();
}
hr = pConnection->NotifyRawReadFilters( pRawStreamInfo,
pfReadMore,
pfComplete );
if ( pMainContext != NULL )
{
pMainContext->QueryFilterContext()->FilterUnlock();
}
*pcbNextReadSize = pConnection->QueryNextReadSize();
return hr;
}
HRESULT
RAW_CONNECTION::NotifyRawReadFilters(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfReadMore,
BOOL * pfComplete
)
/*++
Routine Description:
Notify raw read filters
Arguments:
pRawStreamInfo - Raw stream info
pfReadMore - Set to TRUE to we should read more data
pfComplete - Set to TRUE if we should disconnect
Return Value:
HRESULT
--*/
{
HTTP_FILTER_DLL * pFilterDll;
DWORD err;
SF_STATUS_TYPE sfStatus;
DWORD i;
PVOID pvtmp;
PVOID pvCurrentClientContext;
FILTER_LIST * pFilterList;
HTTP_FILTER_RAW_DATA hfrd;
if ( pRawStreamInfo == NULL ||
pfReadMore == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfComplete = FALSE;
*pfReadMore = FALSE;
//
// Setup filter raw object
//
hfrd.pvInData = pRawStreamInfo->pbBuffer;
hfrd.cbInData = pRawStreamInfo->cbData;
hfrd.cbInBuffer = pRawStreamInfo->cbBuffer;
//
// In certain cases, we can send a notification to a filter while we're still
// processing another filter's notification. In that case, we need to make sure
// we restore the current filter's context when we're done with the notifications
//
pvCurrentClientContext = _hfc.pFilterContext;
pFilterList = QueryFilterList();
DBG_ASSERT( pFilterList != NULL );
for ( i = 0; i < pFilterList->QueryFilterCount(); i++ )
{
pFilterDll = pFilterList->QueryDll( i );
//
// Notification flags are cached in the HTTP_FILTER object, but they're
// only copied from the actual HTTP_FILTER_DLL object if a filter dll
// disables a particular notification [sort of a copy-on-write scheme].
// If a filter dll disables/changes a notification, we need to check the flags
// in the HTTP_FILTER object, not those in the HTTP_FILTER_DLL object
//
if ( !QueryNotificationChanged() )
{
if ( !pFilterDll->IsNotificationNeeded( SF_NOTIFY_READ_RAW_DATA,
_hfc.fIsSecurePort ) )
{
continue;
}
}
else
{
if ( !IsDisableNotificationNeeded( i,
SF_NOTIFY_READ_RAW_DATA ) )
{
continue;
}
}
_hfc.pFilterContext = QueryClientContext( i );
pvtmp = _hfc.pFilterContext;
//
// Keep track of the current filter so that we know which filters
// to notify when a raw filter does a write client
//
_dwCurrentFilter = i;
sfStatus = (SF_STATUS_TYPE)
pFilterDll->QueryEntryPoint()( &_hfc,
SF_NOTIFY_READ_RAW_DATA,
&hfrd );
if ( pvtmp != _hfc.pFilterContext )
{
SetClientContext( i, _hfc.pFilterContext );
pFilterDll->SetHasSetContextBefore();
}
switch ( sfStatus )
{
default:
DBGPRINTF(( DBG_CONTEXT,
"Unknown status code from filter %d\n",
sfStatus ));
//
// Fall through
//
case SF_STATUS_REQ_NEXT_NOTIFICATION:
continue;
case SF_STATUS_REQ_ERROR:
_hfc.pFilterContext = pvCurrentClientContext;
return E_FAIL;
case SF_STATUS_REQ_FINISHED:
case SF_STATUS_REQ_FINISHED_KEEP_CONN: // Not supported at this point
*pfComplete = TRUE;
goto Exit;
case SF_STATUS_REQ_READ_NEXT:
*pfReadMore = TRUE;
goto Exit;
case SF_STATUS_REQ_HANDLED_NOTIFICATION:
//
// Don't notify any other filters
//
goto Exit;
}
}
Exit:
pRawStreamInfo->pbBuffer = (BYTE*) hfrd.pvInData;
pRawStreamInfo->cbData = hfrd.cbInData;
pRawStreamInfo->cbBuffer = hfrd.cbInBuffer;
//
// Reset the filter context we came in with
//
_hfc.pFilterContext = pvCurrentClientContext;
return NO_ERROR;
}
//static
HRESULT
RAW_CONNECTION::ProcessRawWrite(
RAW_STREAM_INFO * pRawStreamInfo,
PVOID pvContext,
BOOL * pfComplete
)
/*++
Routine Description:
Entry point called by stream filter to handle data coming from the
application. We will call SF_NOTIFY_SEND_RAW_DATA filter notifications
here
Arguments:
pRawStreamInfo - The stream to process, as well as an optional opaque
context set by the RAW_CONNECTION code
pvContext - Context pass back
pfComplete - Set to TRUE if we should disconnect
Return Value:
HRESULT
--*/
{
RAW_CONNECTION * pConnection = NULL;
W3_MAIN_CONTEXT * pMainContext;
HRESULT hr = NO_ERROR;
DWORD cbAppRead;
if ( pRawStreamInfo == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfComplete = FALSE;
return NO_ERROR;
}
HRESULT
RAW_CONNECTION::NotifyRawWriteFilters(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfComplete,
DWORD dwStartFilter
)
/*++
Routine Description:
Notify raw write filters
Arguments:
pRawStreamInfo - Raw stream to munge
pfComplete - Set to TRUE if we should disconnect now
dwStartFilter - Filter to start notifying. If this valid is INVALID_DLL,
then simply start with the lowest priority filter
Return Value:
HRESULT
--*/
{
HTTP_FILTER_DLL * pFilterDll;
DWORD err;
SF_STATUS_TYPE sfStatus;
DWORD i;
PVOID pvtmp;
PVOID pvCurrentClientContext;
FILTER_LIST * pFilterList;
HTTP_FILTER_RAW_DATA hfrd;
if ( pRawStreamInfo == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfComplete = FALSE;
hfrd.pvInData = pRawStreamInfo->pbBuffer;
hfrd.cbInData = pRawStreamInfo->cbData;
hfrd.cbInBuffer = pRawStreamInfo->cbBuffer;
//
// In certain cases, we can send a notification to a filter while we're still
// processing another filter's notification. In that case, we need to make sure
// we restore the current filter's context when we're done with the notifications
//
pvCurrentClientContext = _hfc.pFilterContext;
pFilterList = QueryFilterList();
DBG_ASSERT( pFilterList != NULL );
if ( dwStartFilter == INVALID_DLL )
{
dwStartFilter = pFilterList->QueryFilterCount() - 1;
}
i = dwStartFilter;
do
{
pFilterDll = pFilterList->QueryDll( i );
//
// Notification flags are cached in the HTTP_FILTER object, but they're
// only copied from the actual HTTP_FILTER_DLL object if a filter dll
// disables a particular notification [sort of a copy-on-write scheme].
// If a filter dll disables/changes a notification, we need to check the flags
// in the HTTP_FILTER object, not those in the HTTP_FILTER_DLL object
//
if ( !QueryNotificationChanged() )
{
if ( !pFilterDll->IsNotificationNeeded( SF_NOTIFY_SEND_RAW_DATA,
_hfc.fIsSecurePort ) )
{
continue;
}
}
else
{
if ( !IsDisableNotificationNeeded( i,
SF_NOTIFY_SEND_RAW_DATA ) )
{
continue;
}
}
//
// Another slimy optimization. If this filter has never associated
// context with connection, then we don't have to do the lookup
//
_hfc.pFilterContext = QueryClientContext( i );
pvtmp = _hfc.pFilterContext;
//
// Keep track of the current filter so that we know which filters
// to notify when a raw filter does a write client
//
_dwCurrentFilter = i;
sfStatus = (SF_STATUS_TYPE)
pFilterDll->QueryEntryPoint()( &_hfc,
SF_NOTIFY_SEND_RAW_DATA,
&hfrd );
if ( pvtmp != _hfc.pFilterContext )
{
SetClientContext( i, _hfc.pFilterContext );
pFilterDll->SetHasSetContextBefore();
}
switch ( sfStatus )
{
default:
DBGPRINTF(( DBG_CONTEXT,
"Unknown status code from filter %d\n",
sfStatus ));
//
// Fall through
//
case SF_STATUS_REQ_NEXT_NOTIFICATION:
continue;
case SF_STATUS_REQ_ERROR:
_hfc.pFilterContext = pvCurrentClientContext;
return E_FAIL;
case SF_STATUS_REQ_FINISHED:
case SF_STATUS_REQ_FINISHED_KEEP_CONN: // Not supported at this point
*pfComplete = TRUE;
goto Exit;
case SF_STATUS_REQ_HANDLED_NOTIFICATION:
//
// Don't notify any other filters
//
goto Exit;
}
}
while ( i-- > 0 );
Exit:
pRawStreamInfo->pbBuffer = (BYTE*) hfrd.pvInData;
pRawStreamInfo->cbData = hfrd.cbInData;
pRawStreamInfo->cbBuffer = hfrd.cbInBuffer;
//
// Reset the filter context we came in with
//
_hfc.pFilterContext = pvCurrentClientContext;
return NO_ERROR;
}
HRESULT
RAW_CONNECTION::NotifyEndOfNetSessionFilters(
VOID
)
/*++
Routine Description:
Notify END_OF_NET_SESSION filters
Arguments:
None
Return Value:
HRESULT
--*/
{
HTTP_FILTER_DLL * pFilterDll;
DWORD err;
SF_STATUS_TYPE sfStatus;
DWORD i;
PVOID pvtmp;
PVOID pvCurrentClientContext;
FILTER_LIST * pFilterList;
HTTP_FILTER_RAW_DATA hfrd;
//
// In certain cases, we can send a notification to a filter while we're still
// processing another filter's notification. In that case, we need to make sure
// we restore the current filter's context when we're done with the notifications
//
pvCurrentClientContext = _hfc.pFilterContext;
pFilterList = QueryFilterList();
DBG_ASSERT( pFilterList != NULL );
for ( i = 0; i < pFilterList->QueryFilterCount(); i++ )
{
pFilterDll = pFilterList->QueryDll( i );
if ( !QueryNotificationChanged() )
{
if ( !pFilterDll->IsNotificationNeeded( SF_NOTIFY_END_OF_NET_SESSION,
_hfc.fIsSecurePort ) )
{
continue;
}
}
else
{
if ( !IsDisableNotificationNeeded( i,
SF_NOTIFY_END_OF_NET_SESSION ) )
{
continue;
}
}
_hfc.pFilterContext = QueryClientContext( i );
pvtmp = _hfc.pFilterContext;
//
// Keep track of the current filter so that we know which filters
// to notify when a raw filter does a write client
//
_dwCurrentFilter = i;
sfStatus = (SF_STATUS_TYPE)
pFilterDll->QueryEntryPoint()( &_hfc,
SF_NOTIFY_END_OF_NET_SESSION,
&hfrd );
if ( pvtmp != _hfc.pFilterContext )
{
SetClientContext( i, _hfc.pFilterContext );
pFilterDll->SetHasSetContextBefore();
}
switch ( sfStatus )
{
default:
DBGPRINTF(( DBG_CONTEXT,
"Unknown status code from filter %d\n",
sfStatus ));
//
// Fall through
//
case SF_STATUS_REQ_NEXT_NOTIFICATION:
continue;
case SF_STATUS_REQ_ERROR:
_hfc.pFilterContext = pvCurrentClientContext;
return E_FAIL;
case SF_STATUS_REQ_FINISHED:
case SF_STATUS_REQ_FINISHED_KEEP_CONN: // Not supported at this point
goto Exit;
case SF_STATUS_REQ_HANDLED_NOTIFICATION:
//
// Don't notify any other filters
//
goto Exit;
}
}
Exit:
//
// Reset the filter context we came in with
//
_hfc.pFilterContext = pvCurrentClientContext;
return NO_ERROR;
}
//static
VOID
RAW_CONNECTION::ProcessConnectionClose(
PVOID pvContext
)
/*++
Routine Description:
Entry point called by stream filter when a connection has closed
Arguments:
pvContext - Opaque context associated with the connection
Return Value:
None
--*/
{
RAW_CONNECTION * pRawConnection;
pRawConnection = (RAW_CONNECTION*) pvContext;
if ( pRawConnection != NULL )
{
DBG_ASSERT( pRawConnection->CheckSignature() );
//
// We're done with the raw connection. Delete it from hash table
// In the process, this will dereference the connection
//
DBG_ASSERT( sm_pRawConnectionHash != NULL );
sm_pRawConnectionHash->DeleteRecord( pRawConnection );
}
}
VOID
RAW_CONNECTION::CopyAllocatedFilterMemory(
W3_FILTER_CONTEXT * pFilterContext
)
/*++
Routine Description:
Copy over any allocated filter memory items
Arguments:
pFilterContext - Destination of filter memory item references
Return Value:
None
--*/
{
FILTER_POOL_ITEM * pfpi;
//
// We need to grab the raw connection lock since we don't want a
// read-raw data notification to muck with the pool list while we
// are copying it over to the W3_CONNECTION
//
pFilterContext->FilterLock();
while ( !IsListEmpty( &_PoolHead ) )
{
pfpi = CONTAINING_RECORD( _PoolHead.Flink,
FILTER_POOL_ITEM,
_ListEntry );
RemoveEntryList( &pfpi->_ListEntry );
InitializeListHead( &pfpi->_ListEntry );
//
// Copy the pool item to the other list
//
pFilterContext->AddFilterPoolItem( pfpi );
}
pFilterContext->FilterUnlock();
}
VOID
RAW_CONNECTION::CopyContextPointers(
W3_FILTER_CONTEXT * pFilterContext
)
/*++
Routine Description:
The global filter list is constant, in addition, when an instance filter
list is built, the global filters are always built into the list. After
the instance filter list has been identified, we need to copy any non-null
client filter context values from the global filter list to the new
positions in the instance filter list. For example:
Global List & | Instance List &
context values | new context value positions
|
G1 0 | I1 0
G2 555 | G1 0
G3 123 | G2 555
| I2 0
| G3 123
Note: This scheme precludes having the same .dll be used for both a
global and per-instance dll. Since global filters are automatically
per-instance this shouldn't be an interesting case.
--*/
{
DWORD i, j;
DWORD cGlobal;
DWORD cInstance;
HTTP_FILTER_DLL * pFilterDll;
FILTER_LIST * pGlobalFilterList;
FILTER_LIST * pInstanceFilterList;
pFilterContext->FilterLock();
DBG_ASSERT( pFilterContext != NULL );
pGlobalFilterList = FILTER_LIST::QueryGlobalList();
DBG_ASSERT( pGlobalFilterList != NULL );
cGlobal = pGlobalFilterList->QueryFilterCount();
pInstanceFilterList = pFilterContext->QueryFilterList();
DBG_ASSERT( pInstanceFilterList != NULL );
cInstance = pInstanceFilterList->QueryFilterCount();
//
// If no global filters or no instance filters, then there won't be
// any filter context pointers that need adjusting
//
if ( !cGlobal || !cInstance )
{
goto Finished;
}
//
// For each global list context pointer, find the filter in the instance
// list and adjust
//
for ( i = 0; i < cGlobal; i++ )
{
if ( _rgContexts[ i ] != NULL )
{
pFilterDll = pGlobalFilterList->QueryDll( i );
//
// We found one. Find the filter in instance list and set
//
for ( j = 0; j < cInstance; j++ )
{
if ( pInstanceFilterList->QueryDll( j ) == pFilterDll )
{
pFilterContext->SetClientContext( j, _rgContexts[ i ] );
}
}
}
}
Finished:
pFilterContext->FilterUnlock();
}
HRESULT
RAW_CONNECTION::CopyHeaders(
W3_FILTER_CONTEXT * pFilterContext
)
/*++
Routine Description:
Copy denied/response headers from read raw
Arguments:
pFilterContext - Filter context to copy to
Return Value:
HRESULT
--*/
{
HRESULT hr;
if ( pFilterContext == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
hr = pFilterContext->AddDenialHeaders( _strAddDenialHeaders.QueryStr() );
if ( FAILED( hr ) )
{
return hr;
}
hr = pFilterContext->AddResponseHeaders( _strAddResponseHeaders.QueryStr() );
if ( FAILED( hr ) )
{
return hr;
}
_strAddDenialHeaders.Reset();
_strAddResponseHeaders.Reset();
return NO_ERROR;
}
HRESULT
RAW_CONNECTION::SendResponseHeader(
CHAR * pszStatus,
CHAR * pszAdditionalHeaders,
HTTP_FILTER_CONTEXT * pfc
)
/*++
Routine Description:
Called when raw filters want to send a response header. Depending
on whether a W3_CONNECTION is associated or not, we will either
send the stream ourselves here, or call in the main context's
response facilities
Arguments:
pszStatus - ANSI status line
pszAdditionalHeaders - Any additional headers to send
pfc - Filter context (to be passed to FilterWriteClient())
Return Value:
HRESULT
--*/
{
W3_MAIN_CONTEXT * pMainContext = NULL;
STACK_STRA( strResponse, 256 );
HRESULT hr = NO_ERROR;
DWORD cbBytes = 0;
BOOL fRet = FALSE;
W3_RESPONSE * pResponse = NULL;
if ( pszStatus == NULL &&
pszAdditionalHeaders == NULL )
{
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
//
// Which response are we touching?
//
pMainContext = QueryMainContext();
if ( pMainContext != NULL )
{
pResponse = pMainContext->QueryResponse();
}
else
{
pResponse = &_response;
}
//
// Build up a response from what ISAPI gave us
//
hr = pResponse->BuildResponseFromIsapi( pMainContext,
pszStatus,
pszAdditionalHeaders,
pszAdditionalHeaders ?
strlen( pszAdditionalHeaders ) : 0 );
if ( FAILED( hr ) )
{
return hr;
}
//
// Now if we have a w3 context then we can send the response normally.
// Otherwise we must use the UL filter API
//
if ( pMainContext != NULL )
{
hr = pMainContext->SendResponse( W3_FLAG_SYNC | W3_FLAG_NO_ERROR_BODY );
}
else
{
//
// Add denial/response headers
//
if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
{
hr = pResponse->AppendResponseHeaders( _strAddDenialHeaders );
if ( FAILED( hr ) )
{
return hr;
}
}
hr = pResponse->AppendResponseHeaders( _strAddResponseHeaders );
if ( FAILED( hr ) )
{
return hr;
}
hr = pResponse->GetRawResponseStream( &strResponse );
if ( FAILED( hr ) )
{
return hr;
}
//
// Go thru WriteClient() so the right filtering happens on the
// response
//
cbBytes = strResponse.QueryCB();
fRet = RAW_CONNECTION::RawFilterWriteClient( pfc,
strResponse.QueryStr(),
&cbBytes,
0 );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
return hr;
}
//static
HRESULT
RAW_CONNECTION::FindConnection(
HTTP_RAW_CONNECTION_ID rawConnectionId,
RAW_CONNECTION ** ppRawConnection
)
/*++
Routine Description:
Find and return raw connection if found
Arguments:
rawConnectionId - Raw connection ID from UL_HTTP_REQUEST
ppRawConnection - Set to raw connection if found
Return Value:
HRESULT
--*/
{
LK_RETCODE lkrc;
if ( ppRawConnection == NULL ||
rawConnectionId == HTTP_NULL_ID )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
DBG_ASSERT( sm_pRawConnectionHash != NULL );
lkrc = sm_pRawConnectionHash->FindKey( rawConnectionId,
ppRawConnection );
if ( lkrc != LK_SUCCESS )
{
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
}
else
{
return NO_ERROR;
}
}