|
|
#include "DfsGeneric.hxx"
#include "dsgetdc.h"
#include "dsrole.h"
#include "DfsDomainInformation.hxx"
#include "DfsTrustedDomain.hxx"
#include "DfsReferralData.hxx"
#include "DomainControllerSupport.hxx"
#include "DfsReplica.hxx"
#include "dfsadsiapi.hxx"
#include "lmdfs.h"
#include "dfserror.hxx"
#include "dfsfilterapi.hxx"
#include "DomainControllerSupport.tmh"
#define RemoteServerNameString L"remoteServerName"
extern DFS_SERVER_GLOBAL_DATA DfsServerGlobalData;
#define HRESULT_TO_DFSSTATUS(_x) (_x)
DFSSTATUS DfsDcInit( PBOOLEAN pIsDc ) { PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL; DFSSTATUS Status = ERROR_SUCCESS; HANDLE hThread; DWORD idThread;
Status = DsRoleGetPrimaryDomainInformation( NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&pPrimaryDomainInfo);
if (Status == ERROR_SUCCESS) { #if defined (DC_TESTING)
pPrimaryDomainInfo->MachineRole = DsRole_RoleBackupDomainController; #endif
if(pPrimaryDomainInfo->MachineRole == DsRole_RoleStandaloneServer) { DfsServerGlobalData.IsWorkGroup = TRUE; } else { DfsServerGlobalData.IsWorkGroup = FALSE; }
if ( (pPrimaryDomainInfo->MachineRole == DsRole_RoleBackupDomainController) || (pPrimaryDomainInfo->MachineRole == DsRole_RolePrimaryDomainController) ) { *pIsDc = TRUE;
hThread = CreateThread( NULL, //Security attributes
0, //Use default stack size
DcUpdateLoop, // Thread entry procedure
0, // Thread context parameter
0, // Start immediately
&idThread); // Thread ID
if (hThread == NULL) { //
// log this
//
Status = GetLastError(); } else { CloseHandle(hThread); } }
DfsSetDomainNameFlat( pPrimaryDomainInfo->DomainNameFlat); DfsSetDomainNameDns( pPrimaryDomainInfo->DomainNameDns);
DsRoleFreeMemory(pPrimaryDomainInfo);
}
return Status; }
#define DC_PERIODIC_UPDATE_INTERVAL (1000 * 60 * 10) // 10 minutes
DWORD DcUpdateLoop( LPVOID lpThreadParams) {
UNREFERENCED_PARAMETER(lpThreadParams);
DFSSTATUS Status; DfsDomainInformation *pDomainInfo; LONG InitialRetry = 10; ULONG SleepTime = 1000 * 15; // 15 seconds.
static ULONG DomainRefreshFixedInterval; static ULONG DomainRefreshIntervalOnError; ULONG DomainRefreshTime;
// Default is 72 update intervals -> 12 hrs
DomainRefreshFixedInterval = (DfsServerGlobalData.DomainNameRefreshInterval / (DC_PERIODIC_UPDATE_INTERVAL / 1000));
// On enumeration errors, one hour (1/12th the time of domain-name-refresh-interval) is a better time to wait.
DomainRefreshIntervalOnError = DomainRefreshFixedInterval/12; if (DomainRefreshFixedInterval == 0) { DomainRefreshFixedInterval = 1; } if (DomainRefreshIntervalOnError == 0) { DomainRefreshIntervalOnError = 1; } pDomainInfo = NULL; Status = GetDomainInformation(&pDomainInfo); //
// It's possible to get only a part of the domain information because of an error
// (typically because the DC discovery failed). In that case we have a Status != ERROR_SUCCESS here,
// but still have a valid DomainInfo.
//
if (pDomainInfo != NULL) { DfsSetGlobalDomainInfo(pDomainInfo); pDomainInfo->ReleaseReference(); pDomainInfo = NULL; } while ( (Status != ERROR_SUCCESS) && (InitialRetry-- > 0) ) { WaitForSingleObject(DfsServerGlobalData.ShutdownHandle, SleepTime);
if (DfsIsShuttingDown()) { goto Exit; }
Status = GetDomainInformation(&pDomainInfo); if (pDomainInfo != NULL) { DfsSetGlobalDomainInfo(pDomainInfo); pDomainInfo->ReleaseReference(); pDomainInfo = NULL; } DFS_TRACE_LOW(REFERRAL_SERVER, "startup Updating Domain info...%p, %x\n", pDomainInfo,Status); }
SleepTime = DC_PERIODIC_UPDATE_INTERVAL; DomainRefreshTime = DomainRefreshFixedInterval;
do { WaitForSingleObject(DfsServerGlobalData.ShutdownHandle, SleepTime);
if (DfsIsShuttingDown()) { break; }
//
// every so often, throw away our entire domain information
// and rebuild.
// Every 10 minutes, purge our DC information.
//
//
if (--DomainRefreshTime == 0) { Status = GetDomainInformation(&pDomainInfo); DFS_TRACE_LOW(REFERRAL_SERVER, "DcUpdateLoop: Updating Domain info...%p, %x\n", pDomainInfo,Status);
// It'll be 12 * 6 more update-intervals for the next enumeration if everythings working well.
// Otherwise it's 6.
if (Status == ERROR_SUCCESS) { DomainRefreshTime = DomainRefreshFixedInterval; // 72 update intervals = 12 hrs by default.
} else { DomainRefreshTime = DomainRefreshIntervalOnError; // 6 update intervals = 1 hr
DFS_TRACE_HIGH(REFERRAL_SERVER, "DcUpdateLoop: Status 0x%x enumerating domains (info=%p), Resetting refresh time to %d mins\n", Status, pDomainInfo, DomainRefreshTime * SleepTime / 60000); } if (pDomainInfo != NULL) { DfsSetGlobalDomainInfo(pDomainInfo); pDomainInfo->ReleaseReference(); pDomainInfo = NULL; } } else { Status = DfsAcquireDomainInfo(&pDomainInfo); DFS_TRACE_LOW(REFERRAL_SERVER, "DcUpdateLoop: Purging DC info...%p, %x\n", pDomainInfo,Status); if (Status == ERROR_SUCCESS) { pDomainInfo->PurgeDCReferrals(); pDomainInfo->ReleaseReference(); pDomainInfo = NULL; }
}
} while ( TRUE );
Exit:
return 0; }
DFSSTATUS GetDomainInformation( DfsDomainInformation **ppDomainInfo ) { DFSSTATUS Status = ERROR_SUCCESS; DFSSTATUS XforestStatus = ERROR_SUCCESS; *ppDomainInfo = NULL;
DfsDomainInformation *pNewDomainInfo = new DfsDomainInformation( &Status, &XforestStatus ); if (pNewDomainInfo == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } else if (Status != ERROR_SUCCESS) { pNewDomainInfo->ReleaseReference(); } if (Status == ERROR_SUCCESS) { *ppDomainInfo = pNewDomainInfo;
//
// Although we have a DomainInfo, our x-forest enumerations
// may have failed. We go ahead, but we still let the caller know
// that it needs to retry by sending the error status.
//
Status = XforestStatus; }
return Status; }
#if 0
//+-------------------------------------------------------------------------
//
// Function: DfsGenerateReferralDataFromRemoteServerNames
// IADs *pObject - the object
// DfsfolderReferralData *pReferralData - the referral data
//
//
// Returns: Status: Success or Error status code
//
// Description: This routine reads the remote server name
// attribute and creates a referral data structure
// so that we can pass a referral based on these names.
//
//--------------------------------------------------------------------------
DFSSTATUS DfsGenerateReferralDataFromRemoteServerNames( LPWSTR RootName, DfsReferralData **ppReferralData ) { HRESULT HResult = S_OK; BOOLEAN CacheHit = FALSE; DfsReplica *pReplicas = NULL; DFSSTATUS Status = ERROR_SUCCESS; DfsReferralData *pReferralData = NULL; IADs *pObject = NULL; VARIANT Variant;
LPWSTR pszAttrs[] = { RemoteServerNameString }; DWORD Number = sizeof(pszAttrs) / sizeof(LPWSTR);
DFS_TRACE_HIGH( REFERRAL_SERVER, "Entering DfsGenerateReferralDataFromRemoteServerNames for RootName %ws\n", RootName);
Status = DfsGetDfsRootADObject(NULL, RootName, &pObject );
if (Status == ERROR_SUCCESS) { pReferralData = new DfsReferralData (&Status ); if (pReferralData == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; } else if (Status != ERROR_SUCCESS) { pReferralData->ReleaseReference(); }
VariantInit( &Variant );
//
// Try to cache the RemoteServerNameString attribute locally,
// if this fails, we dont care since we will be using the
// GetEx call anyway.
//
if (Status == ERROR_SUCCESS) { HResult = ADsBuildVarArrayStr( pszAttrs, Number, &Variant); if ( SUCCEEDED(HResult)) { HResult = pObject->GetInfoEx( Variant, 0); }
VariantClear( &Variant); }
if (Status == ERROR_SUCCESS) { LONG StartNdx, LastNdx; SAFEARRAY *PropertyArray;
HResult = pObject->GetEx( RemoteServerNameString, &Variant ); if ( SUCCEEDED(HResult) ) { PropertyArray = V_ARRAY( &Variant ); HResult = SafeArrayGetLBound( PropertyArray, 1, &StartNdx ); if ( SUCCEEDED(HResult) ) { HResult = SafeArrayGetUBound( PropertyArray, 1, &LastNdx ); } } else { DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsGenerateReferralDataFromRemoteServerNames-GetEx failed for RootName %ws with Status %x\n", RootName, HResult); }
if ( SUCCEEDED(HResult) && (LastNdx > StartNdx) ) { VARIANT VariantItem; pReplicas = new DfsReplica [ LastNdx - StartNdx ];
if (pReplicas != NULL) {
for ( LONG Index = StartNdx; Index < LastNdx; Index++ ) {
VariantInit( &VariantItem );
CacheHit = FALSE;
HResult = SafeArrayGetElement( PropertyArray, &Index, &VariantItem );
if ( SUCCEEDED(HResult) ) { UNICODE_STRING ServerName, Remaining, Replica; LPWSTR ReplicaString = V_BSTR( &VariantItem );
Status = DfsRtlInitUnicodeStringEx( &Replica, ReplicaString ); if(Status == ERROR_SUCCESS) { DfsGetFirstComponent( &Replica, &ServerName, &Remaining );
Status = (&pReplicas[ Index - StartNdx])->SetTargetServer( &ServerName, &CacheHit ); if (Status == ERROR_SUCCESS) { Status = (&pReplicas[ Index - StartNdx])->SetTargetFolder( &Remaining ); } } } else { Status = DfsGetErrorFromHr( HResult );
DFS_TRACE_HIGH(REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames- SafeArrayGetElement for RootName %ws with Status %x\n", RootName, HResult); }
VariantClear( &VariantItem );
if (Status != ERROR_SUCCESS) { delete [] pReplicas; pReplicas = NULL; break; } } } else { Status = ERROR_NOT_ENOUGH_MEMORY; } } else { DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsGenerateReferralDataFromRemoteServerNames- DfsGetDfsRootADObjectfailed for RootName %ws with Status %x\n", RootName, HResult); Status = DfsGetErrorFromHr( HResult ); }
VariantClear( &Variant );
if (Status == ERROR_SUCCESS) { pReferralData->Timeout = DFS_DEFAULT_REFERRAL_TIMEOUT; pReferralData->ReplicaCount = LastNdx - StartNdx; pReferralData->pReplicas = pReplicas; *ppReferralData = pReferralData; }
if (Status != ERROR_SUCCESS) { pReferralData->ReleaseReference(); } } pObject->Release(); }
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames for RootName %ws and Status %x\n", RootName, HResult);
return Status; }
#endif
//+-------------------------------------------------------------------------
//
// Function: DfsUpdateRemoteServerName
//
// Arguments:
// IADs *pObject - the ds object of interest.
// LPWSTR ServerName - the server name to add or delete
// LPWSTR RemainingName - the rest of the name
// BOOLEAN Add - true for add, false for delete.
//
// Returns: Status: Success or Error status code
//
// Description: This routine updates the RemoteServerName attribute
// in the DS object, either adding a \\servername\remaining
// to the existing DS attribute or removing it, depending
// on add/delete.
// The caller must make sure this parameter does not
// already exist in the add case, or that the parameter
// to be deleted does exist in the delete case.
//
//--------------------------------------------------------------------------
DFSSTATUS DfsUpdateRemoteServerName( IADs *pObject, LPWSTR ServerName, LPWSTR RemainingName, BOOLEAN Add ) { HRESULT HResult = S_OK; DFSSTATUS Status = ERROR_SUCCESS; VARIANT Variant; UNICODE_STRING UpdateName; LPWSTR pServers[1]; BSTR RemoteServerNameBstr;
RemoteServerNameBstr = SysAllocString(RemoteServerNameString); if (RemoteServerNameBstr == NULL) { return ERROR_NOT_ENOUGH_MEMORY; }
DFS_TRACE_HIGH( REFERRAL_SERVER, "Entering DfsUpdateRemoteServerName for ServerName %ws, remaining name %ws, add %d\n", ServerName, RemainingName, Add); //
// create a unc path using the server and remaining name
// to get a path of type \\servername\remainingname
//
Status = DfsCreateUnicodePathString( &UpdateName, 2, // unc path: 2 leading seperators,
ServerName, RemainingName);
pServers[0] = UpdateName.Buffer;
if (Status == ERROR_SUCCESS) { //
// initialize the variant.
//
VariantInit( &Variant );
//
// Create the variant array with a single entry in it.
//
HResult = ADsBuildVarArrayStr( pServers, 1, &Variant );
if ( SUCCEEDED(HResult) ) { //
// either append or delete this string from the remote server
// name attribute
//
HResult = pObject->PutEx( (Add ? ADS_PROPERTY_APPEND : ADS_PROPERTY_DELETE), RemoteServerNameBstr, Variant ); if ( SUCCEEDED(HResult) ) { //
// now update the object in the DS with this info.
//
HResult = pObject->SetInfo(); }
//
// clear the variant
//
VariantClear( &Variant ); }
if ( SUCCEEDED(HResult) == FALSE) { Status = DfsGetErrorFromHr( HResult ); }
//
// free the unicode string we created earlier on.
//
DfsFreeUnicodeString( &UpdateName ); }
SysFreeString(RemoteServerNameBstr);
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Leaving DfsUpdateRemoteServerName ServerName %ws, RemainingName %ws, Add %d, and Status %x\n", ServerName, RemainingName, Add, HResult); return Status; }
DFSSTATUS DfsDcEnumerateRoots( LPWSTR DfsName, LPBYTE pBuffer, ULONG BufferSize, PULONG pEntriesRead, DWORD MaxEntriesToRead, LPDWORD pResumeHandle, PULONG pSizeRequired ) { DFSSTATUS Status = ERROR_SUCCESS; DFSSTATUS PDCStatus; ULONG_PTR CurrentBuffer = (ULONG_PTR)pBuffer; ULONG CurrentSize = BufferSize; DfsString *pPDCName = NULL; LPWSTR UseDC = NULL;
UNREFERENCED_PARAMETER(DfsName);
PDCStatus = DfsGetBlobPDCName( &pPDCName, 0 ); if (PDCStatus == ERROR_SUCCESS) { UseDC = pPDCName->GetString(); //
// At this point we dont care: if we got a dc name use it,
// otherwise, just keep going, we will go to the local dc.
//
Status = DfsEnumerateDfsADRoots( UseDC, &CurrentBuffer, &CurrentSize, pEntriesRead, MaxEntriesToRead, pResumeHandle, pSizeRequired );
DfsReleaseBlobPDCName(pPDCName); }
return Status; }
DFSSTATUS DfsUpdateRootRemoteServerName( LPWSTR Root, LPWSTR DCName, LPWSTR ServerName, LPWSTR RemainingName, BOOLEAN Add )
{ IADs *pRootObject = NULL; DFSSTATUS Status = ERROR_SUCCESS;
Status = DfsGetDfsRootADObject( DCName, Root, &pRootObject );
if (Status == ERROR_SUCCESS) { Status = DfsUpdateRemoteServerName( pRootObject, ServerName, RemainingName, Add ); pRootObject->Release(); } return Status; }
#define UNICODE_STRING_STRUCT(s) \
{sizeof(s) - sizeof(WCHAR), sizeof(s) - sizeof(WCHAR), (s)}
static UNICODE_STRING DfsSpecialDCShares[] = { UNICODE_STRING_STRUCT(L"SYSVOL"), UNICODE_STRING_STRUCT(L"NETLOGON"), };
//+-------------------------------------------------------------------------
//
// Function: DfsIsRemoteServerNameEqual
//
//
// Returns: Status: Success or Error status code. pIsEqual = TRUE if
// the remoteServerName contains the passed in pServerName in its entirety.
//
// Description: This routine reads the remote server name
// attribute and does a string match on its components.
//--------------------------------------------------------------------------
DFSSTATUS DfsIsRemoteServerNameEqual( LPWSTR RootName, PUNICODE_STRING pServerName, PBOOLEAN pIsEqual) { HRESULT HResult = S_OK; DFSSTATUS Status = ERROR_SUCCESS; IADs *pObject = NULL; VARIANT Variant;
LPWSTR pszAttrs[] = { RemoteServerNameString }; DWORD Number = sizeof(pszAttrs) / sizeof(LPWSTR);
DFS_TRACE_NORM( REFERRAL_SERVER, "Entering DfsIsRemoteServerNameEqual for RootName %ws, ServerName %wZ\n", RootName, pServerName);
*pIsEqual = FALSE; Status = DfsGetDfsRootADObject(NULL, RootName, &pObject );
if (Status == ERROR_SUCCESS) { VariantInit( &Variant );
HResult = ADsBuildVarArrayStr( pszAttrs, Number, &Variant); if ( SUCCEEDED(HResult)) { LONG StartNdx, LastNdx; SAFEARRAY *PropertyArray; BSTR RemoteServerNameBstr = SysAllocString(RemoteServerNameString); if (RemoteServerNameBstr == NULL) { Status = ERROR_NOT_ENOUGH_MEMORY; }
if (Status == ERROR_SUCCESS) { HResult = pObject->GetEx( RemoteServerNameBstr, &Variant ); if ( SUCCEEDED(HResult) ) { PropertyArray = V_ARRAY( &Variant ); HResult = SafeArrayGetLBound( PropertyArray, 1, &StartNdx ); if ( SUCCEEDED(HResult) ) { HResult = SafeArrayGetUBound( PropertyArray, 1, &LastNdx ); } } else { DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsIsRemoteServerNameEqual -GetEx failed for RootName %ws with Status %x\n", RootName, HResult); }
if ( SUCCEEDED(HResult) && (LastNdx > StartNdx) ) { VARIANT VariantItem;
for ( LONG Index = StartNdx; Index < LastNdx; Index++ ) { VariantInit( &VariantItem ); HResult = SafeArrayGetElement( PropertyArray, &Index, &VariantItem );
if ( SUCCEEDED(HResult) ) { UNICODE_STRING FirstComp, Replica; LPWSTR ReplicaString = V_BSTR( &VariantItem );
Status = DfsRtlInitUnicodeStringEx( &Replica, ReplicaString ); if(Status == ERROR_SUCCESS) { Status = DfsGetFirstComponent( &Replica, &FirstComp, NULL ); if (Status == ERROR_SUCCESS) { //
// See if this component matches our servername in its entirety.
//
if (RtlEqualDomainName( pServerName, &FirstComp )) { *pIsEqual = TRUE; } } } } else { Status = DfsGetErrorFromHr( HResult );
DFS_TRACE_HIGH(REFERRAL_SERVER, "Leaving DfsGenerateReferralDataFromRemoteServerNames- SafeArrayGetElement for RootName %ws with Status %x\n", RootName, HResult); }
VariantClear( &VariantItem );
if (Status != ERROR_SUCCESS || *pIsEqual == TRUE) { break; } } } if (RemoteServerNameBstr != NULL) { SysFreeString( RemoteServerNameBstr ); } }
VariantClear( &Variant );
} pObject->Release(); } else { DFS_TRACE_HIGH(REFERRAL_SERVER, "DfsIsRemoteServerNameEqual- DfsGetDfsRootADObjectfailed for RootName %ws with Status %x\n", RootName, HResult); Status = DfsGetErrorFromHr( HResult ); }
DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER, "Leaving DfsIsRemoteServerNameEqual for RootName %ws, Server %wZ, Status %x, IsEqual? 0x%x\n", RootName, pServerName, HResult, *pIsEqual);
return Status; }
BOOLEAN DfsIsSpecialDomainShare( PUNICODE_STRING pShareName ) { ULONG Index; ULONG TotalShares; BOOLEAN SpecialShare = FALSE;
TotalShares = sizeof(DfsSpecialDCShares) / sizeof(DfsSpecialDCShares[0]); for (Index = 0; Index < TotalShares; Index++ ) { if (DfsSpecialDCShares[Index].Length == pShareName->Length) { if (_wcsnicmp(DfsSpecialDCShares[Index].Buffer, pShareName->Buffer, pShareName->Length/sizeof(WCHAR)) == 0) { SpecialShare = TRUE; break; } } }
return SpecialShare; }
|