/*++ 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; } }