/*++ Copyright (c) 1994 Microsoft Corporation Module Name : tsvccfg.cxx Abstract: Defines the functions for TCP services Info class. This module is intended to capture the common scheduler code for the tcp services ( especially internet services) which involves the Service Controller dispatch functions. Also this class provides an interface for common dll of servers. Author: Murali R. Krishnan ( MuraliK ) 15-Nov-1994 Project: Internet Servers Common DLL --*/ #include "tcpdllp.hxx" #include #include #include #include #include #include #include "inetreg.h" #include "tcpcons.h" #include "apiutil.h" #include #include #include #include #include // // Used to configure // typedef struct _IIS_SOCKET_CONFIG { DWORD nAcceptExOutstanding; } IIS_SOCKET_CONFIG; IIS_SOCKET_CONFIG TsSocketConfig[3] = {{5}, {40}, {100}}; // // from security.cxx // BOOL BuildAnonymousAcctDesc( IN OUT PCHAR pszAcctDesc, IN const CHAR * pszDomainAndUser, IN const CHAR * pszPwd, OUT LPDWORD pdwAcctDescLen ); BOOL AppendDottedDecimal( STR * pstr, DWORD dwAddress ); // // private functions // extern VOID CopyUnicodeStringToBuffer( OUT WCHAR * pwchBuffer, IN DWORD cchMaxSize, IN LPCWSTR pwszSource ); DWORD SetInetLogConfiguration( IN LOGGING *pLogging, IN EVENT_LOG * pEventLog, IN const INET_LOG_CONFIGURATION * pRpcLogConfig ); DWORD GetRPCLogConfiguration( LOGGING *pLogging, OUT LPINET_LOG_CONFIGURATION * ppLogConfig ); BOOL GenerateIpList( BOOL fIsGrant, ADDRESS_CHECK *pCheck, LPINET_INFO_IP_SEC_LIST *ppInfo ); BOOL FillAddrCheckFromIpList( BOOL fIsGrant, LPINET_INFO_IP_SEC_LIST pInfo, ADDRESS_CHECK *pCheck ); BOOL GetVrootCount( PVOID pvContext, MB * pmb, VIRTUAL_ROOT * pvr ); BOOL GetVroots( PVOID pvContext, MB * pmb, VIRTUAL_ROOT * pvr ); VOID CopyUnicodeStringToBuffer( OUT WCHAR * pwchBuffer, IN DWORD cchMaxSize, IN LPCWSTR pwszSource) /* copies at most cbMaxSize-1 characters from pwszSource to pwchBuffer */ { DBG_ASSERT( pwszSource != NULL); DWORD cchLen = lstrlenW( pwszSource); if ( cchLen >= cchMaxSize) { DBGPRINTF( ( DBG_CONTEXT, "Long String ( %d chars) %ws given." " Truncating to %d chars\n", cchLen, pwszSource, cchMaxSize - 1)); // There is a bug in the lstrcpyn. hence need to work around it. #ifndef LSTRCPYN_DEBUGGED cchLen = cchMaxSize - 2; # else cchLen = cchMaxSize -1; # endif } #ifndef LSTRCPYN_DEBUGGED lstrcpynW( pwchBuffer, pwszSource, cchLen + 1); # else lstrcpynW( pwchBuffer, pwszSource, cchLen ); # endif return; } // CopyUnicodeStringToBuffer() BOOL IIS_SERVER_INSTANCE::GetCommonConfig( IN OUT PCHAR pConfig, IN DWORD dwLevel ) /*++ This function copies the current configuration for a service (IIS_SERVER_INSTANCE) into the given RPC object pConfig. In case of any failures, it deallocates any memory block that was allocated during the process of copy by this function alone. Arguments: pConfig - pointer to RPC configuration object for a service. dwLevel - level of our configuration. Returns: TRUE for success and FALSE for any errors. --*/ { BOOL fReturn = TRUE; LPINETA_CONFIG_INFO pInfoConfig = (LPINETA_CONFIG_INFO)pConfig; ADDRESS_CHECK acCheck; BOOL fMustRel; MB mb( (IMDCOM*) m_Service->QueryMDObject() ); DWORD cRoots = 0; STR strAnon; STR strAnonPwd; STR strServerComment; DWORD err = NO_ERROR; IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT,"GetCommonConfig called with L%d for instance %d\n", dwLevel, QueryInstanceId() )); } LockThisForRead(); // // Get always retrieves all of the parameters except for the anonymous // password, which is retrieved as a secret // pInfoConfig->FieldControl = (FC_INET_INFO_ALL & ~FC_INET_INFO_ANON_PASSWORD); pInfoConfig->dwConnectionTimeout = QueryConnectionTimeout(); pInfoConfig->dwMaxConnections = QueryMaxConnections(); pInfoConfig->LangId = GetSystemDefaultLangID(); pInfoConfig->LocalId = GetSystemDefaultLCID(); // // This is the PSS product ID // ZeroMemory( pInfoConfig->ProductId,sizeof( pInfoConfig->ProductId )); // // Copy the strings // fReturn = (ConvertStringToRpc(&pInfoConfig->lpszAdminName, ""/*QueryAdminName()*/ ) && ConvertStringToRpc( &pInfoConfig->lpszAdminEmail, "" /*QueryAdminEmail()*/ ) ); if ( !fReturn ) { IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT,"ConvertStringToRpc failed with %d\n", GetLastError() )); } goto Exit; } else { DWORD dwError; dwError = GetRPCLogConfiguration(&m_Logging, &pInfoConfig->lpLogConfig); if ( dwError != NO_ERROR) { IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT,"GetRPCLogConfiguration failed with %d\n", dwError)); } SetLastError( dwError); fReturn = FALSE; goto Exit; } } pInfoConfig->fLogAnonymous = QueryLogAnonymous(); pInfoConfig->fLogNonAnonymous = QueryLogNonAnonymous(); ZeroMemory( pInfoConfig->szAnonPassword, sizeof( pInfoConfig->szAnonPassword ) ); // // Copy the IP security info from metabase // if ( mb.Open( QueryMDVRPath() ) ) { VOID * pvData; DWORD cbData; DWORD dwTag; if ( mb.ReferenceData( "", MD_IP_SEC, IIS_MD_UT_FILE, BINARY_METADATA, &pvData, &cbData, &dwTag ) && dwTag ) { acCheck.BindCheckList( (BYTE *) pvData, cbData ); fMustRel = TRUE; } else { fMustRel = FALSE; } fReturn = GenerateIpList( TRUE, &acCheck, &pInfoConfig->GrantIPList ) && GenerateIpList( FALSE, &acCheck, &pInfoConfig->DenyIPList ); if ( fMustRel ) { DBG_REQUIRE( mb.ReleaseReferenceData( dwTag )); } DBG_REQUIRE( mb.Close() ); } else { fReturn = FALSE; } if ( !fReturn ) { goto Exit; } // // Copy the virtual root info, note a NULL VirtualRoots is not // valid as it is for IP security. This should be the last // allocated item for the pConfig structure // if ( !mb.Open( QueryMDPath(), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) { fReturn = FALSE; goto Exit; } if ( TsEnumVirtualRoots( GetVrootCount, &cRoots, &mb ) ) { DWORD cbSize = sizeof(INET_INFO_VIRTUAL_ROOT_LIST) + cRoots * sizeof(INET_INFO_VIRTUAL_ROOT_ENTRY) ; pInfoConfig->VirtualRoots = (LPINET_INFO_VIRTUAL_ROOT_LIST) MIDL_user_allocate( cbSize ); memset( pInfoConfig->VirtualRoots, 0, cbSize ); if ( pInfoConfig->VirtualRoots ) { fReturn = TsEnumVirtualRoots( GetVroots, pInfoConfig->VirtualRoots, &mb ); } // only used for UNC virtual directories (to store the passwords) err = TsSetSecretW( m_lpwszRootPasswordSecretName, L"", sizeof(WCHAR) ); if ( err == ERROR_ACCESS_DENIED && g_fW3OnlyNoAuth ) { err = 0; } } mb.Close(); if ( !fReturn ) { goto Exit; } if ( !mb.Open( QueryMDPath() )) { fReturn = FALSE; goto Exit; } mb.GetDword( "", MD_AUTHORIZATION, IIS_MD_UT_FILE, MD_AUTH_ANONYMOUS, &pInfoConfig->dwAuthentication ); if ( !mb.GetStr( "", MD_ANONYMOUS_USER_NAME, IIS_MD_UT_FILE, &strAnon, METADATA_INHERIT, "<>" )) { fReturn = FALSE; goto Exit; } if ( !mb.GetStr( "", MD_SERVER_COMMENT, IIS_MD_UT_SERVER, &strServerComment, METADATA_INHERIT, INETA_DEF_SERVER_COMMENT )) { // // If this is a single instance service, this is also the // service comment // if ( !m_Service->IsMultiInstance() ) { m_Service->SetServiceComment( strServerComment.QueryStr() ); } } fReturn = ConvertStringToRpc( &pInfoConfig->lpszServerComment, strServerComment.QueryStr() ) && ConvertStringToRpc( &pInfoConfig->lpszAnonUserName, strAnon.QueryStr() ); // // Get the anonymous user password but store it as an LSA secret // if ( mb.GetStr( "", MD_ANONYMOUS_PWD, IIS_MD_UT_FILE, &strAnonPwd, METADATA_INHERIT | METADATA_SECURE )) { BUFFER buff; if ( buff.Resize( (strAnonPwd.QueryCCH() + 1) * sizeof(WCHAR ))) { if ( MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, strAnonPwd.QueryStr(), strAnonPwd.QueryCCH() + 1, (LPWSTR) buff.QueryPtr(), strAnonPwd.QueryCCH() + 1 )) { err = TsSetSecretW( m_lpwszAnonPasswordSecretName, (LPWSTR) buff.QueryPtr(), wcslen( (LPWSTR) buff.QueryPtr()) * sizeof(WCHAR) ); if ( err == ERROR_ACCESS_DENIED && g_fW3OnlyNoAuth ) { err = 0; } } } } else { // // store an empty password if there's no anonymous user at this level // err = TsSetSecretW( m_lpwszAnonPasswordSecretName, L"", sizeof(WCHAR) ); if ( err == ERROR_ACCESS_DENIED && g_fW3OnlyNoAuth ) { err = 0; } } if ( err ) { SetLastError( err ); fReturn = FALSE; } if ( !fReturn ) { IF_DEBUG(INSTANCE) { DBGPRINTF((DBG_CONTEXT,"Cannot get anonymous user name")); } } Exit: if ( !fReturn ) { if ( pInfoConfig->lpLogConfig != NULL) { MIDL_user_free( pInfoConfig->lpLogConfig); pInfoConfig->lpLogConfig = NULL; } // // FreeRpcString checks for NULL pointer // FreeRpcString( pInfoConfig->lpszAdminName ); FreeRpcString( pInfoConfig->lpszAdminEmail ); FreeRpcString( pInfoConfig->lpszServerComment ); FreeRpcString( pInfoConfig->lpszAnonUserName ); pInfoConfig->lpszAdminName = NULL; pInfoConfig->lpszAdminEmail = NULL; pInfoConfig->lpszServerComment = NULL; pInfoConfig->lpszAnonUserName = NULL; if ( pInfoConfig->DenyIPList ) { MIDL_user_free( pInfoConfig->DenyIPList ); pInfoConfig->DenyIPList = NULL; } if ( pInfoConfig->GrantIPList ) { MIDL_user_free( pInfoConfig->GrantIPList ); pInfoConfig->GrantIPList = NULL; } } UnlockThis(); return (fReturn); } // IIS_SERVER_INSTANCE::GetConfiguration() BOOL IIS_SERVER_INSTANCE::RegReadCommonParams( BOOL fReadAll, BOOL fReadVirtualDirs ) /*++ Description Reads the service common items from the registry Arguments: fReadAll - If TRUE read all parameters. If FALSE read only those parameters that are necessary for initialization. fReadVirtualDirs - Initalize Virtual DIrectories. Note: --*/ { MB mb( (IMDCOM*) m_Service->QueryMDObject() ); DBG_ASSERT( QueryInstanceId() != INET_INSTANCE_ROOT ); IF_DEBUG( DLL_RPC) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::ReadParamsFromRegistry() Entered. fReadAll = %d\n", fReadAll)); } // // Open the metabase and read parameters for IIS_SERVER_INSTANCE object // itself. // if ( !mb.Open( QueryMDPath(), TsIsNtServer() ? METADATA_PERMISSION_READ : METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) { DBGPRINTF(( DBG_CONTEXT, "[ReadParamsFromRegistry] mb.Open returned error %d for path %s\n", GetLastError(), QueryMDPath() )); } LockThisForWrite(); // // Values needed for initialization // mb.GetDword( "", MD_SERVER_AUTOSTART, IIS_MD_UT_SERVER, TRUE, (DWORD *) &m_fAutoStart ); mb.GetDword( "", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, FALSE, (DWORD *) &m_fClusterEnabled ); /* That's a fix for a bug 367791 when restarting IIS with vhost sites marked for auto restart isn't bringing them online becuase of current disgn limitation how cluster service is checking for helth of vhost site it is not able to distinguish that only one of few vhost sites are running and is not starting the rest. The fix is to allow to admin to set autorestart on site and then during startup of IIS to start that site not with cluster command but automaticcally Because of that the following lines are removed. if ( m_fClusterEnabled ) { m_fAutoStart = FALSE; } */ if ( !mb.GetStr( "", MD_SERVER_COMMENT, IIS_MD_UT_SERVER, &m_strSiteName ) || m_strSiteName.IsEmpty()) { m_strSiteName.Copy(QueryMDPath()); } // // Other values needed to run the instance // if ( fReadAll) { mb.GetDword( "", MD_CONNECTION_TIMEOUT, IIS_MD_UT_SERVER, INETA_DEF_CONNECTION_TIMEOUT, &m_dwConnectionTimeout ); mb.GetDword( "", MD_MAX_CONNECTIONS, IIS_MD_UT_SERVER, INETA_DEF_MAX_CONNECTIONS, &m_dwMaxConnections ); mb.GetDword( "", MD_MAX_ENDPOINT_CONNECTIONS, IIS_MD_UT_SERVER, (TsIsNtServer() ? TsSocketConfig[MD_SERVER_SIZE_LARGE].nAcceptExOutstanding : INETA_DEF_MAX_ENDPOINT_CONNECTIONS_PWS ), &m_dwMaxEndpointConnections ); mb.GetDword( "", MD_LEVELS_TO_SCAN, IIS_MD_UT_SERVER, INETA_DEF_LEVELS_TO_SCAN, &m_dwLevelsToScan ); // // if not NTS, limit the connections. If reg value exceeds 40, // set it to 10. // if ( !TsIsNtServer() ) { if ( m_dwMaxConnections > INETA_MAX_MAX_CONNECTIONS_PWS ) { m_dwMaxConnections = INETA_DEF_MAX_CONNECTIONS_PWS; mb.SetDword( "", MD_MAX_CONNECTIONS, IIS_MD_UT_SERVER, m_dwMaxConnections ); } if ( m_dwMaxEndpointConnections > INETA_MAX_MAX_ENDPOINT_CONNECTIONS_PWS ) { m_dwMaxEndpointConnections = INETA_DEF_MAX_ENDPOINT_CONNECTIONS_PWS; mb.SetDword( "", MD_MAX_ENDPOINT_CONNECTIONS, IIS_MD_UT_SERVER, m_dwMaxEndpointConnections ); } } // // Log anonymous and Log non-anonymous or for FTP only // mb.GetDword( "", MD_LOG_TYPE, IIS_MD_UT_SERVER, TRUE, (DWORD *) &m_fLoggingEnabled ); mb.GetDword( "", MD_LOG_ANONYMOUS, IIS_MD_UT_SERVER, INETA_DEF_LOG_ANONYMOUS, (DWORD *) &m_fLogAnonymous ); mb.GetDword( "", MD_LOG_NONANONYMOUS, IIS_MD_UT_SERVER, INETA_DEF_LOG_NONANONYMOUS, (DWORD *) &m_fLogNonAnonymous ); #if 0 // // I don't believe that ServerCommand can be set to // started without our noticing, so I'm removing this // code. // if (!m_fAutoStart) { // // Server Command to start this instance may // have been written while service was stopped. // Need to pick it up // DWORD dwServerCommand; mb.GetDword( "", MD_SERVER_COMMAND, IIS_MD_UT_SERVER, TRUE, (DWORD *) &dwServerCommand ); if (dwServerCommand == MD_SERVER_COMMAND_START) { m_fAutoStart = TRUE; } } #endif // // Other fields // // // socket values // mb.GetDword( "", MD_SERVER_SIZE, IIS_MD_UT_SERVER, INETA_DEF_SERVER_SIZE, &m_dwServerSize ); if ( m_dwServerSize > MD_SERVER_SIZE_LARGE ) { m_dwServerSize = INETA_DEF_SERVER_SIZE; } mb.GetDword( "", MD_SERVER_LISTEN_BACKLOG, IIS_MD_UT_SERVER, TsSocketConfig[m_dwServerSize].nAcceptExOutstanding, &m_nAcceptExOutstanding ); mb.GetDword( "", MD_SERVER_LISTEN_TIMEOUT, IIS_MD_UT_SERVER, INETA_DEF_ACCEPTEX_TIMEOUT, &m_AcceptExTimeout ); // // Setup a bandwidth throttle descriptor if necessary (for NT server) // SetBandwidthThrottle( &mb ); // // Set the maximum number of blocked requests for throttler // SetBandwidthThrottleMaxBlocked( &mb ); // Root instance does not have VRs. Close the metabase because the // virtual directories are going to be re-enumerated. // } mb.Close(); if ( fReadVirtualDirs ) { TsReadVirtualRoots( ); } UnlockThis(); return TRUE; } // IIS_SERVER_INSTANCE::ReadParamsFromRegistry() BOOL IIS_SERVER_INSTANCE::SetCommonConfig( IN LPINETA_CONFIG_INFO pInfoConfig, IN BOOL fRefresh ) /*++ Description Writes the service common items to the registry Arguments: pInfoConfig - Admin items to write to the registry fRefresh - Indicates whether we need to read back the data Note: We don't need to lock "this" object because we only write to the registry The anonymous password is set as a secret from the client side --*/ { DWORD err = NO_ERROR; FIELD_CONTROL fcConfig; ADDRESS_CHECK acCheck; BUFFER buff; MB mb( (IMDCOM*) m_Service->QueryMDObject() ); // // Open the metabase and read parameters for IIS_SERVER_INSTANCE object // itself. // if ( !mb.Open( QueryMDPath(), METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE )) { DBGPRINTF(( DBG_CONTEXT, "[SetCommonConfig] mb.Open returned error %d for path %s\n", GetLastError(), QueryMDPath() )); return FALSE; } fcConfig = pInfoConfig->FieldControl; if ( IsFieldSet( fcConfig, FC_INET_INFO_CONNECTION_TIMEOUT )) { mb.SetDword( "", MD_CONNECTION_TIMEOUT, IIS_MD_UT_SERVER, pInfoConfig->dwConnectionTimeout ); } if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_MAX_CONNECTIONS )) { mb.SetDword( "", MD_MAX_CONNECTIONS, IIS_MD_UT_SERVER, pInfoConfig->dwMaxConnections ); } if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_SERVER_COMMENT ) && (pInfoConfig->lpszServerComment != NULL) ) { if ( buff.Resize( 2 * (wcslen(pInfoConfig->lpszServerComment) + 1) * sizeof(CHAR) ) ) { (VOID) ConvertUnicodeToAnsi( pInfoConfig->lpszServerComment, (CHAR *) buff.QueryPtr(), buff.QuerySize() ); mb.SetString( "", MD_SERVER_COMMENT, IIS_MD_UT_SERVER, (CHAR *) buff.QueryPtr() ); } } if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_ANON_USER_NAME ) && (pInfoConfig->lpszAnonUserName != NULL) ) { STR strAnonPwd; if ( buff.Resize( 2 * (wcslen(pInfoConfig->lpszAnonUserName) + 1) * sizeof(CHAR) ) ) { (VOID) ConvertUnicodeToAnsi( pInfoConfig->lpszAnonUserName, (CHAR *) buff.QueryPtr(), buff.QuerySize() ); mb.SetString( "", MD_ANONYMOUS_USER_NAME, IIS_MD_UT_FILE, (CHAR *) buff.QueryPtr() ); } // // Set the anonymous password also. The client sets it as an LSA // secret // if ( TsGetSecretW( m_lpwszAnonPasswordSecretName, &strAnonPwd ) && mb.SetString( "", MD_ANONYMOUS_PWD, IIS_MD_UT_FILE, strAnonPwd.QueryStr() )) { DBGPRINTF(( DBG_CONTEXT, "Failed to get/set anonymous secret, err %d\n", GetLastError() )); } } if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_AUTHENTICATION )) { mb.SetDword( "", MD_AUTHORIZATION, IIS_MD_UT_FILE, pInfoConfig->dwAuthentication ); } // // Write other fields // if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_SITE_SECURITY )) { if ( (pInfoConfig->GrantIPList && pInfoConfig->GrantIPList->cEntries) || (pInfoConfig->DenyIPList && pInfoConfig->DenyIPList->cEntries) ) { acCheck.BindCheckList( NULL, 0 ); if ( FillAddrCheckFromIpList( TRUE, pInfoConfig->GrantIPList, &acCheck ) && FillAddrCheckFromIpList( FALSE, pInfoConfig->DenyIPList, &acCheck ) ) { if ( !mb.SetData( IIS_MD_INSTANCE_ROOT, MD_IP_SEC, IIS_MD_UT_FILE, BINARY_METADATA, (acCheck.GetStorage()->GetAlloc() ? acCheck.GetStorage()->GetAlloc() : (LPBYTE)""), acCheck.GetStorage()->GetUsed(), METADATA_INHERIT | METADATA_REFERENCE )) { err = GetLastError(); } } acCheck.UnbindCheckList(); } else { if ( !mb.DeleteData( IIS_MD_INSTANCE_ROOT, MD_IP_SEC, IIS_MD_UT_FILE, BINARY_METADATA ) ) { // not an error : property may not exists //err = GetLastError(); } } } DBG_REQUIRE( mb.Close() ); if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_LOG_CONFIG) && (pInfoConfig->lpLogConfig != NULL) ) { err = SetInetLogConfiguration(&m_Logging, m_Service->QueryEventLog(), pInfoConfig->lpLogConfig); if ( err != NO_ERROR) { DBGPRINTF(( DBG_CONTEXT, "SetConfiguration() SetInetLogConfig() failed. " " Err=%u\n", err)); } } if ( (err == NO_ERROR) && IsFieldSet( fcConfig, FC_INET_INFO_VIRTUAL_ROOTS )) { if ( QueryInstanceId() != INET_INSTANCE_ROOT ) { if ( !TsSetVirtualRoots( pInfoConfig )) { err = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "[SetConfiguration()]SetVirtualRoots " " returns error %d\n", err)); } } } if ( err != NO_ERROR ) { IF_DEBUG( ERROR) { DBGPRINTF(( DBG_CONTEXT, "IIS_SERVER_INSTANCE::SetCommonConfig ==> Error = %u\n", err)); } SetLastError( err ); return(FALSE); } return TRUE; } // IIS_SERVER_INSTANCE::SetCommonConfig VOID IIS_SERVER_INSTANCE::MDChangeNotify( MD_CHANGE_OBJECT * pcoChangeList ) /*++ This method handles the metabase change notification for this service. Arguments: pcoChangeList - path and id that has changed --*/ { DWORD i; DWORD status = NO_ERROR; BOOL fVRUpdated = FALSE; BOOL fReadCommon = FALSE; BOOL fShouldMirror = FALSE; HRESULT hr; BOOL fShouldCoUninitialize = FALSE; hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if ( SUCCEEDED(hr) ) { fShouldCoUninitialize = TRUE; } else if (hr != E_INVALIDARG && hr != RPC_E_CHANGED_MODE) { // // E_INVALIDARG and RPC_E_CHANGED_MODE could mean com was already // initialized with different parameters, so ignore it but don't // Uninit. Assert on other errors. // DBGPRINTF((DBG_CONTEXT,"CoInitializeEx failed with %x\n",hr)); DBG_ASSERT(FALSE); } if ( (pcoChangeList->dwMDChangeType & (MD_CHANGE_TYPE_DELETE_OBJECT | MD_CHANGE_TYPE_RENAME_OBJECT | MD_CHANGE_TYPE_ADD_OBJECT) ) != 0 ) { // // Something got added/deleted/renamed // fShouldMirror = TRUE; } LockThisForWrite(); for ( i = 0; i < pcoChangeList->dwMDNumDataIDs; i++ ) { m_Logging.NotifyChange( pcoChangeList->pdwMDDataIDs[i] ); switch ( pcoChangeList->pdwMDDataIDs[i] ) { case MD_SERVER_BINDINGS: if( QueryServerState() != MD_SERVER_STATE_STOPPED ) { status = UpdateNormalBindings(); if( status != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: UpdateNormalBindings() failed,error %lu\n", status )); } SetWin32Error( status ); } break; case MD_SECURE_BINDINGS: if( QueryServerState() != MD_SERVER_STATE_STOPPED ) { status = UpdateSecureBindings(); if( status != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: UpdateSecureBindings() failed,error %lu\n", status )); } SetWin32Error( status ); } break; case MD_DISABLE_SOCKET_POOLING: if( QueryServerState() != MD_SERVER_STATE_STOPPED ) { if (HasNormalBindings()) { status = RemoveNormalBindings(); if (NO_ERROR == status) { status = UpdateNormalBindings(); if( status != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: UpdateNormalBindings() failed,error %lu\n", status )); } } else { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: RemoveNormalBindings() failed,error %lu\n", status )); } } if ( (status == NO_ERROR) && HasSecureBindings()) { status = RemoveSecureBindings(); if (NO_ERROR == status) { status = UpdateSecureBindings(); if( status != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: UpdateSecureBindings() failed,error %lu\n", status )); } } else { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: RemoveSecureBindings() failed,error %lu\n", status )); } } SetWin32Error( status ); } break; case MD_CLUSTER_ENABLED: status = PerformClusterModeChange(); if( status != NO_ERROR ) { IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: PerformClusterModeChange() failed, error %lu\n", status )); } } break; case MD_SERVER_COMMAND: // // If cluster mode is enabled command must be specified // using MD_CLUSTER_SERVER_COMMAND, so that ISM cannot set the server state : // State management is to be done by cluster code exclusively. // if ( IsClusterEnabled() ) { break; } case MD_CLUSTER_SERVER_COMMAND: status = PerformStateChange(); if( status != NO_ERROR ) { IF_DEBUG( INSTANCE ) { DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: ProcessStateChange() failed, error %lu\n", status )); } } // // if command started server then need to reload virtual roots // as failing-over may have enabled new file system resources // if ( QueryServerState() != MD_SERVER_STATE_STARTED ) { break; } // fall-through case MD_VR_PATH: case MD_VR_USERNAME: case MD_VR_PASSWORD: fShouldMirror = TRUE; if ( !fVRUpdated ) { // // Note individual root errors log an event // if ( !TsReadVirtualRoots(pcoChangeList) ) { DBGPRINTF(( DBG_CONTEXT, "Error %d (0x%lx) reading virtual root info for %s\n", GetLastError(), GetLastError(), pcoChangeList->pszMDPath )); } fVRUpdated = TRUE; } break; case MD_MAX_BANDWIDTH: { MB mb( (IMDCOM*) m_Service->QueryMDObject() ); if ( mb.Open( QueryMDPath() ) ) { if ( !SetBandwidthThrottle( &mb ) ) { DWORD dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: SetBandwidthThrottle failed, error %lu\n", dwError )); SetWin32Error( dwError ); } DBG_REQUIRE( mb.Close() ); } break; } case MD_MAX_BANDWIDTH_BLOCKED: { MB mb( (IMDCOM*) m_Service->QueryMDObject() ); if ( mb.Open( QueryMDPath() ) ) { if ( !SetBandwidthThrottleMaxBlocked( &mb ) ) { DWORD dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "MDChangeNotify: SetBandwidthThrottle failed, error %lu\n", dwError )); SetWin32Error( dwError ); } DBG_REQUIRE( mb.Close() ); } break; } // // Ignore state & status updates // case MD_SERVER_STATE: case MD_WIN32_ERROR: break; case MD_ACCESS_PERM: fShouldMirror = TRUE; break; case MD_LOG_TYPE: { DWORD dwLogType; MB mb( (IMDCOM*) m_Service->QueryMDObject() ); if ( mb.Open( QueryMDPath() ) && mb.GetDword("", MD_LOG_TYPE, IIS_MD_UT_SERVER, &dwLogType) ) { m_fLoggingEnabled = (1 == dwLogType); } fReadCommon = TRUE; break; } default: fReadCommon = TRUE; break; } } if ( fReadCommon ) { m_Logging.NotifyChange( 0 ); RegReadCommonParams( TRUE, FALSE ); } if ((MD_CHANGE_TYPE_DELETE_OBJECT == pcoChangeList->dwMDChangeType) && (! _strnicmp( (LPCSTR) pcoChangeList->pszMDPath+QueryMDPathLen()+1, IIS_MD_INSTANCE_ROOT, sizeof(IIS_MD_INSTANCE_ROOT)-1)) ) { if ( !TsReadVirtualRoots(pcoChangeList) ) { DBGPRINTF(( DBG_CONTEXT, "Error %d (0x%lx) removing virtual root %s\n", GetLastError(), GetLastError(), pcoChangeList->pszMDPath )); } } // // reflect the changes to the registry // if ( fShouldMirror && IsDownLevelInstance() ) { MDMirrorVirtualRoots( ); } UnlockThis(); if ( fShouldCoUninitialize ) { CoUninitialize( ); } return; } // IIS_SERVER_INSTANCE::MDChangeNotify VOID IIS_SERVER_INSTANCE::MDMirrorVirtualRoots( VOID ) { DWORD err; HKEY hkey = NULL; // // Delete VR key // err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_Service->QueryRegParamKey(), 0, KEY_ALL_ACCESS, &hkey ); if ( err != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "RegOpenKeyEx for returned error %d\n",err )); return; } // // First delete the key to remove any old values // err = RegDeleteKey( hkey, VIRTUAL_ROOTS_KEY_A ); RegCloseKey(hkey); if ( err != NO_ERROR ) { DBGPRINTF(( DBG_CONTEXT, "[MDMirrorVRoots] Unable to remove old values\n")); return; } // // Now recreate the keys // MoveMDVroots2Registry( ); return; } // IIS_SERVER_INSTANCE::MDMirrorVirtualRoots DWORD GetRPCLogConfiguration(IN LOGGING *pLogging, OUT LPINET_LOG_CONFIGURATION * ppLogConfig) /*++ This function allocates space (using MIDL_ functions) and stores log configuration for the given log handle in it. Arguments: hInetLog handle for InetLog object. ppLogConfig pointer to INET_LOG_CONFIGURATION object which on return contains valid log config informtion, on success. Returns: Win32 error code. --*/ { DWORD dwError = NO_ERROR; LPINET_LOG_CONFIGURATION pRpcConfig; WCHAR cBuffer[MAX_PATH]; DBG_ASSERT( ppLogConfig != NULL); pRpcConfig = ((LPINET_LOG_CONFIGURATION ) MIDL_user_allocate( sizeof(INET_LOG_CONFIGURATION))); if ( pRpcConfig != NULL) { INETLOG_CONFIGURATIONA ilogConfig; DWORD cbConfig = sizeof(ilogConfig); BOOL fReturn=TRUE; ZeroMemory( &ilogConfig, sizeof(ilogConfig )); pLogging->GetConfig( &ilogConfig ); // // we got valid config. copy it into pRpcConfig. // since the enumerated values in inetlog.w are same in inetasrv.h // we do no mapping, we directly copy values. ZeroMemory( pRpcConfig, sizeof( INET_LOG_CONFIGURATION)); pRpcConfig->inetLogType = ilogConfig.inetLogType; switch ( ilogConfig.inetLogType) { case INET_LOG_TO_FILE: pRpcConfig->ilPeriod = ilogConfig.u.logFile.ilPeriod; pRpcConfig->cbSizeForTruncation = ilogConfig.u.logFile.cbSizeForTruncation; ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ilogConfig.u.logFile.rgchLogFileDirectory, -1, (WCHAR *)cBuffer, MAX_PATH ); CopyUnicodeStringToBuffer( pRpcConfig->rgchLogFileDirectory, MAX_PATH, cBuffer); *((DWORD *)&(pRpcConfig->rgchDataSource[MAX_PATH-sizeof(DWORD)]))=ilogConfig.u.logFile.ilFormat; *((DWORD *)&(pRpcConfig->rgchDataSource[MAX_PATH-2*sizeof(DWORD)]))=ilogConfig.u.logFile.dwFieldMask; break; case INET_LOG_TO_SQL: ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ilogConfig.u.logSql.rgchDataSource, -1, (WCHAR *)cBuffer, MAX_PATH ); CopyUnicodeStringToBuffer( pRpcConfig->rgchDataSource, MAX_PATH, cBuffer); ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ilogConfig.u.logSql.rgchTableName, -1, (WCHAR *)cBuffer, MAX_PATH ); CopyUnicodeStringToBuffer( pRpcConfig->rgchTableName, MAX_TABLE_NAME_LEN, cBuffer); ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ilogConfig.u.logSql.rgchUserName, -1, (WCHAR *)cBuffer, MAX_PATH ); CopyUnicodeStringToBuffer( pRpcConfig->rgchUserName, UNLEN, cBuffer); ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ilogConfig.u.logSql.rgchPassword, -1, (WCHAR *)cBuffer, MAX_PATH ); CopyUnicodeStringToBuffer( pRpcConfig->rgchPassword, PWLEN, cBuffer); break; case INET_LOG_DISABLED: default: // do nothing break; } // switch() } else { dwError = ERROR_NOT_ENOUGH_MEMORY; } *ppLogConfig = pRpcConfig; return (dwError); } // GetRPCLogConfiguration() DWORD SetInetLogConfiguration(IN LOGGING *pLogging, IN EVENT_LOG * pEventLog, IN const INET_LOG_CONFIGURATION * pRpcLogConfig) /*++ This function modifies the logconfiguration associated with a given InetLog handle. It also updates the registry containing log configuration for service with which the inetlog handle is associated. Arguments: hInetLog Handle to INETLOG object whose configuration needs to be changed. pRpcLogConfig new RPC log configuration Returns: Win32 Error code. NO_ERROR returned on success. --*/ { DWORD dwError = NO_ERROR; INETLOG_CONFIGURATIONA ilConfig; WCHAR cBuffer[MAX_PATH]; // // initialize // ZeroMemory( &ilConfig, sizeof(INETLOG_CONFIGURATIONA)); // Copy the RPC inet log configuration into local INETLOG_CONFIGURATIONW ilConfig.inetLogType = pRpcLogConfig->inetLogType; switch (ilConfig.inetLogType) { case INET_LOG_DISABLED: break; // do nothing case INET_LOG_TO_FILE: CopyUnicodeStringToBuffer(cBuffer, MAX_PATH, pRpcLogConfig->rgchLogFileDirectory); (VOID) ConvertUnicodeToAnsi( cBuffer, ilConfig.u.logFile.rgchLogFileDirectory, MAX_PATH ); ilConfig.u.logFile.ilPeriod = pRpcLogConfig->ilPeriod; if ( ilConfig.u.logFile.ilPeriod > INET_LOG_PERIOD_MONTHLY ) { return (ERROR_INVALID_PARAMETER); } ilConfig.u.logFile.cbSizeForTruncation = pRpcLogConfig->cbSizeForTruncation; ilConfig.u.logFile.ilFormat = *((DWORD *)&(pRpcLogConfig->rgchDataSource[MAX_PATH-sizeof(DWORD)])); ilConfig.u.logFile.dwFieldMask = *((DWORD *)&(pRpcLogConfig->rgchDataSource[MAX_PATH-2*sizeof(DWORD)])); break; case INET_LOG_TO_SQL: CopyUnicodeStringToBuffer(cBuffer, MAX_PATH, pRpcLogConfig->rgchDataSource); (VOID) ConvertUnicodeToAnsi( cBuffer, ilConfig.u.logSql.rgchDataSource, MAX_PATH); CopyUnicodeStringToBuffer(cBuffer, MAX_TABLE_NAME_LEN, pRpcLogConfig->rgchTableName); (VOID) ConvertUnicodeToAnsi( cBuffer, ilConfig.u.logSql.rgchTableName, MAX_PATH); CopyUnicodeStringToBuffer(cBuffer, UNLEN, pRpcLogConfig->rgchUserName); (VOID) ConvertUnicodeToAnsi( cBuffer, ilConfig.u.logSql.rgchUserName, MAX_PATH); CopyUnicodeStringToBuffer(cBuffer, CNLEN, pRpcLogConfig->rgchPassword); (VOID) ConvertUnicodeToAnsi( cBuffer, ilConfig.u.logSql.rgchPassword, MAX_PATH); break; default: return (ERROR_INVALID_PARAMETER); } // switch() // // Now the ilConfig contains the local data related to configuration. // call modify log config to modify dynamically the log handle. // pLogging->SetConfig( &ilConfig ); return (dwError); } // SetInetLogConfiguration() BOOL GenerateIpList( BOOL fIsGrant, ADDRESS_CHECK *pCheck, LPINET_INFO_IP_SEC_LIST *ppInfo ) /*++ Routine Description: generate an IP address list from an access check object Arguments: fIsGrant - TRUE to access grant list, FALSE to access deny list pCheck - ptr to address check object to query from ppInfo - updated with ptr to IP list if success Return: TRUE if success, otherwise FALSE --*/ { UINT iM = pCheck->GetNbAddr( fIsGrant ); LPINET_INFO_IP_SEC_LIST pInfo; LPINET_INFO_IP_SEC_ENTRY pI; UINT x; if ( iM == 0 ) { *ppInfo = NULL; return TRUE; } if ( pInfo = (LPINET_INFO_IP_SEC_LIST)MIDL_user_allocate( sizeof(INET_INFO_IP_SEC_LIST) + iM * sizeof(INET_INFO_IP_SEC_ENTRY) ) ) { pInfo->cEntries = 0; for ( x = 0, pI = pInfo->aIPSecEntry ; x < iM ; ++x ) { LPBYTE pM; LPBYTE pA; DWORD dwF; if ( pCheck->GetAddr( fIsGrant, x, &dwF, &pM, &pA ) && dwF == AF_INET ) { pI->dwMask = *(LPDWORD)pM; pI->dwNetwork = *(LPDWORD)pA; ++pI; ++pInfo->cEntries; } } *ppInfo = pInfo; return TRUE; } SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } BOOL FillAddrCheckFromIpList( BOOL fIsGrant, LPINET_INFO_IP_SEC_LIST pInfo, ADDRESS_CHECK *pCheck ) /*++ Routine Description: Fill an access check object from an IP address list from Arguments: fIsGrant - TRUE to access grant list, FALSE to access deny list pInfo - ptr to IP address list pCheck - ptr to address check object to update Return: TRUE if success, otherwise FALSE --*/ { UINT x; if ( pInfo ) { for ( x = 0 ; x < pInfo->cEntries ; ++x ) { if ( ! pCheck->AddAddr( fIsGrant, AF_INET, (LPBYTE)&pInfo->aIPSecEntry[x].dwMask, (LPBYTE)&pInfo->aIPSecEntry[x].dwNetwork ) ) { return FALSE; } } } return TRUE; } BOOL GetVrootCount( PVOID pvContext, MB * pmb, VIRTUAL_ROOT * pvr ) /*++ Routine Description: Virtual directory enumerater callback that calculates the total required buffer size Arguments: pvContext is a dword * that receives the count of virtual directories Return: TRUE if success, otherwise FALSE --*/ { *((DWORD *) pvContext) += 1; return TRUE; } BOOL GetVroots( PVOID pvContext, MB * pmb, VIRTUAL_ROOT * pvr ) /*++ Routine Description: Virtual directory enumerater callback that allocates and builds the virtual directory structure list Arguments: pvContext is a pointer to the midl allocated memory Return: TRUE if success, otherwise FALSE --*/ { LPINET_INFO_VIRTUAL_ROOT_LIST pvrl = (LPINET_INFO_VIRTUAL_ROOT_LIST) pvContext; DWORD i = pvrl->cEntries; LPINET_INFO_VIRTUAL_ROOT_ENTRY pvre = &pvrl->aVirtRootEntry[i]; // // Password doesn't go on the wire // DBG_ASSERT( pvr->pszAlias[0] == '/' ); if ( !ConvertStringToRpc( &pvre->pszRoot, pvr->pszAlias ) || !ConvertStringToRpc( &pvre->pszDirectory, pvr->pszPath ) || !ConvertStringToRpc( &pvre->pszAddress, "" ) || !ConvertStringToRpc( &pvre->pszAccountName, pvr->pszUserName )) { FreeRpcString( pvre->pszRoot ); pvre->pszRoot = NULL; FreeRpcString( pvre->pszDirectory ); pvre->pszDirectory = NULL; FreeRpcString( pvre->pszAddress ); pvre->pszAddress = NULL; FreeRpcString( pvre->pszAccountName ); pvre->pszAccountName = NULL; return FALSE; } pvre->dwMask = pvr->dwAccessPerm; pmb->GetDword( pvr->pszAlias, MD_WIN32_ERROR, IIS_MD_UT_SERVER, &pvre->dwError ); pvrl->cEntries++; return TRUE; }