1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Text; 6 using System.Collections; 7 using System.ComponentModel; 8 using System.Diagnostics; 9 using System.Runtime.InteropServices; 10 11 namespace System.DirectoryServices.ActiveDirectory 12 { 13 public class ConfigurationSet 14 { 15 // Private Variables 16 private readonly DirectoryContext _context = null; 17 private readonly DirectoryEntryManager _directoryEntryMgr = null; 18 private bool _disposed = false; 19 20 // variables corresponding to public properties 21 private readonly string _configSetName = null; 22 private ReadOnlySiteCollection _cachedSites = null; 23 private AdamInstanceCollection _cachedADAMInstances = null; 24 private ApplicationPartitionCollection _cachedApplicationPartitions = null; 25 private ActiveDirectorySchema _cachedSchema = null; 26 private AdamInstance _cachedSchemaRoleOwner = null; 27 private AdamInstance _cachedNamingRoleOwner = null; 28 private ReplicationSecurityLevel _cachedSecurityLevel = (ReplicationSecurityLevel)(-1); 29 30 // 4 minutes timeout for locating an ADAM instance in the configset 31 private static TimeSpan s_locationTimeout = new TimeSpan(0, 4, 0); 32 33 #region constructors ConfigurationSet(DirectoryContext context, string configSetName, DirectoryEntryManager directoryEntryMgr)34 internal ConfigurationSet(DirectoryContext context, string configSetName, DirectoryEntryManager directoryEntryMgr) 35 { 36 _context = context; 37 _configSetName = configSetName; 38 _directoryEntryMgr = directoryEntryMgr; 39 } 40 ConfigurationSet(DirectoryContext context, string configSetName)41 internal ConfigurationSet(DirectoryContext context, string configSetName) 42 : this(context, configSetName, new DirectoryEntryManager(context)) 43 { 44 } 45 #endregion constructors 46 47 #region IDisposable 48 Dispose()49 public void Dispose() => Dispose(true); 50 51 // private Dispose method Dispose(bool disposing)52 protected virtual void Dispose(bool disposing) 53 { 54 if (!_disposed) 55 { 56 // check if this is an explicit Dispose 57 // only then clean up the directory entries 58 if (disposing) 59 { 60 // dispose all directory entries 61 foreach (DirectoryEntry entry in _directoryEntryMgr.GetCachedDirectoryEntries()) 62 { 63 entry.Dispose(); 64 } 65 } 66 _disposed = true; 67 } 68 } 69 #endregion IDisposable 70 71 #region public methods 72 GetConfigurationSet(DirectoryContext context)73 public static ConfigurationSet GetConfigurationSet(DirectoryContext context) 74 { 75 // check that the argument is not null 76 if (context == null) 77 throw new ArgumentNullException("context"); 78 79 // target should ConfigurationSet or DirectoryServer 80 if ((context.ContextType != DirectoryContextType.ConfigurationSet) && 81 (context.ContextType != DirectoryContextType.DirectoryServer)) 82 { 83 throw new ArgumentException(SR.TargetShouldBeServerORConfigSet, "context"); 84 } 85 86 // target should be an adam config set or server 87 if (((!context.isServer()) && (!context.isADAMConfigSet()))) 88 { 89 // the target should be a server or an ADAM Config Set 90 if (context.ContextType == DirectoryContextType.ConfigurationSet) 91 { 92 throw new ActiveDirectoryObjectNotFoundException(SR.ConfigSetNotFound, typeof(ConfigurationSet), context.Name); 93 } 94 else 95 { 96 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.AINotFound , context.Name), typeof(ConfigurationSet), null); 97 } 98 } 99 100 // work with copy of the context 101 context = new DirectoryContext(context); 102 103 // 104 // bind to rootdse of an adam instance (if target is already a server, verify that it is an adam instance) 105 // 106 DirectoryEntryManager directoryEntryMgr = new DirectoryEntryManager(context); 107 DirectoryEntry rootDSE = null; 108 string configSetName = null; 109 110 try 111 { 112 rootDSE = directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE); 113 if ((context.isServer()) && (!Utils.CheckCapability(rootDSE, Capability.ActiveDirectoryApplicationMode))) 114 { 115 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.AINotFound , context.Name), typeof(ConfigurationSet), null); 116 } 117 118 configSetName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.ConfigurationNamingContext); 119 } 120 catch (COMException e) 121 { 122 int errorCode = e.ErrorCode; 123 124 if (errorCode == unchecked((int)0x8007203a)) 125 { 126 if (context.ContextType == DirectoryContextType.ConfigurationSet) 127 { 128 throw new ActiveDirectoryObjectNotFoundException(SR.ConfigSetNotFound, typeof(ConfigurationSet), context.Name); 129 } 130 else 131 { 132 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.AINotFound , context.Name), typeof(ConfigurationSet), null); 133 } 134 } 135 else 136 { 137 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 138 } 139 } 140 catch (ActiveDirectoryObjectNotFoundException) 141 { 142 if (context.ContextType == DirectoryContextType.ConfigurationSet) 143 { 144 // this is the case when we could not find an ADAM instance in that config set 145 throw new ActiveDirectoryObjectNotFoundException(SR.ConfigSetNotFound, typeof(ConfigurationSet), context.Name); 146 } 147 else 148 throw; 149 } 150 151 // return config set object 152 return new ConfigurationSet(context, configSetName, directoryEntryMgr); 153 } 154 FindAdamInstance()155 public AdamInstance FindAdamInstance() 156 { 157 CheckIfDisposed(); 158 return FindOneAdamInstance(Name, _context, null, null); 159 } 160 FindAdamInstance(string partitionName)161 public AdamInstance FindAdamInstance(string partitionName) 162 { 163 CheckIfDisposed(); 164 165 if (partitionName == null) 166 { 167 throw new ArgumentNullException("partitionName"); 168 } 169 170 return FindOneAdamInstance(Name, _context, partitionName, null); 171 } 172 FindAdamInstance(string partitionName, string siteName)173 public AdamInstance FindAdamInstance(string partitionName, string siteName) 174 { 175 CheckIfDisposed(); 176 177 // 178 // null partitionName would signify that we don't care about the partition 179 // 180 181 if (siteName == null) 182 { 183 throw new ArgumentNullException("siteName"); 184 } 185 186 return FindOneAdamInstance(Name, _context, partitionName, siteName); 187 } 188 FindAllAdamInstances()189 public AdamInstanceCollection FindAllAdamInstances() 190 { 191 CheckIfDisposed(); 192 193 return FindAdamInstances(_context, null, null); 194 } 195 FindAllAdamInstances(string partitionName)196 public AdamInstanceCollection FindAllAdamInstances(string partitionName) 197 { 198 CheckIfDisposed(); 199 200 if (partitionName == null) 201 { 202 throw new ArgumentNullException("partitionName"); 203 } 204 205 return FindAdamInstances(_context, partitionName, null); 206 } 207 FindAllAdamInstances(string partitionName, string siteName)208 public AdamInstanceCollection FindAllAdamInstances(string partitionName, string siteName) 209 { 210 CheckIfDisposed(); 211 212 // 213 // null partitionName would signify that we don't care about the partition 214 // 215 216 if (siteName == null) 217 { 218 throw new ArgumentNullException("siteName"); 219 } 220 221 return FindAdamInstances(_context, partitionName, siteName); 222 } 223 GetDirectoryEntry()224 public DirectoryEntry GetDirectoryEntry() 225 { 226 CheckIfDisposed(); 227 return DirectoryEntryManager.GetDirectoryEntry(_context, WellKnownDN.ConfigurationNamingContext); 228 } 229 GetSecurityLevel()230 public ReplicationSecurityLevel GetSecurityLevel() 231 { 232 CheckIfDisposed(); 233 if (_cachedSecurityLevel == (ReplicationSecurityLevel)(-1)) 234 { 235 DirectoryEntry configEntry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.ConfigurationNamingContext); 236 _cachedSecurityLevel = (ReplicationSecurityLevel)((int)PropertyManager.GetPropertyValue(_context, configEntry, PropertyManager.MsDSReplAuthenticationMode)); 237 } 238 return _cachedSecurityLevel; 239 } 240 SetSecurityLevel(ReplicationSecurityLevel securityLevel)241 public void SetSecurityLevel(ReplicationSecurityLevel securityLevel) 242 { 243 CheckIfDisposed(); 244 if (securityLevel < ReplicationSecurityLevel.NegotiatePassThrough || securityLevel > ReplicationSecurityLevel.MutualAuthentication) 245 { 246 throw new InvalidEnumArgumentException("securityLevel", (int)securityLevel, typeof(ReplicationSecurityLevel)); 247 } 248 249 try 250 { 251 DirectoryEntry configEntry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.ConfigurationNamingContext); 252 configEntry.Properties[PropertyManager.MsDSReplAuthenticationMode].Value = (int)securityLevel; 253 configEntry.CommitChanges(); 254 } 255 catch (COMException e) 256 { 257 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 258 } 259 260 // invalidate the cached entry 261 _cachedSecurityLevel = (ReplicationSecurityLevel)(-1); 262 } 263 ToString()264 public override string ToString() => Name; 265 266 #endregion public methods 267 268 #region public properties 269 270 public string Name 271 { 272 get 273 { 274 CheckIfDisposed(); 275 return _configSetName; 276 } 277 } 278 279 public ReadOnlySiteCollection Sites 280 { 281 get 282 { 283 CheckIfDisposed(); 284 if (_cachedSites == null) 285 { 286 _cachedSites = new ReadOnlySiteCollection(GetSites()); 287 } 288 return _cachedSites; 289 } 290 } 291 292 public AdamInstanceCollection AdamInstances 293 { 294 get 295 { 296 CheckIfDisposed(); 297 if (_cachedADAMInstances == null) 298 { 299 _cachedADAMInstances = FindAllAdamInstances(); 300 } 301 return _cachedADAMInstances; 302 } 303 } 304 305 public ApplicationPartitionCollection ApplicationPartitions 306 { 307 get 308 { 309 CheckIfDisposed(); 310 if (_cachedApplicationPartitions == null) 311 { 312 _cachedApplicationPartitions = new ApplicationPartitionCollection(GetApplicationPartitions()); 313 } 314 return _cachedApplicationPartitions; 315 } 316 } 317 318 public ActiveDirectorySchema Schema 319 { 320 get 321 { 322 CheckIfDisposed(); 323 if (_cachedSchema == null) 324 { 325 try 326 { 327 _cachedSchema = new ActiveDirectorySchema(_context, _directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.SchemaNamingContext)); 328 } 329 catch (COMException e) 330 { 331 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 332 } 333 } 334 return _cachedSchema; 335 } 336 } 337 338 public AdamInstance SchemaRoleOwner 339 { 340 get 341 { 342 CheckIfDisposed(); 343 if (_cachedSchemaRoleOwner == null) 344 { 345 _cachedSchemaRoleOwner = GetRoleOwner(AdamRole.SchemaRole); 346 } 347 return _cachedSchemaRoleOwner; 348 } 349 } 350 351 public AdamInstance NamingRoleOwner 352 { 353 get 354 { 355 CheckIfDisposed(); 356 if (_cachedNamingRoleOwner == null) 357 { 358 _cachedNamingRoleOwner = GetRoleOwner(AdamRole.NamingRole); 359 } 360 return _cachedNamingRoleOwner; 361 } 362 } 363 364 #endregion public properties 365 366 #region private methods 367 GetSearchRootEntry(Forest forest)368 private static DirectoryEntry GetSearchRootEntry(Forest forest) 369 { 370 DirectoryEntry rootEntry; 371 DirectoryContext forestContext = forest.GetDirectoryContext(); 372 bool isServer = false; 373 bool isGC = false; 374 AuthenticationTypes authType = Utils.DefaultAuthType; 375 376 if (forestContext.ContextType == DirectoryContextType.DirectoryServer) 377 { 378 // 379 // the forest object was created by specifying a server name 380 // so we will stick to that server for the search. We need to determine 381 // whether or not the server is a DC or GC 382 // 383 isServer = true; 384 DirectoryEntry rootDSE = DirectoryEntryManager.GetDirectoryEntry(forestContext, WellKnownDN.RootDSE); 385 string isGCReady = (string)PropertyManager.GetPropertyValue(forestContext, rootDSE, PropertyManager.IsGlobalCatalogReady); 386 isGC = (Utils.Compare(isGCReady, "TRUE") == 0); 387 } 388 389 if (isServer) 390 { 391 authType |= AuthenticationTypes.ServerBind; 392 393 if (isGC) 394 { 395 rootEntry = new DirectoryEntry("GC://" + forestContext.GetServerName(), forestContext.UserName, forestContext.Password, authType); 396 } 397 else 398 { 399 rootEntry = new DirectoryEntry("LDAP://" + forestContext.GetServerName(), forestContext.UserName, forestContext.Password, authType); 400 } 401 } 402 else 403 { 404 // need to find any GC in the forest 405 rootEntry = new DirectoryEntry("GC://" + forest.Name, forestContext.UserName, forestContext.Password, authType); 406 } 407 408 return rootEntry; 409 } 410 FindAnyAdamInstance(DirectoryContext context)411 internal static AdamInstance FindAnyAdamInstance(DirectoryContext context) 412 { 413 if (context.ContextType != DirectoryContextType.ConfigurationSet) 414 { 415 // assuming it's an ADAM Instance 416 // check that it is an ADAM server only (not AD) 417 DirectoryEntryManager directoryEntryMgr = new DirectoryEntryManager(context); 418 DirectoryEntry rootDSE = directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE); 419 420 if (!Utils.CheckCapability(rootDSE, Capability.ActiveDirectoryApplicationMode)) 421 { 422 directoryEntryMgr.RemoveIfExists(directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.RootDSE)); 423 throw new ArgumentException(SR.TargetShouldBeServerORConfigSet, "context"); 424 } 425 426 string dnsHostName = (string)PropertyManager.GetPropertyValue(context, rootDSE, PropertyManager.DnsHostName); 427 428 return new AdamInstance(context, dnsHostName, directoryEntryMgr); 429 } 430 431 // Now this is the case where context is a Config Set 432 // Here we need to search for the service connection points in the forest 433 // (if the forest object was created by specifying the server, we stick to that, else search in a GC) 434 DirectoryEntry rootEntry = GetSearchRootEntry(Forest.GetCurrentForest()); 435 ArrayList adamInstanceNames = new ArrayList(); 436 437 try 438 { 439 string entryName = (string)rootEntry.Properties["distinguishedName"].Value; 440 441 // Search for computer "serviceConnectionObjects" where the keywords attribute 442 // contains the specified keyword 443 // set up the searcher object 444 445 // build the filter 446 StringBuilder str = new StringBuilder(15); 447 str.Append("(&("); 448 str.Append(PropertyManager.ObjectCategory); 449 str.Append("=serviceConnectionPoint)"); 450 str.Append("("); 451 str.Append(PropertyManager.Keywords); 452 str.Append("=1.2.840.113556.1.4.1851)("); 453 str.Append(PropertyManager.Keywords); 454 str.Append("="); 455 str.Append(Utils.GetEscapedFilterValue(context.Name)); // target = config set name 456 str.Append("))"); 457 458 string filter = str.ToString(); 459 string[] propertiesToLoad = new string[1]; 460 461 propertiesToLoad[0] = PropertyManager.ServiceBindingInformation; 462 463 ADSearcher searcher = new ADSearcher(rootEntry, filter, propertiesToLoad, SearchScope.Subtree, false /*not paged search*/, false /*no cached results*/); 464 SearchResultCollection resCol = searcher.FindAll(); 465 466 try 467 { 468 foreach (SearchResult res in resCol) 469 { 470 // the binding info contains two values 471 // "ldap://hostname:ldapport" 472 // and "ldaps://hostname:sslport" 473 // we need the "hostname:ldapport" value 474 string prefix = "ldap://"; 475 476 foreach (string bindingInfo in res.Properties[PropertyManager.ServiceBindingInformation]) 477 { 478 if ((bindingInfo.Length > prefix.Length) && (String.Compare(bindingInfo.Substring(0, prefix.Length), prefix, StringComparison.OrdinalIgnoreCase) == 0)) 479 { 480 adamInstanceNames.Add(bindingInfo.Substring(prefix.Length)); 481 } 482 } 483 } 484 } 485 finally 486 { 487 resCol.Dispose(); 488 } 489 } 490 catch (COMException e) 491 { 492 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 493 } 494 finally 495 { 496 rootEntry.Dispose(); 497 } 498 499 // 500 // we have all the adam instance names in teh form of server:port from the scp 501 // now we need to find one that is alive 502 // 503 return FindAliveAdamInstance(null, context, adamInstanceNames); 504 } 505 FindOneAdamInstance(DirectoryContext context, string partitionName, string siteName)506 internal static AdamInstance FindOneAdamInstance(DirectoryContext context, string partitionName, string siteName) 507 { 508 return FindOneAdamInstance(null, context, partitionName, siteName); 509 } 510 FindOneAdamInstance(string configSetName, DirectoryContext context, string partitionName, string siteName)511 internal static AdamInstance FindOneAdamInstance(string configSetName, DirectoryContext context, string partitionName, string siteName) 512 { 513 // can expect valid context (non-null) 514 if (partitionName != null && partitionName.Length == 0) 515 { 516 throw new ArgumentException(SR.EmptyStringParameter, "partitionName"); 517 } 518 519 if (siteName != null && siteName.Length == 0) 520 { 521 throw new ArgumentException(SR.EmptyStringParameter, "siteName"); 522 } 523 524 ArrayList ntdsaNames = Utils.GetReplicaList(context, partitionName, siteName, false /* isDefaultNC */, true /* isADAM */, false /* mustBeGC */); 525 526 if (ntdsaNames.Count < 1) 527 { 528 throw new ActiveDirectoryObjectNotFoundException(SR.ADAMInstanceNotFound, typeof(AdamInstance), null); 529 } 530 531 return FindAliveAdamInstance(configSetName, context, ntdsaNames); 532 } 533 FindAdamInstances(DirectoryContext context, string partitionName, string siteName)534 internal static AdamInstanceCollection FindAdamInstances(DirectoryContext context, string partitionName, string siteName) 535 { 536 // can expect valid context (non-null) 537 if (partitionName != null && partitionName.Length == 0) 538 { 539 throw new ArgumentException(SR.EmptyStringParameter, "partitionName"); 540 } 541 542 if (siteName != null && siteName.Length == 0) 543 { 544 throw new ArgumentException(SR.EmptyStringParameter, "siteName"); 545 } 546 547 ArrayList adamInstanceList = new ArrayList(); 548 549 foreach (string adamInstanceName in Utils.GetReplicaList(context, partitionName, siteName, false /* isDefaultNC */, true /* isADAM */, false /* mustBeGC */)) 550 { 551 DirectoryContext adamInstContext = Utils.GetNewDirectoryContext(adamInstanceName, DirectoryContextType.DirectoryServer, context); 552 adamInstanceList.Add(new AdamInstance(adamInstContext, adamInstanceName)); 553 } 554 555 return new AdamInstanceCollection(adamInstanceList); 556 } 557 558 // 559 // The input to this function is a list of adam instance names in the form server:port 560 // This function tries to bind to each of the instances in this list sequentially until one of the following occurs: 561 // 1. An ADAM instance responds to an ldap_bind - we return an ADAMInstance object for that adam instance 562 // 2. We exceed the timeout duration - we return an ActiveDirectoryObjectNotFoundException 563 // FindAliveAdamInstance(string configSetName, DirectoryContext context, ArrayList adamInstanceNames)564 internal static AdamInstance FindAliveAdamInstance(string configSetName, DirectoryContext context, ArrayList adamInstanceNames) 565 { 566 bool foundAliveADAMInstance = false; 567 AdamInstance adamInstance = null; 568 569 // record the start time so that we can determine if the timeout duration has been exceeded or not 570 DateTime startTime = DateTime.UtcNow; 571 572 // loop through each adam instance and try to bind to the rootdse 573 foreach (string adamInstanceName in adamInstanceNames) 574 { 575 DirectoryContext adamInstContext = Utils.GetNewDirectoryContext(adamInstanceName, DirectoryContextType.DirectoryServer, context); 576 DirectoryEntryManager directoryEntryMgr = new DirectoryEntryManager(adamInstContext); 577 DirectoryEntry tempRootEntry = directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE); 578 579 try 580 { 581 tempRootEntry.Bind(true); 582 adamInstance = new AdamInstance(adamInstContext, adamInstanceName, directoryEntryMgr, true /* nameIncludesPort */); 583 foundAliveADAMInstance = true; 584 } 585 catch (COMException e) 586 { 587 // if this is server down /server busy / server unavailable / timeout exception we should just eat this up and try the next one 588 if ((e.ErrorCode == unchecked((int)0x8007203a)) || 589 (e.ErrorCode == unchecked((int)0x8007200e)) || 590 (e.ErrorCode == unchecked((int)0x8007200f)) || 591 (e.ErrorCode == unchecked((int)0x800705b4))) 592 { 593 // if we are passed the timeout period, we should throw, else do nothing 594 if (DateTime.UtcNow.Subtract(startTime) > s_locationTimeout) 595 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.ADAMInstanceNotFoundInConfigSet , (configSetName != null) ? configSetName : context.Name), typeof(AdamInstance), null); 596 } 597 else 598 throw ExceptionHelper.GetExceptionFromCOMException(context, e); 599 } 600 601 if (foundAliveADAMInstance) 602 { 603 return adamInstance; 604 } 605 } 606 607 // if we reach here, we haven't found an adam instance 608 throw new ActiveDirectoryObjectNotFoundException(SR.Format(SR.ADAMInstanceNotFoundInConfigSet , (configSetName != null) ? configSetName : context.Name), typeof(AdamInstance), null); 609 } 610 611 /// <returns>Returns a DomainController object for the DC that holds the specified FSMO role</returns> GetRoleOwner(AdamRole role)612 private AdamInstance GetRoleOwner(AdamRole role) 613 { 614 DirectoryEntry entry = null; 615 616 string adamInstName = null; 617 try 618 { 619 switch (role) 620 { 621 case AdamRole.SchemaRole: 622 { 623 entry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.SchemaNamingContext); 624 break; 625 } 626 627 case AdamRole.NamingRole: 628 { 629 entry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.PartitionsContainer); 630 break; 631 } 632 633 default: 634 // should not happen since we are calling this only internally 635 Debug.Assert(false, "ConfigurationSet.GetRoleOwner: Invalid role type."); 636 break; 637 } 638 entry.RefreshCache(); 639 adamInstName = Utils.GetAdamDnsHostNameFromNTDSA(_context, (string)PropertyManager.GetPropertyValue(_context, entry, PropertyManager.FsmoRoleOwner)); 640 } 641 catch (COMException e) 642 { 643 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 644 } 645 finally 646 { 647 if (entry != null) 648 { 649 entry.Dispose(); 650 } 651 } 652 653 // create a new context object for the adam instance passing on the 654 // credentials from the context 655 DirectoryContext adamInstContext = Utils.GetNewDirectoryContext(adamInstName, DirectoryContextType.DirectoryServer, _context); 656 657 return new AdamInstance(adamInstContext, adamInstName); 658 } 659 GetSites()660 private ArrayList GetSites() 661 { 662 ArrayList sites = new ArrayList(); 663 DirectoryEntry sitesEntry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.SitesContainer); 664 665 // search for all the "site" objects 666 // (one-level search is good enough) 667 // setup the directory searcher object 668 string filter = "(" + PropertyManager.ObjectCategory + "=site)"; 669 string[] propertiesToLoad = new string[1]; 670 671 propertiesToLoad[0] = PropertyManager.Cn; 672 673 ADSearcher searcher = new ADSearcher(sitesEntry, filter, propertiesToLoad, SearchScope.OneLevel); 674 SearchResultCollection resCol = null; 675 676 try 677 { 678 resCol = searcher.FindAll(); 679 680 foreach (SearchResult res in resCol) 681 { 682 // an existing site 683 sites.Add(new ActiveDirectorySite(_context, (string)PropertyManager.GetSearchResultPropertyValue(res, PropertyManager.Cn), true)); 684 } 685 } 686 catch (COMException e) 687 { 688 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 689 } 690 finally 691 { 692 if (resCol != null) 693 { 694 // call dispose on search result collection 695 resCol.Dispose(); 696 } 697 } 698 return sites; 699 } 700 GetApplicationPartitions()701 private ArrayList GetApplicationPartitions() 702 { 703 ArrayList appNCs = new ArrayList(); 704 DirectoryEntry rootDSE = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.RootDSE); 705 DirectoryEntry partitionsEntry = _directoryEntryMgr.GetCachedDirectoryEntry(WellKnownDN.PartitionsContainer); 706 707 // search for all the "crossRef" objects that have the 708 // ADS_SYSTEMFLAG_CR_NTDS_NC set and the SYSTEMFLAG_CR_NTDS_DOMAIN flag not set 709 // (one-level search is good enough) 710 // setup the directory searcher object 711 712 // build the filter 713 StringBuilder str = new StringBuilder(100); 714 str.Append("(&("); 715 str.Append(PropertyManager.ObjectCategory); 716 str.Append("=crossRef)("); 717 str.Append(PropertyManager.SystemFlags); 718 str.Append(":1.2.840.113556.1.4.804:="); 719 str.Append((int)SystemFlag.SystemFlagNtdsNC); 720 str.Append(")(!("); 721 str.Append(PropertyManager.SystemFlags); 722 str.Append(":1.2.840.113556.1.4.803:="); 723 str.Append((int)SystemFlag.SystemFlagNtdsDomain); 724 str.Append(")))"); 725 726 string filter = str.ToString(); 727 string[] propertiesToLoad = new string[2]; 728 propertiesToLoad[0] = PropertyManager.NCName; 729 propertiesToLoad[1] = PropertyManager.MsDSNCReplicaLocations; 730 731 ADSearcher searcher = new ADSearcher(partitionsEntry, filter, propertiesToLoad, SearchScope.OneLevel); 732 SearchResultCollection resCol = null; 733 734 try 735 { 736 resCol = searcher.FindAll(); 737 738 string schemaNamingContext = (string)PropertyManager.GetPropertyValue(_context, rootDSE, PropertyManager.SchemaNamingContext); 739 string configurationNamingContext = (string)PropertyManager.GetPropertyValue(_context, rootDSE, PropertyManager.ConfigurationNamingContext); 740 741 foreach (SearchResult res in resCol) 742 { 743 // add the name of the appNC only if it is not 744 // the Schema or Configuration partition 745 string nCName = (string)PropertyManager.GetSearchResultPropertyValue(res, PropertyManager.NCName); 746 747 if ((!(nCName.Equals(schemaNamingContext))) && (!(nCName.Equals(configurationNamingContext)))) 748 { 749 ResultPropertyValueCollection replicaLocations = res.Properties[PropertyManager.MsDSNCReplicaLocations]; 750 if (replicaLocations.Count > 0) 751 { 752 string replicaName = Utils.GetAdamDnsHostNameFromNTDSA(_context, (string)replicaLocations[Utils.GetRandomIndex(replicaLocations.Count)]); 753 DirectoryContext appNCContext = Utils.GetNewDirectoryContext(replicaName, DirectoryContextType.DirectoryServer, _context); 754 appNCs.Add(new ApplicationPartition(appNCContext, nCName, null, ApplicationPartitionType.ADAMApplicationPartition, new DirectoryEntryManager(appNCContext))); 755 } 756 } 757 } 758 } 759 catch (COMException e) 760 { 761 throw ExceptionHelper.GetExceptionFromCOMException(_context, e); 762 } 763 finally 764 { 765 if (resCol != null) 766 { 767 // call dispose on search result collection 768 resCol.Dispose(); 769 } 770 } 771 return appNCs; 772 } 773 CheckIfDisposed()774 private void CheckIfDisposed() 775 { 776 if (_disposed) 777 { 778 throw new ObjectDisposedException(GetType().Name); 779 } 780 } 781 #endregion private methods 782 } 783 } 784