/*++ Copyright (c) 1996 Microsoft Corporation Module Name: filtinit.cxx Abstract: This module contains the Microsoft HTTP server filter module for stuff that has to do with filter initialization, loading and unloading. Author: John Ludeman (johnl) 06-Aug-1996 Revision History: --*/ #include "w3p.hxx" // // If the request doesn't specify an entry point, default to using // this // #define SF_DEFAULT_ENTRY "HttpFilterProc" #define SF_VERSION_ENTRY "GetFilterVersion" #define SF_TERM_ENTRY "TerminateFilter" // // Name of the value under the parameters key containing the list of // filter dlls. This was the value for IIS 1.0 and 2.0. Global filters // can still be specified under this key. // #define HTTP_FILTER_DLLS "Filter DLLs" // // Globals // static BOOL g_fInitialized = FALSE; LIST_ENTRY g_FilterHead; // List of filter DLLs CRITICAL_SECTION g_csFilterDlls; // // Prototypes // BOOL UpdateInstanceFilters( IN const CHAR * pszNewDll, IN const CHAR * pszOldDll, IN W3_SERVER_INSTANCE * pInst ); BOOL AdjustInstanceFilterListFlags( IN PVOID pvContext1, IN PVOID pvContext2, IN IIS_SERVER_INSTANCE *pInstance ); FILTER_LIST * InitializeFilters( BOOL * pfAnySecureFilters, W3_IIS_SERVICE * pSvc ) /*++ Routine Description: Loads the global filter DLLs and their corresponding entry point. Note the global filter list does not allow unloads. g_fInitialized should only be set after all of the global filters are loaded. Arguments: pfAnySecureFilters - Set to TRUE if there are any secure filters pSvc - Pointer to service global. The global service pointer isn't initialized yet so we pass it in for global filter initialization. Only used for logging events. Return Value: Global filter list or NULL if an error occurred --*/ { CHAR szFilterKey[MAX_PATH+1]; FILTER_LIST * pfl; DWORD cb; CHAR szLoadOrder[1024]; CHAR szDllName[MAX_PATH+1]; CHAR * pchFilter; CHAR * pchComma; MB mb( (IMDCOM*) pSvc->QueryMDObject() ); DWORD fEnabled; DWORD cFilters; HKEY hkeyParam = NULL; DWORD err = NO_ERROR; BOOL fOpened; INITIALIZE_CRITICAL_SECTION( &g_csFilterDlls ); InitializeListHead( &g_FilterHead ); strcpy( szFilterKey, IIS_MD_LOCAL_MACHINE_PATH "/" W3_SERVICE_NAME_A ); strcat( szFilterKey, IIS_MD_ISAPI_FILTERS ); DBG_ASSERT( strlen( szFilterKey ) + 1 < sizeof( szFilterKey )); pfl = new FILTER_LIST(); if ( !pfl ) { return NULL; } // // Loop through filter keys, if we can't access the metabase, we assume // success and continue // if ( !mb.Open( szFilterKey, METADATA_PERMISSION_READ )) { DBGPRINTF(( DBG_CONTEXT, "[InitializeFilterList] Cannot open path %s, error %lu\n", szFilterKey, GetLastError() )); return pfl; } fOpened = TRUE; // // Get the filter load order // cb = sizeof( szLoadOrder ); *szLoadOrder = '\0'; if ( !mb.GetString( "", MD_FILTER_LOAD_ORDER, IIS_MD_UT_SERVER, szLoadOrder, &cb, 0 )) { DBGPRINTF(( DBG_CONTEXT, "[InitializeFilterList] Cannot load filter load order, error %lu\n", szFilterKey, GetLastError() )); delete pfl; return NULL; } pchFilter = szLoadOrder; while ( *pchFilter ) { if ( !fOpened && !mb.Open( szFilterKey, METADATA_PERMISSION_READ )) { DBGPRINTF(( DBG_CONTEXT, "[InitializeFilterList] Cannot open path %s, error %lu\n", szFilterKey, GetLastError() )); break; } fOpened = TRUE; pchComma = strchr( pchFilter, ',' ); if ( pchComma ) { *pchComma = '\0'; } while ( ISWHITEA( *pchFilter )) { pchFilter++; } fEnabled = TRUE; mb.GetDword( pchFilter, MD_FILTER_ENABLED, IIS_MD_UT_SERVER, &fEnabled ); if ( fEnabled ) { cb = sizeof(szDllName); if ( mb.GetString( pchFilter, MD_FILTER_IMAGE_PATH, IIS_MD_UT_SERVER, szDllName, &cb, 0 )) { mb.Close(); fOpened = FALSE; if ( pfl->LoadFilter( &mb, szFilterKey, &fOpened, pchFilter, szDllName, TRUE, pSvc )) { DBGPRINTF(( DBG_CONTEXT, "[InitializeFilterList] Loaded %s\n", szDllName )); } } } if ( pchComma ) { pchFilter = pchComma + 1; } else { break; } } // // Now load any filters that have been added in the registry for // downlevel compatibility // err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, W3_PARAMETERS_KEY, 0, KEY_READ, &hkeyParam ); if( err == NO_ERROR ) { TCHAR * psz; TCHAR * pszFilterList = NULL; if ( ReadRegString( hkeyParam, &pszFilterList, HTTP_FILTER_DLLS, "" )) { RegCloseKey( hkeyParam ); psz = pszFilterList; // // Parse the comma separated list of dlls // INET_PARSER Parser( pszFilterList ); Parser.SetListMode( TRUE ); while ( *(psz = Parser.QueryToken()) ) { if ( pfl->LoadFilter( NULL, NULL, &fOpened, psz, psz, TRUE, pSvc )) { const CHAR * apszSubString[1]; DBGPRINTF(( DBG_CONTEXT, "[InitializeFilterList] Loaded %s\n", psz )); apszSubString[0] = psz; // // Log a warning here if we picked up a filter from the // registry // pSvc->LogEvent( W3_MSG_LOADED_FILTER_FROM_REG, 1, apszSubString, NO_ERROR ); } Parser.NextItem(); } Parser.RestoreBuffer(); TCP_FREE( pszFilterList ); } else { RegCloseKey( hkeyParam ); } } *pfAnySecureFilters = CheckForSecurityFilter( pfl ); g_fInitialized = TRUE; return pfl; } VOID TerminateFilters( VOID ) /*++ Routine Description: Unloads any filter DLLs and their corresponding entry point --*/ { LIST_ENTRY * pEntry; HTTP_FILTER_DLL * pFilterDll; if ( g_fInitialized ) { EnterCriticalSection( &g_csFilterDlls ); for ( pEntry = g_FilterHead.Flink; pEntry != &g_FilterHead; pEntry = g_FilterHead.Flink) { pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry ); RemoveEntryList( pEntry ); // // This should delete the filter dll // DBGPRINTF(( DBG_CONTEXT, "[TerminateFilters] Filter %s, ref count %d\n", pFilterDll->QueryName(), pFilterDll->QueryRef() )); // DBG_ASSERT( pFilterDll->QueryRef() == 1 ); HTTP_FILTER_DLL::Dereference( pFilterDll ); } LeaveCriticalSection( &g_csFilterDlls ); DeleteCriticalSection( &g_csFilterDlls ); } } HTTP_FILTER_DLL * CheckoutFilterDll( IN const CHAR * pszFilterDll ) /*++ Routine Description: Checks to see if an existing filter dll is already loaded and ups the ref count if it is NOTE: THE FILTER LIST LOCK MUST BE TAKEN PRIOR TO CALLING THIS FUNCTION Arguments: pszFilterDLL - Fully qualified path to filter dll to load Return Value: Pointer to the filter if it's already loaded, NULL if the filter isn't loaded --*/ { LIST_ENTRY * pEntry; HTTP_FILTER_DLL * pFilterDll; for ( pEntry = g_FilterHead.Flink; pEntry != &g_FilterHead; pEntry = pEntry->Flink ) { pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry ); DBG_ASSERT( pFilterDll->CheckSignature() ); if ( !lstrcmpi( pFilterDll->QueryName(), pszFilterDll )) { pFilterDll->Reference(); return pFilterDll; } } return NULL; } BOOL HTTP_FILTER_DLL::LoadDll( MB * pmb OPTIONAL, const CHAR * pszKeyName, LPBOOL pfOpened, const CHAR * pszRelFilterPath, const CHAR * pszFilterDll ) /*++ Routine Description: Loads the specified dll Arguments: pmb - Open metabase to /filters key pszKeyName - metabase filters key name pfOpened - updated with TRUE if pmb opened on return pszFilterDll - Fully qualified name of filter to load THE GLOBAL FILTER LIST LOCK MUST BE TAKEN PRIOR TO CALLING THIS METHOD Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { HTTP_FILTER_VERSION ver; if ( !m_strName.Copy( pszFilterDll )) { return FALSE; } // // Load it and put it in the list, note the filter list lock // is still taken // m_hmod = LoadLibraryEx( pszFilterDll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); if ( g_fIsWindows95 && (m_hmod == NULL) && (GetLastError() == ERROR_FILE_NOT_FOUND) ) { // // According to vlads: the behaviour of flags used in loadlibraryex // is different between chicago and NT. This caused a problem // loading frontpage filters. // m_hmod = LoadLibrary( pszFilterDll); } if ( !m_hmod ) { DBGPRINTF(( DBG_CONTEXT, "[LoadDll] LoadLibrary failed with error %d\n", GetLastError())); goto ErrorExit; } // // Retrieve the entry point // m_pfnSFVer = (PFN_SF_VER_PROC) GetProcAddress( m_hmod, SF_VERSION_ENTRY ); m_pfnSFProc = (PFN_SF_DLL_PROC) GetProcAddress( m_hmod, SF_DEFAULT_ENTRY ); m_pfnSFTerm = (PFN_SF_TERM_PROC) GetProcAddress( m_hmod, SF_TERM_ENTRY ); if ( !m_pfnSFProc || !m_pfnSFVer ) { // // Don't call the terminator if we didn't call the initializer // m_pfnSFTerm = NULL; goto ErrorExit; } ver.dwServerFilterVersion = HTTP_FILTER_REVISION; // // Call the version entry point and get the filter capabilities // if ( !m_pfnSFVer( &ver ) ) { goto ErrorExit; } if ( pmb && (*pfOpened || pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) ) { *pfOpened = TRUE; if ( !pmb->SetString( pszRelFilterPath, MD_FILTER_DESCRIPTION, IIS_MD_UT_SERVER, ver.lpszFilterDesc, 0 ) || !pmb->SetDword( pszRelFilterPath, MD_FILTER_FLAGS, IIS_MD_UT_SERVER, ver.dwFlags, 0 )) { goto ErrorExit; } } // // If the client didn't specify any of the secure port notifications, // supply them with the default of both // if ( !(ver.dwFlags & (SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT))) { ver.dwFlags |= (SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT); } m_dwVersion = ver.dwFilterVersion; m_dwFlags = (ver.dwFlags & SF_NOTIFY_NONSECURE_PORT) ? ver.dwFlags : 0; m_dwSecureFlags = (ver.dwFlags & SF_NOTIFY_SECURE_PORT) ? ver.dwFlags : 0; // // Put the new dll on the filter dll list // InsertHeadList( &g_FilterHead, &ListEntry ); return TRUE; ErrorExit: return FALSE; } BOOL HTTP_FILTER_DLL::Unload( const CHAR * pszDll ) /*++ Routine Description: Finds the existing DLL and sets it up to be unloaded Arguments: pszDLL - Fully qualified path to filter dll to load Return Value: TRUE if the filter DLL was found, FALSE if it wasn't found or it's already marked for deletion --*/ { LIST_ENTRY * pEntry; HTTP_FILTER_DLL * pFilterDll; EnterCriticalSection( &g_csFilterDlls ); for ( pEntry = g_FilterHead.Flink; pEntry != &g_FilterHead; pEntry = pEntry->Flink ) { pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry ); DBG_ASSERT( pFilterDll->CheckSignature() ); if ( !lstrcmpi( pFilterDll->QueryName(), pszDll )) { RemoveEntryList( pEntry ); LeaveCriticalSection( &g_csFilterDlls ); return TRUE; } } LeaveCriticalSection( &g_csFilterDlls ); return FALSE; } BOOL FILTER_LIST::LoadFilter( MB * pmb, LPSTR pszKeyName, LPBOOL pfOpened, const CHAR * pszRelativeMBPath, const CHAR * pszFilterDll, BOOL fAllowRawRead, W3_IIS_SERVICE * pSvc ) /*++ Routine Description: Loads the specified filter into this filter list. Note the filter dll's ref count starts at one for being on the g_FilterHead list. Thus if the refcount drops to one, no filter list is using the filter dll and it can be deleted. Arguments: pmb - metabase open at the filter root, opened for Read/Write pszKeyName - Metabase open path for filters key pfOpened - TRUE if pmb currently opened, otherwise FALSE must be set before calling this function, which will update it. pszRelativeMBPath - Metabase path to this filter dll pszFilterDll - Fully qualified name of filter to load fAllowRawRead - Set to TRUE if raw read filters are allowed pSvc - Optional service pointer for logging Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { HTTP_FILTER_VERSION ver; HTTP_FILTER_DLL * pFilterDll; DWORD i; // // pSvc will only be passed during InitializeFilters // if ( !pSvc ) { pSvc = (W3_IIS_SERVICE *) g_pInetSvc; } // // Make sure there's a free entry in the filter list array, and // the secure/non-secure notification arrays (the latter two are used // in conjunction with filters disabling themselves per request // if ( (m_cFilters+1) > (m_buffFilterArray.QuerySize() / sizeof(PVOID))) { if ( !m_buffFilterArray.Resize( (m_cFilters + 5) * sizeof(PVOID)) ) { return FALSE; } if ( !m_buffSecureArray.Resize( (m_cFilters + 5) * sizeof(DWORD)) ) { return FALSE; } if ( !m_buffNonSecureArray.Resize( (m_cFilters + 5) * sizeof(DWORD)) ) { return FALSE; } } // // Load the filter DLL // EnterCriticalSection( &g_csFilterDlls ); if ( !(pFilterDll = CheckoutFilterDll( pszFilterDll ))) { pFilterDll = new HTTP_FILTER_DLL; if ( !pFilterDll || !pFilterDll->LoadDll( pmb, pszKeyName, pfOpened, pszRelativeMBPath, pszFilterDll ) ) { const CHAR * apszSubString[1]; DWORD err = GetLastError(); LeaveCriticalSection( &g_csFilterDlls ); delete pFilterDll; apszSubString[0] = pszFilterDll; if ( err ) { // // Log a warning here if the filter supplied an error code // pSvc->LogEvent( W3_EVENT_FILTER_DLL_LOAD_FAILED, 1, apszSubString, err ); } DBGPRINTF(( DBG_CONTEXT, "Cannot load filter dll (err = %d)\n", err)); if ( pmb && (*pfOpened || pmb->Open( pszKeyName, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) ) { *pfOpened = TRUE; pmb->SetDword( pszRelativeMBPath, MD_WIN32_ERROR, IIS_MD_UT_SERVER, err, 0 ); pmb->SetDword( pszRelativeMBPath, MD_FILTER_STATE, IIS_MD_UT_SERVER, MD_FILTER_STATE_UNLOADED, 0 ); } return FALSE; } // // This filter list now officially owns a reference to this filter dll // pFilterDll->Reference(); } // // Not a fatal error if the status doesn't get updated // DBG_ASSERT( pFilterDll != NULL ); if ( pmb && (*pfOpened || pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) ) { *pfOpened = TRUE; if ( !pmb->SetDword( pszRelativeMBPath, MD_WIN32_ERROR, IIS_MD_UT_SERVER, NO_ERROR, 0 ) || !pmb->SetDword( pszRelativeMBPath, MD_FILTER_STATE, IIS_MD_UT_SERVER, MD_FILTER_STATE_LOADED, 0 )) { DBGPRINTF(( DBG_CONTEXT, "Error %d setting filter status\n", GetLastError() )); } } // // Disallow any per-instance read raw filters // if ( !fAllowRawRead && (pFilterDll->QueryNotificationFlags() & SF_NOTIFY_READ_RAW_DATA) ) { const CHAR * apszSubString[1]; apszSubString[0] = pszFilterDll; pSvc->LogEvent( W3_MSG_READ_RAW_MUST_BE_GLOBAL, 1, apszSubString, 0 ); DBGPRINTF(( DBG_CONTEXT, "Refusing READ_RAW filter on server instance (%s)\n", pszFilterDll)); if ( pmb && (*pfOpened || pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) ) { *pfOpened = TRUE; if ( !pmb->SetDword( pszRelativeMBPath, MD_WIN32_ERROR, IIS_MD_UT_SERVER, ERROR_INVALID_PARAMETER, 0 ) || !pmb->SetDword( pszRelativeMBPath, MD_FILTER_STATE, IIS_MD_UT_SERVER, MD_FILTER_STATE_UNLOADED, 0 )) { DBGPRINTF(( DBG_CONTEXT, "Error %d setting filter status\n", GetLastError() )); } } // // Undo the ref for being on this filter list // HTTP_FILTER_DLL::Dereference( pFilterDll ); // // If nobody else is using this filter dll, then go ahead and force // an unload. The filter dll can't be checked out with the filter // critical section held so this is a safe operation. // if ( pFilterDll->QueryRef() == 1 ) { RemoveEntryList( &pFilterDll->ListEntry ); HTTP_FILTER_DLL::Dereference( pFilterDll ); } LeaveCriticalSection( &g_csFilterDlls ); return FALSE; } // // Find where pFilterDll goes in the list // for ( i = 0; i < m_cFilters; i++ ) { if ( (QueryDll(i)->QueryNotificationFlags() & SF_NOTIFY_ORDER_MASK) < (pFilterDll->QueryNotificationFlags() & SF_NOTIFY_ORDER_MASK) ) { break; } } // // And insert it into the array // memmove( (PVOID *) m_buffFilterArray.QueryPtr() + i + 1, (PVOID *) m_buffFilterArray.QueryPtr() + i, (m_cFilters - i) * sizeof(PVOID) ); (((HTTP_FILTER_DLL * *) (m_buffFilterArray.QueryPtr())))[i] = pFilterDll; // // Add notification DWORDS to secure/non-secure arrays // memmove( (DWORD *) m_buffSecureArray.QueryPtr() + i + 1, (DWORD *) m_buffSecureArray.QueryPtr() + i, (m_cFilters - i) * sizeof(DWORD) ); ((DWORD*) m_buffSecureArray.QueryPtr())[i] = pFilterDll->QuerySecureFlags(); memmove( (DWORD *) m_buffNonSecureArray.QueryPtr() + i + 1, (DWORD *) m_buffNonSecureArray.QueryPtr() + i, (m_cFilters - i) * sizeof(DWORD) ); ((DWORD*) m_buffNonSecureArray.QueryPtr())[i] = pFilterDll->QueryNonsecureFlags(); m_cFilters++; // // Segregate the secure and non-secure port notifications // m_SecureNotifications |= pFilterDll->QuerySecureFlags(); m_NonSecureNotifications |= pFilterDll->QueryNonsecureFlags(); LeaveCriticalSection( &g_csFilterDlls ); return TRUE; } #if 0 BOOL FILTER_LIST::Copy( IN FILTER_LIST * pClone ) /*++ Routine Description: Clones the passed filter list Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { DWORD i; DBG_ASSERT( pClone->CheckSignature() ); DBG_ASSERT( CheckSignature() ); // // Walk the list to be cloned and load all of the dlls // EnterCriticalSection( &g_csFilterDlls ); for ( i = 0; i < pClone->QueryFilterCount(); i++ ) { if ( !LoadFilter( pClone->QueryDll( i )->QueryName(), FALSE )) { DBGPRINTF(( DBG_CONTEXT, "[FILTER_LIST::Copy] Failed loading %s, error %d\n", pClone->QueryDll( i )->QueryName(), GetLastError() )); } } LeaveCriticalSection( &g_csFilterDlls ); return TRUE; } BOOL ReplaceFilterDll( IN const CHAR * pszNewDll, IN const CHAR * pszOldDll ) /*++ Routine Description: Given a new filter and an old filter, this function renames the old filter dll to a temporary name, copies the new .dll into place then walks the instance list and replaces all occurrences of the old .dll with the new .dll. Arguments: pszNewDll - New filter dll pszOldDll - Existing filter dll that should be replaced Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { CHAR achTmp[MAX_PATH + 1]; CHAR * pszTmp; DWORD cVer = 1; CHAR achVer[32]; const CHAR * apsz[3]; apsz[0] = pszOldDll; apsz[1] = pszNewDll; g_pInetSvc->LogEvent( W3_MSG_REP_FILTER, 2, apsz, 0 ); // // Find a back up name - this just appends a number onto the dll // strcpy( achTmp, pszOldDll ); pszTmp = achTmp + strlen( pszOldDll ); while ( TRUE ) { _itoa( cVer, achVer, 10 ); strcpy( pszTmp, achVer ); if ( GetFileAttributes( achTmp ) != 0xffffffff || GetLastError() != ERROR_FILE_NOT_FOUND ) { cVer++; // // If we've tried a reasonable number of backups and we couldn't // find a candidate then get out // if ( cVer > 8192 ) { SetLastError( ERROR_FILE_NOT_FOUND ); return FALSE; } continue; } // // Rename the old DLL to the new dll name // if ( !MoveFileEx( pszOldDll, achTmp, MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED )) { DBGPRINTF(( DBG_CONTEXT, "[ReplaceFilterDll] Move old file failed, %s -> %s, error %d\n", pszOldDll, achTmp, GetLastError() )); if ( GetLastError() == ERROR_FILE_EXISTS ) { cVer++; continue; } apsz[0] = pszOldDll; apsz[1] = pszNewDll; g_pInetSvc->LogEvent( W3_MSG_REP_FILTER, 2, apsz, GetLastError() ); return FALSE; } break; } // // Rename the new DLL name to the old dll // if ( !MoveFileEx( pszNewDll, pszOldDll, MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED )) { DBGPRINTF(( DBG_CONTEXT, "[ReplaceFilterDll] Move new file failed, %s -> %s, error %d\n", pszNewDll, pszOldDll, GetLastError() )); apsz[0] = pszNewDll; apsz[1] = pszOldDll; g_pInetSvc->LogEvent( W3_MSG_REP_FILTER_FAILED, 2, apsz, 0 ); if ( !MoveFileEx( achTmp, pszOldDll, MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED )) { DBGPRINTF(( DBG_CONTEXT, "[ReplaceFilterDll] Move tmp to old file failed, %s -> %s, error %d\n", achTmp, pszOldDll, GetLastError() )); apsz[0] = achTmp; apsz[1] = pszOldDll; g_pInetSvc->LogEvent( W3_MSG_REP_FILTER_FAILED, 2, apsz, 0 ); } return FALSE; } // // Update all of the instance filters // if ( !g_pInetSvc->EnumServiceInstances( (PVOID) pszNewDll, (PVOID) pszOldDll, (PFN_INSTANCE_ENUM) UpdateInstanceFilters )) { return FALSE; } return TRUE; } BOOL UpdateInstanceFilters( IN const CHAR * pszNewDll, IN const CHAR * pszOldDll, IN W3_SERVER_INSTANCE * pInst ) /*++ Routine Description: This is a simple utility function that handles the instance callbacks Arguments: pszNewDll - New filter dll pszOldDll - Existing filter dll that should be replaced pInst - The instance the change is being applied to Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { #if 0 if ( !pInst->UpdateFilterList( pszNewDll, pszOldDll )) { DBGPRINTF(( DBG_CONTEXT, "[UpdateInstanceFilters] Failed with error %d\n", GetLastError() )); return FALSE; } #endif return TRUE; } #endif BOOL FILTER_LIST::InsertGlobalFilters( VOID ) /*++ Routine Description: Transfers all of the global filters to the per-instance filter list Note: This method assumes the server filter list is not dynamic Return Value: TRUE on success, FALSE on failure (call GetLastError) --*/ { DWORD i; HTTP_FILTER_DLL * pFilterDll; BOOL fOpened; for ( i = 0; i < GLOBAL_FILTER_LIST()->QueryFilterCount(); i++ ) { // // Ignore the return code, an event gets logged in LoadFilter() // We allow raw read filters here as we're just duplicating the // global filter list // LoadFilter( NULL, NULL, &fOpened, "", GLOBAL_FILTER_LIST()->QueryDll( i )->QueryName(), TRUE ); } return TRUE; } BOOL CheckForSecurityFilter( FILTER_LIST * pFilterList ) /*++ Routine Description: Checks to see if there are any global filters that are encryption filters Return Value: TRUE if an encryption filter was found, FALSE otherwise --*/ { DWORD i; HTTP_FILTER_DLL * pFilterDll; BOOL fRet = FALSE; for ( i = 0; i < pFilterList->QueryFilterCount(); i++ ) { pFilterDll = pFilterList->QueryDll( i ); // // port notification, assume he's an encryption filter // #define SECURE_FILTER (SF_NOTIFY_SECURE_PORT | \ SF_NOTIFY_READ_RAW_DATA | \ SF_NOTIFY_SEND_RAW_DATA | \ SF_NOTIFY_ORDER_HIGH) if ( (pFilterDll->QueryNotificationFlags() & SECURE_FILTER) == SECURE_FILTER) { fRet = TRUE; break; } } return fRet; } BOOL AdjustFilterFlags( PVOID pfnSFProc, DWORD dwNewFlags ) /*++ Routine Description: Private exported routine that allows a filter to dynamically adjust its notification flags. This can only be done for global filters. Return Value: TRUE on success, FALSE on failure --*/ { W3_IIS_SERVICE * pSvc = (W3_IIS_SERVICE *) g_pInetSvc; FILTER_LIST * pFilterList; HTTP_FILTER_DLL * pFilterDll; DWORD i; BOOL fFoundSvcDLL = FALSE; BOOL fFoundInstanceDLL = FALSE; // // Can't adjust priority, just notification flags // if ( dwNewFlags & SF_NOTIFY_ORDER_MASK ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } pFilterList = pSvc->QueryGlobalFilterList(); if ( !pFilterList ) { // // Shouldn't happen but you never know // return TRUE; } // // Try to find the appropriate DLL in the single global per-service filter list // for ( i = 0; i < pFilterList->QueryFilterCount(); i++ ) { DBG_ASSERT( pFilterList->QueryDll(i)->CheckSignature() ); if ( pFilterList->QueryDll(i)->QueryEntryPoint() == pfnSFProc ) { pFilterDll = pFilterList->QueryDll(i); // // We've found the matching filter dll, adjust the flags in the // filter dll object then adjust the flags in the filter list // pFilterDll->SetNotificationFlags( dwNewFlags ); pFilterList->SetNotificationFlags( i, pFilterDll ); fFoundSvcDLL = TRUE; } } if ( fFoundSvcDLL ) { // // Try to update the flags in each of the per-instance filter lists // if ( pSvc ) { if ( pSvc->EnumServiceInstances( (PVOID) &dwNewFlags, (PVOID) pfnSFProc, AdjustInstanceFilterListFlags ) ) { fFoundInstanceDLL = TRUE; } } } if ( !( fFoundInstanceDLL && fFoundSvcDLL ) ) { SetLastError( ERROR_FILE_NOT_FOUND ); return FALSE; } return TRUE; } BOOL AdjustInstanceFilterListFlags( IN PVOID pdwNewFlags, IN PVOID pfnSFProc, IN IIS_SERVER_INSTANCE *pInstance ) /*++ Routine Description: This is the callback function called when we need to adjust the notification flags of a particular filter dll for the filter list associated with a server instance. Note that this filter list consists of the -global- [ie service-wide] filter dlls. Arguments : pdwNewFlags - pointer to DWORD containing new notification flags pfnSFProc - pointer to filter entry point function pInstance - pointer to W3_SERVER_INSTANCE whose filter list is to be updated Return Value: TRUE if the DLL was found and the flags adjusted, FALSE otherwise --*/ { HTTP_FILTER_DLL *pFilterDll = NULL; W3_SERVER_INSTANCE *pW3Instance = (W3_SERVER_INSTANCE *) pInstance; BOOL fFound = FALSE; if ( !pInstance ) { DBGPRINTF((DBG_CONTEXT, "NULL instance passed to AdjustInstanceFilterFlags !\n")); return FALSE; } pInstance->LockThisForRead(); FILTER_LIST *pFilterList = ((W3_SERVER_INSTANCE *) pInstance)->QueryFilterList(); pInstance->UnlockThis(); if ( !pFilterList ) { DBGPRINTF((DBG_CONTEXT, "Very odd - instance 0x%p has no filter list associated with it !\n", pInstance)); return TRUE; } pInstance->LockThisForWrite(); // // Try to find the appropriate DLL in the filter list for this instance // for ( DWORD i = 0; i < pFilterList->QueryFilterCount(); i++ ) { DBG_ASSERT( pFilterList->QueryDll(i)->CheckSignature() ); if ( pFilterList->QueryDll(i)->QueryEntryPoint() == pfnSFProc ) { pFilterDll = pFilterList->QueryDll(i); // // We've found the matching filter dll, adjust the flags in the // filter dll object then adjust the flags in the filter list // pFilterDll->SetNotificationFlags( *((DWORD *) pdwNewFlags) ); pFilterList->SetNotificationFlags( i, pFilterDll ); fFound = TRUE; break; } } pInstance->UnlockThis(); return fFound; }