|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Runtime.InteropServices;
namespace System.DirectoryServices.ActiveDirectory
{
[Flags]
public enum SyncFromAllServersOptions
{
None = 0,
AbortIfServerUnavailable = 1,
SyncAdjacentServerOnly = 2,
CheckServerAlivenessOnly = 0x8,
SkipInitialCheck = 0x10,
PushChangeOutward = 0x20,
CrossSite = 0x40
}
public enum SyncFromAllServersEvent
{
Error = 0,
SyncStarted = 1,
SyncCompleted = 2,
Finished = 3
}
public enum SyncFromAllServersErrorCategory
{
ErrorContactingServer = 0,
ErrorReplicating = 1,
ServerUnreachable = 2
}
public delegate bool SyncUpdateCallback(SyncFromAllServersEvent eventType, string? targetServer, string? sourceServer, SyncFromAllServersOperationException? exception);
internal delegate bool SyncReplicaFromAllServersCallback(IntPtr data, IntPtr update);
public class DomainController : DirectoryServer
{
private IntPtr _dsHandle = IntPtr.Zero;
private IntPtr _authIdentity = IntPtr.Zero;
private readonly string[] _becomeRoleOwnerAttrs = null!;
private bool _disposed;
// internal variables for the public properties
private string? _cachedComputerObjectName;
private string? _cachedOSVersion;
private double _cachedNumericOSVersion;
private Forest? _currentForest;
private Domain? _cachedDomain;
private ActiveDirectoryRoleCollection? _cachedRoles;
private bool _dcInfoInitialized;
internal SyncUpdateCallback? userDelegate;
internal readonly SyncReplicaFromAllServersCallback syncAllFunctionPointer = null!;
// this is twice the maximum allowed RIDPool size which is 15k
internal const int UpdateRidPoolSeizureValue = 30000;
#region constructors
// Internal constructors
protected DomainController()
{
}
internal DomainController(DirectoryContext context, string domainControllerName)
: this(context, domainControllerName, new DirectoryEntryManager(context))
{
}
internal DomainController(DirectoryContext context, string domainControllerName, DirectoryEntryManager directoryEntryMgr)
{
this.context = context;
this.replicaName = domainControllerName;
this.directoryEntryMgr = directoryEntryMgr;
// initialize the transfer role owner attributes
_becomeRoleOwnerAttrs = new string[5];
_becomeRoleOwnerAttrs[0] = PropertyManager.BecomeSchemaMaster;
_becomeRoleOwnerAttrs[1] = PropertyManager.BecomeDomainMaster;
_becomeRoleOwnerAttrs[2] = PropertyManager.BecomePdc;
_becomeRoleOwnerAttrs[3] = PropertyManager.BecomeRidMaster;
_becomeRoleOwnerAttrs[4] = PropertyManager.BecomeInfrastructureMaster;
// initialize the callback function
syncAllFunctionPointer = new SyncReplicaFromAllServersCallback(SyncAllCallbackRoutine);
}
#endregion constructors
#region IDisposable
~DomainController() => Dispose(false);
// private Dispose method
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
try
{
// if there are any managed or unmanaged
// resources to be freed, those should be done here
// if disposing = true, only unmanaged resources should
// be freed, else both managed and unmanaged.
FreeDSHandle();
_disposed = true;
}
finally
{
base.Dispose();
}
}
}
#endregion IDisposable
#region public methods
public static DomainController GetDomainController(DirectoryContext context)
{
string? dcDnsName = null;
DirectoryEntryManager? directoryEntryMgr = null;
// check that the context argument is not null
if (context == null)
throw new ArgumentNullException(nameof(context));
// target should be DC
if (context.ContextType != DirectoryContextType.DirectoryServer)
{
throw new ArgumentException(SR.TargetShouldBeDC, nameof(context));
}
// target should be a server
if (!(context.isServer()))
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFound, context.Name), typeof(DomainController), context.Name);
}
// work with copy of the context
context = new DirectoryContext(context);
try
{
// Get dns name of the dc
// by binding to root dse and getting the "dnsHostName" attribute
directoryEntryMgr = new DirectoryEntryManager(context);
DirectoryEntry rootDSE = directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE);
if (!Utils.CheckCapability(rootDSE, Capability.ActiveDirectory))
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFound, context.Name), typeof(DomainController), context.Name);
}
dcDnsName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName)!;
}
catch (COMException e)
{
int errorCode = e.ErrorCode;
if (errorCode == unchecked((int)0x8007203a))
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFound, context.Name), typeof(DomainController), context.Name);
}
else
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
}
return new DomainController(context, dcDnsName, directoryEntryMgr);
}
public static DomainController FindOne(DirectoryContext context)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
return FindOneWithCredentialValidation(context, null, 0);
}
public static DomainController FindOne(DirectoryContext context, string siteName)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
ArgumentNullException.ThrowIfNull(siteName);
return FindOneWithCredentialValidation(context, siteName, 0);
}
public static DomainController FindOne(DirectoryContext context, LocatorOptions flag)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
return FindOneWithCredentialValidation(context, null, flag);
}
public static DomainController FindOne(DirectoryContext context, string siteName, LocatorOptions flag)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
ArgumentNullException.ThrowIfNull(siteName);
return FindOneWithCredentialValidation(context, siteName, flag);
}
public static DomainControllerCollection FindAll(DirectoryContext context)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
// work with copy of the context
context = new DirectoryContext(context);
return FindAllInternal(context, context.Name, false /* isDnsDomainName */, null);
}
public static DomainControllerCollection FindAll(DirectoryContext context, string siteName)
{
ArgumentNullException.ThrowIfNull(context);
if (context.ContextType != DirectoryContextType.Domain)
{
throw new ArgumentException(SR.TargetShouldBeDomain, nameof(context));
}
ArgumentNullException.ThrowIfNull(siteName);
// work with copy of the context
context = new DirectoryContext(context);
return FindAllInternal(context, context.Name, false /* isDnsDomainName */, siteName);
}
public virtual GlobalCatalog EnableGlobalCatalog()
{
CheckIfDisposed();
try
{
// bind to the server object
DirectoryEntry serverNtdsaEntry = directoryEntryMgr.GetCachedDirectoryEntry(NtdsaObjectName);
// set the NTDSDSA_OPT_IS_GC flag on the "options" property
int options = 0;
if (serverNtdsaEntry.Properties[PropertyManager.Options].Value != null)
{
options = (int)serverNtdsaEntry.Properties[PropertyManager.Options].Value!;
}
serverNtdsaEntry.Properties[PropertyManager.Options].Value = options | 1;
serverNtdsaEntry.CommitChanges();
}
catch (COMException e)
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
// return a GlobalCatalog object
return new GlobalCatalog(context, Name);
}
public virtual bool IsGlobalCatalog()
{
CheckIfDisposed();
try
{
DirectoryEntry serverNtdsaEntry = directoryEntryMgr.GetCachedDirectoryEntry(NtdsaObjectName);
serverNtdsaEntry.RefreshCache();
// check if the NTDSDSA_OPT_IS_GC flag is set in the
// "options" attribute (lowest bit)
int options = 0;
if (serverNtdsaEntry.Properties[PropertyManager.Options].Value != null)
{
options = (int)serverNtdsaEntry.Properties[PropertyManager.Options].Value!;
}
if ((options & (1)) == 1)
{
return true;
}
else
{
return false;
}
}
catch (COMException e)
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
}
public void TransferRoleOwnership(ActiveDirectoryRole role)
{
CheckIfDisposed();
if (role < ActiveDirectoryRole.SchemaRole || role > ActiveDirectoryRole.InfrastructureRole)
{
throw new InvalidEnumArgumentException(nameof(role), (int)role, typeof(ActiveDirectoryRole));
}
try
{
// set the appropriate attribute on the rootDSE
DirectoryEntry rootDSE = directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE);
rootDSE.Properties[_becomeRoleOwnerAttrs[(int)role]].Value = 1;
rootDSE.CommitChanges();
}
catch (COMException e)
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
// invalidate the role collection so that it gets loaded again next time
_cachedRoles = null;
}
public void SeizeRoleOwnership(ActiveDirectoryRole role)
{
// set the "fsmoRoleOwner" attribute on the appropriate role object
// to the NTDSAObjectName of this DC
string? roleObjectDN = null;
CheckIfDisposed();
switch (role)
{
case ActiveDirectoryRole.SchemaRole:
{
roleObjectDN = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.SchemaNamingContext);
break;
}
case ActiveDirectoryRole.NamingRole:
{
roleObjectDN = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.PartitionsContainer);
break;
}
case ActiveDirectoryRole.PdcRole:
{
roleObjectDN = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.DefaultNamingContext);
break;
}
case ActiveDirectoryRole.RidRole:
{
roleObjectDN = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.RidManager);
break;
}
case ActiveDirectoryRole.InfrastructureRole:
{
roleObjectDN = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.Infrastructure);
break;
}
default:
throw new InvalidEnumArgumentException(nameof(role), (int)role, typeof(ActiveDirectoryRole));
}
DirectoryEntry? roleObjectEntry = null;
try
{
roleObjectEntry = DirectoryEntryManager.GetDirectoryEntry(context, roleObjectDN);
// For RID FSMO role
// Increment the RIDAvailablePool by 30k.
if (role == ActiveDirectoryRole.RidRole)
{
System.DirectoryServices.UnsafeNativeMethods.IADsLargeInteger ridPool = (System.DirectoryServices.UnsafeNativeMethods.IADsLargeInteger)roleObjectEntry.Properties[PropertyManager.RIDAvailablePool].Value!;
// check the overflow of the low part
if (ridPool.LowPart + UpdateRidPoolSeizureValue < ridPool.LowPart)
{
throw new InvalidOperationException(SR.UpdateAvailableRIDPoolOverflowFailure);
}
ridPool.LowPart += UpdateRidPoolSeizureValue;
roleObjectEntry.Properties[PropertyManager.RIDAvailablePool].Value = ridPool;
}
roleObjectEntry.Properties[PropertyManager.FsmoRoleOwner].Value = NtdsaObjectName;
roleObjectEntry.CommitChanges();
}
catch (COMException e)
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
finally
{
roleObjectEntry?.Dispose();
}
// invalidate the role collection so that it gets loaded again next time
_cachedRoles = null;
}
public virtual DirectorySearcher GetDirectorySearcher()
{
CheckIfDisposed();
return InternalGetDirectorySearcher();
}
public override void CheckReplicationConsistency()
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
// get the handle
GetDSHandle();
// call private helper function
CheckConsistencyHelper(_dsHandle, DirectoryContext.ADHandle);
}
public override ReplicationCursorCollection GetReplicationCursors(string partition)
{
IntPtr info = (IntPtr)0;
int context = 0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (partition == null)
throw new ArgumentNullException(nameof(partition));
if (partition.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(partition));
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_CURSORS_3_FOR_NC, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_CURSORS_FOR_NC, partition, ref advanced, context, DirectoryContext.ADHandle);
return ConstructReplicationCursors(_dsHandle, advanced, info, partition, this, DirectoryContext.ADHandle);
}
public override ReplicationOperationInformation GetReplicationOperationInformation()
{
IntPtr info = (IntPtr)0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_PENDING_OPS, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_PENDING_OPS, null, ref advanced, 0, DirectoryContext.ADHandle);
return ConstructPendingOperations(info, this, DirectoryContext.ADHandle);
}
public override ReplicationNeighborCollection GetReplicationNeighbors(string partition)
{
IntPtr info = (IntPtr)0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (partition == null)
throw new ArgumentNullException(nameof(partition));
if (partition.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(partition));
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_NEIGHBORS, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_NEIGHBORS, partition, ref advanced, 0, DirectoryContext.ADHandle);
return ConstructNeighbors(info, this, DirectoryContext.ADHandle);
}
public override ReplicationNeighborCollection GetAllReplicationNeighbors()
{
IntPtr info = (IntPtr)0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_NEIGHBORS, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_NEIGHBORS, null, ref advanced, 0, DirectoryContext.ADHandle);
return ConstructNeighbors(info, this, DirectoryContext.ADHandle);
}
public override ReplicationFailureCollection GetReplicationConnectionFailures()
{
return GetReplicationFailures(DS_REPL_INFO_TYPE.DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES);
}
public override ActiveDirectoryReplicationMetadata GetReplicationMetadata(string objectPath)
{
IntPtr info = (IntPtr)0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (objectPath == null)
throw new ArgumentNullException(nameof(objectPath));
if (objectPath.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(objectPath));
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_METADATA_2_FOR_OBJ, (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_METADATA_FOR_OBJ, objectPath, ref advanced, 0, DirectoryContext.ADHandle);
return ConstructMetaData(advanced, info, this, DirectoryContext.ADHandle);
}
public override void SyncReplicaFromServer(string partition, string sourceServer)
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (partition == null)
throw new ArgumentNullException(nameof(partition));
if (partition.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(partition));
if (sourceServer == null)
throw new ArgumentNullException(nameof(sourceServer));
if (sourceServer.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(sourceServer));
// get the dsHandle
GetDSHandle();
SyncReplicaHelper(_dsHandle, false, partition, sourceServer, 0, DirectoryContext.ADHandle);
}
public override void TriggerSyncReplicaFromNeighbors(string partition)
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (partition == null)
throw new ArgumentNullException(nameof(partition));
if (partition.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(partition));
// get the dsHandle
GetDSHandle();
SyncReplicaHelper(_dsHandle, false, partition, null, DS_REPSYNC_ASYNCHRONOUS_OPERATION | DS_REPSYNC_ALL_SOURCES, DirectoryContext.ADHandle);
}
public override void SyncReplicaFromAllServers(string partition, SyncFromAllServersOptions options)
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
if (partition == null)
throw new ArgumentNullException(nameof(partition));
if (partition.Length == 0)
throw new ArgumentException(SR.EmptyStringParameter, nameof(partition));
// get the dsHandle
GetDSHandle();
SyncReplicaAllHelper(_dsHandle, syncAllFunctionPointer, partition, options, SyncFromAllServersCallback, DirectoryContext.ADHandle);
}
#endregion public methods
#region public properties
public Forest Forest
{
get
{
CheckIfDisposed();
if (_currentForest == null)
{
DirectoryContext forestContext = Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context);
_currentForest = Forest.GetForest(forestContext);
}
return _currentForest;
}
}
public DateTime CurrentTime
{
get
{
CheckIfDisposed();
DirectoryEntry rootDSE = DirectoryEntryManager.GetDirectoryEntry(context, WellKnownDN.RootDSE);
string? serverUTCTime = null;
try
{
serverUTCTime = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.CurrentTime)!;
}
finally
{
rootDSE.Dispose();
}
return ParseDateTime(serverUTCTime);
}
}
public long HighestCommittedUsn
{
get
{
CheckIfDisposed();
DirectoryEntry rootDSE = DirectoryEntryManager.GetDirectoryEntry(context, WellKnownDN.RootDSE);
string? serverHighestCommittedUsn = null;
try
{
serverHighestCommittedUsn = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.HighestCommittedUSN)!;
}
finally
{
rootDSE.Dispose();
}
return long.Parse(serverHighestCommittedUsn, NumberFormatInfo.InvariantInfo);
}
}
public string OSVersion
{
get
{
CheckIfDisposed();
if (_cachedOSVersion == null)
{
// get the operating system version attribute
DirectoryEntry computerEntry = directoryEntryMgr.GetCachedDirectoryEntry(ComputerObjectName);
// is in the form Windows Server 2003
_cachedOSVersion = (string)PropertyManager.GetPropertyValue(context, computerEntry, PropertyManager.OperatingSystem)!;
}
return _cachedOSVersion;
}
}
internal double NumericOSVersion
{
get
{
CheckIfDisposed();
if (_cachedNumericOSVersion == 0)
{
// get the operating system version attribute
DirectoryEntry computerEntry = directoryEntryMgr.GetCachedDirectoryEntry(ComputerObjectName);
// is in the form Windows Server 2003
string osVersion = (string)PropertyManager.GetPropertyValue(context, computerEntry, PropertyManager.OperatingSystemVersion)!;
// this could be in the form 5.2 (3790), so we need to take out the (3790)
int index = osVersion.IndexOf('(');
if (index != -1)
{
osVersion = osVersion.Substring(0, index);
}
_cachedNumericOSVersion = (double)double.Parse(osVersion, NumberFormatInfo.InvariantInfo);
}
return _cachedNumericOSVersion;
}
}
public ActiveDirectoryRoleCollection Roles
{
get
{
CheckIfDisposed();
return _cachedRoles ??= new ActiveDirectoryRoleCollection(GetRoles());
}
}
public Domain Domain
{
get
{
CheckIfDisposed();
if (_cachedDomain == null)
{
string? domainName = null;
try
{
string defaultNCName = directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.DefaultNamingContext);
domainName = Utils.GetDnsNameFromDN(defaultNCName);
}
catch (COMException e)
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
// For domain controllers this is always the
// domain naming context
// create a new domain context for the domain
DirectoryContext domainContext = Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context);
_cachedDomain = new Domain(domainContext, domainName);
}
return _cachedDomain;
}
}
public override string? IPAddress
{
get
{
CheckIfDisposed();
IPHostEntry hostEntry = Dns.GetHostEntry(Name);
if (hostEntry.AddressList.GetLength(0) > 0)
{
return (hostEntry.AddressList[0]).ToString();
}
else
{
return null;
}
}
}
public override string SiteName
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized || siteInfoModified)
{
GetDomainControllerInfo();
}
if (cachedSiteName == null)
{
throw new ActiveDirectoryOperationException(SR.Format(SR.SiteNameNotFound, Name));
}
return cachedSiteName;
}
}
internal string SiteObjectName
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized || siteInfoModified)
{
GetDomainControllerInfo();
}
if (cachedSiteObjectName == null)
{
throw new ActiveDirectoryOperationException(SR.Format(SR.SiteObjectNameNotFound, Name));
}
return cachedSiteObjectName;
}
}
internal string ComputerObjectName
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized)
{
GetDomainControllerInfo();
}
if (_cachedComputerObjectName == null)
{
throw new ActiveDirectoryOperationException(SR.Format(SR.ComputerObjectNameNotFound, Name));
}
return _cachedComputerObjectName;
}
}
internal string ServerObjectName
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized || siteInfoModified)
{
GetDomainControllerInfo();
}
if (cachedServerObjectName == null)
{
throw new ActiveDirectoryOperationException(SR.Format(SR.ServerObjectNameNotFound, Name));
}
return cachedServerObjectName;
}
}
internal string NtdsaObjectName
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized || siteInfoModified)
{
GetDomainControllerInfo();
}
if (cachedNtdsaObjectName == null)
{
throw new ActiveDirectoryOperationException(SR.Format(SR.NtdsaObjectNameNotFound, Name));
}
return cachedNtdsaObjectName;
}
}
internal Guid NtdsaObjectGuid
{
get
{
CheckIfDisposed();
if (!_dcInfoInitialized || siteInfoModified)
{
GetDomainControllerInfo();
}
if (cachedNtdsaObjectGuid.Equals(Guid.Empty))
{
throw new ActiveDirectoryOperationException(SR.Format(SR.NtdsaObjectGuidNotFound, Name));
}
return cachedNtdsaObjectGuid;
}
}
public override SyncUpdateCallback? SyncFromAllServersCallback
{
get
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
return userDelegate;
}
set
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
userDelegate = value;
}
}
public override ReplicationConnectionCollection InboundConnections => GetInboundConnectionsHelper();
public override ReplicationConnectionCollection OutboundConnections => GetOutboundConnectionsHelper();
internal IntPtr Handle
{
get
{
GetDSHandle();
return _dsHandle;
}
}
#endregion public properties
#region private methods
internal static void ValidateCredential(DomainController dc, DirectoryContext context)
{
DirectoryEntry de;
de = new DirectoryEntry("LDAP://" + dc.Name + "/RootDSE", context.UserName, context.Password, Utils.DefaultAuthType | AuthenticationTypes.ServerBind);
de.Bind(true);
}
internal static DomainController FindOneWithCredentialValidation(DirectoryContext context, string? siteName, LocatorOptions flag)
{
DomainController dc;
bool retry = false;
bool credsValidated = false;
// work with copy of the context
context = new DirectoryContext(context);
// authenticate against this DC to validate the credentials
dc = FindOneInternal(context, context.Name, siteName, flag);
try
{
ValidateCredential(dc, context);
credsValidated = true;
}
catch (COMException e)
{
if (e.ErrorCode == unchecked((int)0x8007203a))
{
// server is down , so try again with force rediscovery if the flags did not already contain force rediscovery
if ((flag & LocatorOptions.ForceRediscovery) == 0)
{
retry = true;
}
else
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFoundInDomain, context.Name), typeof(DomainController), null);
}
}
else
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
}
finally
{
if (!credsValidated)
{
dc.Dispose();
}
}
if (retry)
{
credsValidated = false;
dc = FindOneInternal(context, context.Name, siteName, flag | LocatorOptions.ForceRediscovery);
try
{
ValidateCredential(dc, context);
credsValidated = true;
}
catch (COMException e)
{
if (e.ErrorCode == unchecked((int)0x8007203a))
{
// server is down
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFoundInDomain, context.Name), typeof(DomainController), null);
}
else
{
throw ExceptionHelper.GetExceptionFromCOMException(context, e);
}
}
finally
{
if (!credsValidated)
{
dc.Dispose();
}
}
}
return dc;
}
internal static DomainController FindOneInternal(DirectoryContext context, string? domainName, string? siteName, LocatorOptions flag)
{
DomainControllerInfo domainControllerInfo;
int errorCode = 0;
if (siteName != null && siteName.Length == 0)
{
throw new ArgumentException(SR.EmptyStringParameter, nameof(siteName));
}
// check that the flags passed have only the valid bits set
if (((long)flag & (~((long)LocatorOptions.AvoidSelf | (long)LocatorOptions.ForceRediscovery | (long)LocatorOptions.KdcRequired | (long)LocatorOptions.TimeServerRequired | (long)LocatorOptions.WriteableRequired))) != 0)
{
throw new ArgumentException(SR.InvalidFlags, nameof(flag));
}
domainName ??= DirectoryContext.GetLoggedOnDomain();
// call DsGetDcName
errorCode = Locator.DsGetDcNameWrapper(null, domainName, siteName, (long)flag | (long)PrivateLocatorFlags.DirectoryServicesRequired, out domainControllerInfo);
if (errorCode == Interop.Errors.ERROR_NO_SUCH_DOMAIN)
{
throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.DCNotFoundInDomain, domainName), typeof(DomainController), null);
}
// this can only occur when flag is being explicitly passed (since the flags that we pass internally are valid)
if (errorCode == Interop.Errors.ERROR_INVALID_FLAGS)
{
throw new ArgumentException(SR.InvalidFlags, nameof(flag));
}
else if (errorCode != 0)
{
throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
}
// create a DomainController object
Debug.Assert(domainControllerInfo.DomainControllerName.Length > 2);
string domainControllerName = domainControllerInfo.DomainControllerName.Substring(2);
// create a new context object for the domain controller
DirectoryContext dcContext = Utils.GetNewDirectoryContext(domainControllerName, DirectoryContextType.DirectoryServer, context);
return new DomainController(dcContext, domainControllerName);
}
internal static DomainControllerCollection FindAllInternal(DirectoryContext context, string? domainName, bool isDnsDomainName, string? siteName)
{
ArrayList dcList = new ArrayList();
if (siteName != null && siteName.Length == 0)
{
throw new ArgumentException(SR.EmptyStringParameter, nameof(siteName));
}
if (domainName == null || !isDnsDomainName)
{
// get the dns name of the domain
DomainControllerInfo domainControllerInfo;
int errorCode = Locator.DsGetDcNameWrapper(null, domainName ?? DirectoryContext.GetLoggedOnDomain(), null, (long)PrivateLocatorFlags.DirectoryServicesRequired, out domainControllerInfo);
if (errorCode == Interop.Errors.ERROR_NO_SUCH_DOMAIN)
{
// return an empty collection
return new DomainControllerCollection(dcList);
}
else if (errorCode != 0)
{
throw ExceptionHelper.GetExceptionFromErrorCode(errorCode);
}
Debug.Assert(domainControllerInfo.DomainName != null);
domainName = domainControllerInfo.DomainName;
}
foreach (string dcName in Utils.GetReplicaList(context, Utils.GetDNFromDnsName(domainName), siteName, true /* isDefaultNC */, false /* isADAM */, false /* mustBeGC */))
{
DirectoryContext dcContext = Utils.GetNewDirectoryContext(dcName, DirectoryContextType.DirectoryServer, context);
dcList.Add(new DomainController(dcContext, dcName));
}
return new DomainControllerCollection(dcList);
}
private unsafe void GetDomainControllerInfo()
{
int result = 0;
int dcCount = 0;
IntPtr dcInfoPtr = IntPtr.Zero;
int dcInfoLevel = 0;
bool initialized = false;
// get the handle
GetDSHandle();
// call DsGetDomainControllerInfo
/*DWORD DsGetDomainControllerInfo(
HANDLE hDs,
LPTSTR DomainName,
DWORD InfoLevel,
DWORD* pcOut,
VOID** ppInfo
);*/
var dsGetDomainControllerInfo = (delegate* unmanaged<IntPtr, char*, int, int*, IntPtr*, int>)global::Interop.Kernel32.GetProcAddress(DirectoryContext.ADHandle, "DsGetDomainControllerInfoW");
if (dsGetDomainControllerInfo == null)
{
throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastPInvokeError());
}
fixed (char* domainName = Domain.Name)
{
// try DsDomainControllerInfoLevel3 first which supports Read only DC (RODC)
dcInfoLevel = NativeMethods.DsDomainControllerInfoLevel3;
result = dsGetDomainControllerInfo(_dsHandle, domainName, dcInfoLevel, &dcCount, &dcInfoPtr);
if (result != 0)
{
// fallback to DsDomainControllerInfoLevel2
dcInfoLevel = NativeMethods.DsDomainControllerInfoLevel2;
result = dsGetDomainControllerInfo(_dsHandle, domainName, dcInfoLevel, &dcCount, &dcInfoPtr);
}
}
if (result == 0)
{
try
{
IntPtr currentDc = dcInfoPtr;
for (int i = 0; i < dcCount; i++)
{
if (dcInfoLevel == NativeMethods.DsDomainControllerInfoLevel3)
{
DsDomainControllerInfo3 domainControllerInfo3 = new DsDomainControllerInfo3();
Marshal.PtrToStructure(currentDc, domainControllerInfo3);
// check if this is the same as "this" DC
if (domainControllerInfo3 != null)
{
if (Utils.Compare(domainControllerInfo3.dnsHostName, replicaName) == 0)
{
initialized = true;
// update all the fields
cachedSiteName = domainControllerInfo3.siteName;
cachedSiteObjectName = domainControllerInfo3.siteObjectName;
_cachedComputerObjectName = domainControllerInfo3.computerObjectName;
cachedServerObjectName = domainControllerInfo3.serverObjectName;
cachedNtdsaObjectName = domainControllerInfo3.ntdsaObjectName;
cachedNtdsaObjectGuid = domainControllerInfo3.ntdsDsaObjectGuid;
}
}
currentDc = IntPtr.Add(currentDc, Marshal.SizeOf(domainControllerInfo3));
}
else
{ //NativeMethods.DsDomainControllerInfoLevel2
DsDomainControllerInfo2 domainControllerInfo2 = new DsDomainControllerInfo2();
Marshal.PtrToStructure(currentDc, domainControllerInfo2);
// check if this is the same as "this" DC
if (domainControllerInfo2 != null)
{
if (Utils.Compare(domainControllerInfo2.dnsHostName, replicaName) == 0)
{
initialized = true;
// update all the fields
cachedSiteName = domainControllerInfo2.siteName;
cachedSiteObjectName = domainControllerInfo2.siteObjectName;
_cachedComputerObjectName = domainControllerInfo2.computerObjectName;
cachedServerObjectName = domainControllerInfo2.serverObjectName;
cachedNtdsaObjectName = domainControllerInfo2.ntdsaObjectName;
cachedNtdsaObjectGuid = domainControllerInfo2.ntdsDsaObjectGuid;
}
}
currentDc = IntPtr.Add(currentDc, Marshal.SizeOf(domainControllerInfo2));
}
}
}
finally
{
// free the domain controller info structure
if (dcInfoPtr != IntPtr.Zero)
{
// call DsFreeDomainControllerInfo
/*VOID DsFreeDomainControllerInfo(
DWORD InfoLevel,
DWORD cInfo,
VOID* pInfo
);*/
var dsFreeDomainControllerInfo = (delegate* unmanaged<int, int, IntPtr, void>)global::Interop.Kernel32.GetProcAddress(DirectoryContext.ADHandle, "DsFreeDomainControllerInfoW");
if (dsFreeDomainControllerInfo == null)
{
throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastPInvokeError());
}
dsFreeDomainControllerInfo(dcInfoLevel, dcCount, dcInfoPtr);
}
}
// if we couldn't get this DC's info, throw an exception
if (!initialized)
{
throw new ActiveDirectoryOperationException(SR.DCInfoNotFound);
}
_dcInfoInitialized = true;
siteInfoModified = false;
}
else
{
throw ExceptionHelper.GetExceptionFromErrorCode(result, Name);
}
}
internal void GetDSHandle()
{
if (_disposed)
{
// cannot bind to the domain controller as the object has been
// disposed (finalizer has been suppressed)
throw new ObjectDisposedException(GetType().Name);
}
// this part of the code needs to be synchronized
lock (this)
{
if (_dsHandle == IntPtr.Zero)
{
// get the credentials object
if (_authIdentity == IntPtr.Zero)
{
_authIdentity = Utils.GetAuthIdentity(context, DirectoryContext.ADHandle);
}
// DsBind
_dsHandle = Utils.GetDSHandle(replicaName, null, _authIdentity, DirectoryContext.ADHandle);
}
}
}
internal void FreeDSHandle()
{
lock (this)
{
// DsUnbind
Utils.FreeDSHandle(_dsHandle, DirectoryContext.ADHandle);
// free the credentials object
Utils.FreeAuthIdentity(_authIdentity, DirectoryContext.ADHandle);
}
}
internal ReplicationFailureCollection GetReplicationFailures(DS_REPL_INFO_TYPE type)
{
IntPtr info = (IntPtr)0;
bool advanced = true;
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
// get the handle
GetDSHandle();
info = GetReplicationInfoHelper(_dsHandle, (int)type, (int)type, null, ref advanced, 0, DirectoryContext.ADHandle);
return ConstructFailures(info, this, DirectoryContext.ADHandle);
}
private unsafe ArrayList GetRoles()
{
ArrayList roleList = new ArrayList();
int result = 0;
IntPtr rolesPtr = IntPtr.Zero;
GetDSHandle();
// Get the roles
// call DsListRoles
/*DWORD DsListRoles(
HANDLE hDs,
PDS_NAME_RESULTW* ppRoles
);*/
var dsListRoles = (delegate* unmanaged<IntPtr, IntPtr*, int>)global::Interop.Kernel32.GetProcAddress(DirectoryContext.ADHandle, "DsListRolesW");
if (dsListRoles == null)
{
throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastPInvokeError());
}
result = dsListRoles(_dsHandle, &rolesPtr);
if (result == 0)
{
try
{
DsNameResult dsNameResult = new DsNameResult();
Marshal.PtrToStructure(rolesPtr, dsNameResult);
IntPtr currentItem = dsNameResult.items;
for (int i = 0; i < dsNameResult.itemCount; i++)
{
DsNameResultItem dsNameResultItem = new DsNameResultItem();
Marshal.PtrToStructure(currentItem, dsNameResultItem);
// check if the role owner is this dc
if (dsNameResultItem.status == NativeMethods.DS_NAME_NO_ERROR)
{
if (dsNameResultItem.name!.Equals(NtdsaObjectName))
{
// add this role to the array
// the index of the item in the result signifies
// which role owner it is
roleList.Add((ActiveDirectoryRole)i);
}
}
currentItem = IntPtr.Add(currentItem, Marshal.SizeOf(dsNameResultItem));
}
}
finally
{
// free the DsNameResult structure
if (rolesPtr != IntPtr.Zero)
{
// call DsFreeNameResult
var dsFreeNameResult = (delegate* unmanaged<IntPtr, void>)global::Interop.Kernel32.GetProcAddress(DirectoryContext.ADHandle, "DsFreeNameResultW");
if (dsFreeNameResult == null)
{
throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastPInvokeError());
}
dsFreeNameResult(rolesPtr);
}
}
}
else
{
throw ExceptionHelper.GetExceptionFromErrorCode(result, Name);
}
return roleList;
}
private DateTime ParseDateTime(string dateTime)
{
int year = (int)int.Parse(dateTime.Substring(0, 4), NumberFormatInfo.InvariantInfo);
int month = (int)int.Parse(dateTime.Substring(4, 2), NumberFormatInfo.InvariantInfo);
int day = (int)int.Parse(dateTime.Substring(6, 2), NumberFormatInfo.InvariantInfo);
int hour = (int)int.Parse(dateTime.Substring(8, 2), NumberFormatInfo.InvariantInfo);
int min = (int)int.Parse(dateTime.Substring(10, 2), NumberFormatInfo.InvariantInfo);
int sec = (int)int.Parse(dateTime.Substring(12, 2), NumberFormatInfo.InvariantInfo);
// this is the UniversalTime
return new DateTime(year, month, day, hour, min, sec, 0);
}
private DirectorySearcher InternalGetDirectorySearcher()
{
DirectoryEntry de = new DirectoryEntry("LDAP://" + Name);
de.AuthenticationType = Utils.DefaultAuthType | AuthenticationTypes.ServerBind;
de.Username = context.UserName;
de.Password = context.Password;
return new DirectorySearcher(de);
}
#endregion private methods
}
}
|