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.Collections;
6 using System.Globalization;
7 using System.Runtime.InteropServices;
8 using System.Diagnostics;
9 
10 namespace System.DirectoryServices.ActiveDirectory
11 {
12     public abstract class DirectoryServer : IDisposable
13     {
14         private bool _disposed = false;
15         internal DirectoryContext context = null;
16         internal string replicaName = null;
17         internal DirectoryEntryManager directoryEntryMgr = null;
18 
19         // internal variables for the public properties
20         internal bool siteInfoModified = false;
21         internal string cachedSiteName = null;
22         internal string cachedSiteObjectName = null;
23         internal string cachedServerObjectName = null;
24         internal string cachedNtdsaObjectName = null;
25         internal Guid cachedNtdsaObjectGuid = Guid.Empty;
26         // disable csharp compiler warning #0414: field assigned unused value
27 #pragma warning disable 0414
28         internal string cachedIPAddress = null;
29 #pragma warning restore 0414
30         internal ReadOnlyStringCollection cachedPartitions = null;
31 
32         internal const int DS_REPSYNC_ASYNCHRONOUS_OPERATION = 0x00000001;
33         internal const int DS_REPSYNC_ALL_SOURCES = 0x00000010;
34         internal const int DS_REPSYNCALL_ID_SERVERS_BY_DN = 0x00000004;
35         internal const int DS_REPL_NOTSUPPORTED = 50;
36         private const int DS_REPL_INFO_FLAG_IMPROVE_LINKED_ATTRS = 0x00000001;
37         private ReplicationConnectionCollection _inbound = null;
38         private ReplicationConnectionCollection _outbound = null;
39 
40         #region constructors
DirectoryServer()41         protected DirectoryServer()
42         {
43         }
44         #endregion constructors
45 
46         #region IDisposable
~DirectoryServer()47         ~DirectoryServer() => Dispose(false);
48 
Dispose()49         public void Dispose()
50         {
51             Dispose(true);
52 
53             // Take yourself off of the Finalization queue
54             // to prevent finalization code for this object
55             // from executing a second time.
56             GC.SuppressFinalize(this);
57         }
58 
59         // private Dispose method
Dispose(bool disposing)60         protected virtual void Dispose(bool disposing)
61         {
62             if (!_disposed)
63             {
64                 // check if this is an explicit Dispose
65                 // only then clean up the directory entries
66                 if (disposing)
67                 {
68                     // dispose all directory entries
69                     foreach (DirectoryEntry entry in directoryEntryMgr.GetCachedDirectoryEntries())
70                     {
71                         entry.Dispose();
72                     }
73                 }
74                 _disposed = true;
75             }
76         }
77         #endregion IDisposable
78 
79         #region public methods
ToString()80         public override string ToString() => Name;
81 
MoveToAnotherSite(string siteName)82         public void MoveToAnotherSite(string siteName)
83         {
84             CheckIfDisposed();
85 
86             // validate siteName
87             if (siteName == null)
88             {
89                 throw new ArgumentNullException("siteName");
90             }
91 
92             if (siteName.Length == 0)
93             {
94                 throw new ArgumentException(SR.EmptyStringParameter, "siteName");
95             }
96 
97             // the dc is really being moved to a different site
98             if (Utils.Compare(SiteName, siteName) != 0)
99             {
100                 DirectoryEntry newParentEntry = null;
101                 try
102                 {
103                     // Bind to the target site's server container
104                     // Get the distinguished name for the site
105                     string parentDN = "CN=Servers,CN=" + siteName + "," + directoryEntryMgr.ExpandWellKnownDN(WellKnownDN.SitesContainer);
106                     newParentEntry = DirectoryEntryManager.GetDirectoryEntry(context, parentDN);
107 
108                     string serverName = (this is DomainController) ? ((DomainController)this).ServerObjectName : ((AdamInstance)this).ServerObjectName;
109 
110                     DirectoryEntry serverEntry = directoryEntryMgr.GetCachedDirectoryEntry(serverName);
111 
112                     // force binding (needed otherwise S.DS throw an exception while releasing the COM interface pointer)
113                     string dn = (string)PropertyManager.GetPropertyValue(context, serverEntry, PropertyManager.DistinguishedName);
114 
115                     // move the object to the servers container of the target site
116                     serverEntry.MoveTo(newParentEntry);
117                 }
118                 catch (COMException e)
119                 {
120                     throw ExceptionHelper.GetExceptionFromCOMException(context, e);
121                 }
122                 finally
123                 {
124                     if (newParentEntry != null)
125                     {
126                         newParentEntry.Dispose();
127                     }
128                 }
129 
130                 // remove stale cached directory entries
131                 // invalidate the cached properties that get affected by this
132                 siteInfoModified = true;
133                 cachedSiteName = null;
134 
135                 if (cachedSiteObjectName != null)
136                 {
137                     directoryEntryMgr.RemoveIfExists(cachedSiteObjectName);
138                     cachedSiteObjectName = null;
139                 }
140 
141                 if (cachedServerObjectName != null)
142                 {
143                     directoryEntryMgr.RemoveIfExists(cachedServerObjectName);
144                     cachedServerObjectName = null;
145                 }
146 
147                 if (cachedNtdsaObjectName != null)
148                 {
149                     directoryEntryMgr.RemoveIfExists(cachedNtdsaObjectName);
150                     cachedNtdsaObjectName = null;
151                 }
152             }
153         }
154 
GetDirectoryEntry()155         public DirectoryEntry GetDirectoryEntry()
156         {
157             CheckIfDisposed();
158             string serverName = (this is DomainController) ? ((DomainController)this).ServerObjectName : ((AdamInstance)this).ServerObjectName;
159             return DirectoryEntryManager.GetDirectoryEntry(context, serverName);
160         }
161 
162         #endregion public methods
163 
164         #region abstract public methods
165 
CheckReplicationConsistency()166         public abstract void CheckReplicationConsistency();
167 
GetReplicationCursors(string partition)168         public abstract ReplicationCursorCollection GetReplicationCursors(string partition);
169 
GetReplicationOperationInformation()170         public abstract ReplicationOperationInformation GetReplicationOperationInformation();
171 
GetReplicationNeighbors(string partition)172         public abstract ReplicationNeighborCollection GetReplicationNeighbors(string partition);
173 
GetAllReplicationNeighbors()174         public abstract ReplicationNeighborCollection GetAllReplicationNeighbors();
175 
GetReplicationConnectionFailures()176         public abstract ReplicationFailureCollection GetReplicationConnectionFailures();
177 
GetReplicationMetadata(string objectPath)178         public abstract ActiveDirectoryReplicationMetadata GetReplicationMetadata(string objectPath);
179 
SyncReplicaFromServer(string partition, string sourceServer)180         public abstract void SyncReplicaFromServer(string partition, string sourceServer);
181 
TriggerSyncReplicaFromNeighbors(string partition)182         public abstract void TriggerSyncReplicaFromNeighbors(string partition);
183 
SyncReplicaFromAllServers(string partition, SyncFromAllServersOptions options)184         public abstract void SyncReplicaFromAllServers(string partition, SyncFromAllServersOptions options);
185 
186         #endregion abstract public methods
187 
188         #region public properties
189 
190         public string Name
191         {
192             get
193             {
194                 CheckIfDisposed();
195                 return replicaName;
196             }
197         }
198 
199         public ReadOnlyStringCollection Partitions
200         {
201             get
202             {
203                 CheckIfDisposed();
204                 if (cachedPartitions == null)
205                 {
206                     cachedPartitions = new ReadOnlyStringCollection(GetPartitions());
207                 }
208                 return cachedPartitions;
209             }
210         }
211 
212         #endregion public properties
213 
214         #region abstract public properties
215 
216         public abstract string IPAddress { get; }
217 
218         public abstract string SiteName { get; }
219 
220         public abstract SyncUpdateCallback SyncFromAllServersCallback { get; set; }
221 
222         public abstract ReplicationConnectionCollection InboundConnections { get; }
223 
224         public abstract ReplicationConnectionCollection OutboundConnections { get; }
225 
226         #endregion abstract public properties
227 
228         #region private methods
229 
GetPartitions()230         internal ArrayList GetPartitions()
231         {
232             ArrayList partitionList = new ArrayList();
233             DirectoryEntry rootDSE = null;
234             DirectoryEntry serverNtdsaEntry = null;
235 
236             try
237             {
238                 // get the writable partitions
239                 rootDSE = DirectoryEntryManager.GetDirectoryEntry(context, WellKnownDN.RootDSE);
240                 // don't need range retrieval for root dse attributes
241                 foreach (string partition in rootDSE.Properties[PropertyManager.NamingContexts])
242                 {
243                     partitionList.Add(partition);
244                 }
245 
246                 // also the read only partitions
247                 string ntdsaName = (this is DomainController) ? ((DomainController)this).NtdsaObjectName : ((AdamInstance)this).NtdsaObjectName;
248                 serverNtdsaEntry = DirectoryEntryManager.GetDirectoryEntry(context, ntdsaName);
249 
250                 // use range retrieval
251                 ArrayList propertyNames = new ArrayList();
252                 propertyNames.Add(PropertyManager.HasPartialReplicaNCs);
253 
254                 Hashtable values = null;
255                 try
256                 {
257                     values = Utils.GetValuesWithRangeRetrieval(serverNtdsaEntry, null, propertyNames, SearchScope.Base);
258                 }
259                 catch (COMException e)
260                 {
261                     throw ExceptionHelper.GetExceptionFromCOMException(context, e);
262                 }
263 
264                 ArrayList readOnlyPartitions = (ArrayList)values[PropertyManager.HasPartialReplicaNCs.ToLower(CultureInfo.InvariantCulture)];
265 
266                 Debug.Assert(readOnlyPartitions != null);
267                 foreach (string readOnlyPartition in readOnlyPartitions)
268                 {
269                     partitionList.Add(readOnlyPartition);
270                 }
271             }
272             catch (COMException e)
273             {
274                 throw ExceptionHelper.GetExceptionFromCOMException(context, e);
275             }
276             finally
277             {
278                 if (rootDSE != null)
279                 {
280                     rootDSE.Dispose();
281                 }
282 
283                 if (serverNtdsaEntry != null)
284                 {
285                     serverNtdsaEntry.Dispose();
286                 }
287             }
288             return partitionList;
289         }
290 
CheckIfDisposed()291         internal void CheckIfDisposed()
292         {
293             if (_disposed)
294             {
295                 throw new ObjectDisposedException(GetType().Name);
296             }
297         }
298 
299         internal DirectoryContext Context => context;
300 
CheckConsistencyHelper(IntPtr dsHandle, LoadLibrarySafeHandle libHandle)301         internal void CheckConsistencyHelper(IntPtr dsHandle, LoadLibrarySafeHandle libHandle)
302         {
303             // call DsReplicaConsistencyCheck
304             IntPtr functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaConsistencyCheck");
305             if (functionPtr == (IntPtr)0)
306             {
307                 throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
308             }
309             UnsafeNativeMethods.DsReplicaConsistencyCheck replicaConsistencyCheck = (UnsafeNativeMethods.DsReplicaConsistencyCheck)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaConsistencyCheck));
310 
311             int result = replicaConsistencyCheck(dsHandle, 0, 0);
312 
313             if (result != 0)
314                 throw ExceptionHelper.GetExceptionFromErrorCode(result, Name);
315         }
316 
GetReplicationInfoHelper(IntPtr dsHandle, int type, int secondaryType, string partition, ref bool advanced, int context, LoadLibrarySafeHandle libHandle)317         internal IntPtr GetReplicationInfoHelper(IntPtr dsHandle, int type, int secondaryType, string partition, ref bool advanced, int context, LoadLibrarySafeHandle libHandle)
318         {
319             IntPtr info = (IntPtr)0;
320             int result = 0;
321             bool needToTryAgain = true;
322             IntPtr functionPtr;
323 
324             // first try to use the DsReplicaGetInfo2W API which does not exist on win2k machine
325             // call DsReplicaGetInfo2W
326             functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaGetInfo2W");
327             if (functionPtr == (IntPtr)0)
328             {
329                 // a win2k machine which does not have it.
330                 functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaGetInfoW");
331                 if (functionPtr == (IntPtr)0)
332                 {
333                     throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
334                 }
335                 UnsafeNativeMethods.DsReplicaGetInfoW dsReplicaGetInfoW = (UnsafeNativeMethods.DsReplicaGetInfoW)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaGetInfoW));
336                 result = dsReplicaGetInfoW(dsHandle, secondaryType, partition, (IntPtr)0, ref info);
337                 advanced = false;
338                 needToTryAgain = false;
339             }
340             else
341             {
342                 UnsafeNativeMethods.DsReplicaGetInfo2W dsReplicaGetInfo2W = (UnsafeNativeMethods.DsReplicaGetInfo2W)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaGetInfo2W));
343                 result = dsReplicaGetInfo2W(dsHandle, type, partition, (IntPtr)0, null, null, 0, context, ref info);
344             }
345 
346             // check the result
347             if (needToTryAgain && result == DS_REPL_NOTSUPPORTED)
348             {
349                 // this is the case that client is xp/win2k3, dc is win2k
350                 functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaGetInfoW");
351                 if (functionPtr == (IntPtr)0)
352                 {
353                     throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
354                 }
355                 UnsafeNativeMethods.DsReplicaGetInfoW dsReplicaGetInfoW = (UnsafeNativeMethods.DsReplicaGetInfoW)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaGetInfoW));
356 
357                 result = dsReplicaGetInfoW(dsHandle, secondaryType, partition, (IntPtr)0, ref info);
358                 advanced = false;
359             }
360 
361             if (result != 0)
362             {
363                 if (partition != null)
364                 {
365                     // this is the case of meta data
366                     if (type == (int)DS_REPL_INFO_TYPE.DS_REPL_INFO_METADATA_2_FOR_OBJ)
367                     {
368                         if (result == ExceptionHelper.ERROR_DS_DRA_BAD_DN || result == ExceptionHelper.ERROR_DS_NAME_UNPARSEABLE)
369                             throw new ArgumentException(ExceptionHelper.GetErrorMessage(result, false), "objectPath");
370 
371                         DirectoryEntry verifyEntry = DirectoryEntryManager.GetDirectoryEntry(this.context, partition);
372                         try
373                         {
374                             verifyEntry.RefreshCache(new string[] { "name" });
375                         }
376                         catch (COMException e)
377                         {
378                             if (e.ErrorCode == unchecked((int)0x80072020) |          // dir_error on server side
379                                    e.ErrorCode == unchecked((int)0x80072030))           // object not exists
380                                 throw new ArgumentException(SR.DSNoObject, "objectPath");
381                             else if (e.ErrorCode == unchecked((int)0x80005000) |          // bad path name
382                                    e.ErrorCode == unchecked((int)0x80072032)) // ERROR_DS_INVALID_DN_SYNTAX
383                                 throw new ArgumentException(SR.DSInvalidPath, "objectPath");
384                         }
385                     }
386                     else
387                     {
388                         if (!Partitions.Contains(partition))
389                             throw new ArgumentException(SR.ServerNotAReplica, "partition");
390                     }
391                 }
392 
393                 throw ExceptionHelper.GetExceptionFromErrorCode(result, Name);
394             }
395 
396             return info;
397         }
398 
ConstructReplicationCursors(IntPtr dsHandle, bool advanced, IntPtr info, string partition, DirectoryServer server, LoadLibrarySafeHandle libHandle)399         internal ReplicationCursorCollection ConstructReplicationCursors(IntPtr dsHandle, bool advanced, IntPtr info, string partition, DirectoryServer server, LoadLibrarySafeHandle libHandle)
400         {
401             int context = 0;
402             int count = 0;
403             ReplicationCursorCollection collection = new ReplicationCursorCollection(server);
404 
405             // construct the collection
406             if (advanced)
407             {
408                 // using paging to get all the results
409                 while (true)
410                 {
411                     try
412                     {
413                         if (info != (IntPtr)0)
414                         {
415                             DS_REPL_CURSORS_3 cursors = new DS_REPL_CURSORS_3();
416                             Marshal.PtrToStructure(info, cursors);
417                             count = cursors.cNumCursors;
418                             if (count > 0)
419                                 collection.AddHelper(partition, cursors, advanced, info);
420 
421                             context = cursors.dwEnumerationContext;
422 
423                             // we already get all the results or there is no result
424                             if (context == -1 || count == 0)
425                                 break;
426                         }
427                         else
428                         {
429                             // should not happen, treat as no result is returned.
430                             break;
431                         }
432                     }
433                     finally
434                     {
435                         FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_CURSORS_3_FOR_NC, info, libHandle);
436                     }
437 
438                     // get the next batch of results
439                     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, libHandle);
440                 }
441             }
442             else
443             {
444                 try
445                 {
446                     if (info != (IntPtr)0)
447                     {
448                         // structure of DS_REPL_CURSORS_3 and DS_REPL_CURSORS actually are the same
449                         DS_REPL_CURSORS cursors = new DS_REPL_CURSORS();
450                         Marshal.PtrToStructure(info, cursors);
451 
452                         collection.AddHelper(partition, cursors, advanced, info);
453                     }
454                 }
455                 finally
456                 {
457                     FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_CURSORS_FOR_NC, info, libHandle);
458                 }
459             }
460 
461             return collection;
462         }
463 
ConstructPendingOperations(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)464         internal ReplicationOperationInformation ConstructPendingOperations(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)
465         {
466             ReplicationOperationInformation replicationInfo = new ReplicationOperationInformation();
467             ReplicationOperationCollection collection = new ReplicationOperationCollection(server);
468             replicationInfo.collection = collection;
469             int count = 0;
470 
471             try
472             {
473                 if (info != (IntPtr)0)
474                 {
475                     DS_REPL_PENDING_OPS operations = new DS_REPL_PENDING_OPS();
476                     Marshal.PtrToStructure(info, operations);
477                     count = operations.cNumPendingOps;
478                     if (count > 0)
479                     {
480                         collection.AddHelper(operations, info);
481                         replicationInfo.startTime = DateTime.FromFileTime(operations.ftimeCurrentOpStarted);
482                         replicationInfo.currentOp = collection.GetFirstOperation();
483                     }
484                 }
485             }
486             finally
487             {
488                 FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_PENDING_OPS, info, libHandle);
489             }
490             return replicationInfo;
491         }
492 
ConstructNeighbors(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)493         internal ReplicationNeighborCollection ConstructNeighbors(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)
494         {
495             ReplicationNeighborCollection collection = new ReplicationNeighborCollection(server);
496             int count = 0;
497 
498             try
499             {
500                 if (info != (IntPtr)0)
501                 {
502                     DS_REPL_NEIGHBORS neighbors = new DS_REPL_NEIGHBORS();
503                     Marshal.PtrToStructure(info, neighbors);
504                     Debug.Assert(neighbors != null);
505                     count = neighbors.cNumNeighbors;
506                     if (count > 0)
507                         collection.AddHelper(neighbors, info);
508                 }
509             }
510             finally
511             {
512                 FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_NEIGHBORS, info, libHandle);
513             }
514             return collection;
515         }
516 
ConstructFailures(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)517         internal ReplicationFailureCollection ConstructFailures(IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)
518         {
519             ReplicationFailureCollection collection = new ReplicationFailureCollection(server);
520             int count = 0;
521 
522             try
523             {
524                 if (info != (IntPtr)0)
525                 {
526                     DS_REPL_KCC_DSA_FAILURES failures = new DS_REPL_KCC_DSA_FAILURES();
527                     Marshal.PtrToStructure(info, failures);
528                     count = failures.cNumEntries;
529                     if (count > 0)
530                         collection.AddHelper(failures, info);
531                 }
532             }
533             finally
534             {
535                 FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES, info, libHandle);
536             }
537             return collection;
538         }
539 
ConstructMetaData(bool advanced, IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)540         internal ActiveDirectoryReplicationMetadata ConstructMetaData(bool advanced, IntPtr info, DirectoryServer server, LoadLibrarySafeHandle libHandle)
541         {
542             ActiveDirectoryReplicationMetadata collection = new ActiveDirectoryReplicationMetadata(server);
543             int count = 0;
544 
545             if (advanced)
546             {
547                 try
548                 {
549                     if (info != (IntPtr)0)
550                     {
551                         DS_REPL_OBJ_META_DATA_2 objMetaData = new DS_REPL_OBJ_META_DATA_2();
552                         Marshal.PtrToStructure(info, objMetaData);
553 
554                         count = objMetaData.cNumEntries;
555                         if (count > 0)
556                         {
557                             collection.AddHelper(count, info, true);
558                         }
559                     }
560                 }
561                 finally
562                 {
563                     FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_METADATA_2_FOR_OBJ, info, libHandle);
564                 }
565             }
566             else
567             {
568                 try
569                 {
570                     DS_REPL_OBJ_META_DATA objMetadata = new DS_REPL_OBJ_META_DATA();
571                     Marshal.PtrToStructure(info, objMetadata);
572 
573                     count = objMetadata.cNumEntries;
574                     if (count > 0)
575                     {
576                         collection.AddHelper(count, info, false);
577                     }
578                 }
579                 finally
580                 {
581                     FreeReplicaInfo(DS_REPL_INFO_TYPE.DS_REPL_INFO_METADATA_FOR_OBJ, info, libHandle);
582                 }
583             }
584 
585             return collection;
586         }
587 
SyncAllCallbackRoutine(IntPtr data, IntPtr update)588         internal bool SyncAllCallbackRoutine(IntPtr data, IntPtr update)
589         {
590             if (SyncFromAllServersCallback == null)
591             {
592                 // user does not specify callback, resume the DsReplicaSyncAll execution
593                 return true;
594             }
595             else
596             {
597                 // user specifies callback
598                 // our callback is invoked, update should not be NULL, do assertion here
599                 Debug.Assert(update != (IntPtr)0);
600 
601                 DS_REPSYNCALL_UPDATE syncAllUpdate = new DS_REPSYNCALL_UPDATE();
602                 Marshal.PtrToStructure(update, syncAllUpdate);
603 
604                 // get the event type
605                 SyncFromAllServersEvent eventType = syncAllUpdate.eventType;
606 
607                 // get the error information
608                 IntPtr temp = syncAllUpdate.pErrInfo;
609                 SyncFromAllServersOperationException exception = null;
610 
611                 if (temp != (IntPtr)0)
612                 {
613                     // error information is available
614                     exception = ExceptionHelper.CreateSyncAllException(temp, true);
615                     if (exception == null)
616                     {
617                         // this is the special case that we ingore the failure when SyncAllOptions.CheckServerAlivenessOnly is specified
618                         return true;
619                     }
620                 }
621 
622                 string targetName = null;
623                 string sourceName = null;
624 
625                 temp = syncAllUpdate.pSync;
626                 if (temp != (IntPtr)0)
627                 {
628                     DS_REPSYNCALL_SYNC sync = new DS_REPSYNCALL_SYNC();
629                     Marshal.PtrToStructure(temp, sync);
630 
631                     targetName = Marshal.PtrToStringUni(sync.pszDstId);
632                     sourceName = Marshal.PtrToStringUni(sync.pszSrcId);
633                 }
634 
635                 // invoke the client callback
636                 SyncUpdateCallback clientCallback = SyncFromAllServersCallback;
637 
638                 return clientCallback(eventType, targetName, sourceName, exception);
639             }
640         }
641 
SyncReplicaAllHelper(IntPtr handle, SyncReplicaFromAllServersCallback syncAllFunctionPointer, string partition, SyncFromAllServersOptions option, SyncUpdateCallback callback, LoadLibrarySafeHandle libHandle)642         internal void SyncReplicaAllHelper(IntPtr handle, SyncReplicaFromAllServersCallback syncAllFunctionPointer, string partition, SyncFromAllServersOptions option, SyncUpdateCallback callback, LoadLibrarySafeHandle libHandle)
643         {
644             IntPtr errorInfo = (IntPtr)0;
645 
646             if (!Partitions.Contains(partition))
647                 throw new ArgumentException(SR.ServerNotAReplica, "partition");
648 
649             // we want to return the dn instead of DNS guid
650             // call DsReplicaSyncAllW
651             IntPtr functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaSyncAllW");
652             if (functionPtr == (IntPtr)0)
653             {
654                 throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
655             }
656             UnsafeNativeMethods.DsReplicaSyncAllW dsReplicaSyncAllW = (UnsafeNativeMethods.DsReplicaSyncAllW)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaSyncAllW));
657 
658             int result = dsReplicaSyncAllW(handle, partition, (int)option | DS_REPSYNCALL_ID_SERVERS_BY_DN, syncAllFunctionPointer, (IntPtr)0, ref errorInfo);
659 
660             try
661             {
662                 // error happens during the synchronization
663                 if (errorInfo != (IntPtr)0)
664                 {
665                     SyncFromAllServersOperationException e = ExceptionHelper.CreateSyncAllException(errorInfo, false);
666                     if (e == null)
667                         return;
668                     else
669                         throw e;
670                 }
671                 else
672                 {
673                     // API does not return error infor occurred during synchronization, but result is not success.
674                     if (result != 0)
675                         throw new SyncFromAllServersOperationException(ExceptionHelper.GetErrorMessage(result, false));
676                 }
677             }
678             finally
679             {
680                 // release the memory
681                 if (errorInfo != (IntPtr)0)
682                     UnsafeNativeMethods.LocalFree(errorInfo);
683             }
684         }
685 
FreeReplicaInfo(DS_REPL_INFO_TYPE type, IntPtr value, LoadLibrarySafeHandle libHandle)686         private void FreeReplicaInfo(DS_REPL_INFO_TYPE type, IntPtr value, LoadLibrarySafeHandle libHandle)
687         {
688             if (value != (IntPtr)0)
689             {
690                 // call DsReplicaFreeInfo
691                 IntPtr functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaFreeInfo");
692                 if (functionPtr == (IntPtr)0)
693                 {
694                     throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
695                 }
696                 UnsafeNativeMethods.DsReplicaFreeInfo dsReplicaFreeInfo = (UnsafeNativeMethods.DsReplicaFreeInfo)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaFreeInfo));
697 
698                 dsReplicaFreeInfo((int)type, value);
699             }
700         }
701 
SyncReplicaHelper(IntPtr dsHandle, bool isADAM, string partition, string sourceServer, int option, LoadLibrarySafeHandle libHandle)702         internal void SyncReplicaHelper(IntPtr dsHandle, bool isADAM, string partition, string sourceServer, int option, LoadLibrarySafeHandle libHandle)
703         {
704             int structSize = Marshal.SizeOf(typeof(Guid));
705             IntPtr unmanagedGuid = (IntPtr)0;
706             Guid guid = Guid.Empty;
707             AdamInstance adamServer = null;
708             DomainController dcServer = null;
709 
710             unmanagedGuid = Marshal.AllocHGlobal(structSize);
711             try
712             {
713                 if (sourceServer != null)
714                 {
715                     DirectoryContext newContext = Utils.GetNewDirectoryContext(sourceServer, DirectoryContextType.DirectoryServer, context);
716                     if (isADAM)
717                     {
718                         adamServer = AdamInstance.GetAdamInstance(newContext);
719                         guid = adamServer.NtdsaObjectGuid;
720                     }
721                     else
722                     {
723                         dcServer = DomainController.GetDomainController(newContext);
724                         guid = dcServer.NtdsaObjectGuid;
725                     }
726 
727                     Marshal.StructureToPtr(guid, unmanagedGuid, false);
728                 }
729 
730                 // call DsReplicaSyncW
731                 IntPtr functionPtr = UnsafeNativeMethods.GetProcAddress(libHandle, "DsReplicaSyncW");
732                 if (functionPtr == (IntPtr)0)
733                 {
734                     throw ExceptionHelper.GetExceptionFromErrorCode(Marshal.GetLastWin32Error());
735                 }
736                 UnsafeNativeMethods.DsReplicaSyncW dsReplicaSyncW = (UnsafeNativeMethods.DsReplicaSyncW)Marshal.GetDelegateForFunctionPointer(functionPtr, typeof(UnsafeNativeMethods.DsReplicaSyncW));
737 
738                 int result = dsReplicaSyncW(dsHandle, partition, unmanagedGuid, (int)option);
739 
740                 // check the result
741                 if (result != 0)
742                 {
743                     if (!Partitions.Contains(partition))
744                         throw new ArgumentException(SR.ServerNotAReplica, "partition");
745 
746                     string serverDownName = null;
747                     // this is the error returned when the server that we want to sync from is down
748                     if (result == ExceptionHelper.RPC_S_SERVER_UNAVAILABLE)
749                         serverDownName = sourceServer;
750                     // this is the error returned when the server that we want to get synced is down
751                     else if (result == ExceptionHelper.RPC_S_CALL_FAILED)
752                         serverDownName = Name;
753 
754                     throw ExceptionHelper.GetExceptionFromErrorCode(result, serverDownName);
755                 }
756             }
757             finally
758             {
759                 if (unmanagedGuid != (IntPtr)0)
760                     Marshal.FreeHGlobal(unmanagedGuid);
761 
762                 if (adamServer != null)
763                     adamServer.Dispose();
764 
765                 if (dcServer != null)
766                     dcServer.Dispose();
767             }
768         }
769 
GetInboundConnectionsHelper()770         internal ReplicationConnectionCollection GetInboundConnectionsHelper()
771         {
772             if (_inbound == null)
773             {
774                 // construct the replicationconnection collection
775                 _inbound = new ReplicationConnectionCollection();
776                 DirectoryContext newContext = Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context);
777 
778                 // this is the first time that user tries to retrieve this property, so get it from the directory
779                 string serverName = (this is DomainController) ? ((DomainController)this).ServerObjectName : ((AdamInstance)this).ServerObjectName;
780                 string srchDN = "CN=NTDS Settings," + serverName;
781                 DirectoryEntry de = DirectoryEntryManager.GetDirectoryEntry(Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context), srchDN);
782 
783                 ADSearcher adSearcher = new ADSearcher(de,
784                                                       "(&(objectClass=nTDSConnection)(objectCategory=nTDSConnection))",
785                                                       new string[] { "cn" },
786                                                       SearchScope.OneLevel);
787                 SearchResultCollection srchResults = null;
788 
789                 try
790                 {
791                     srchResults = adSearcher.FindAll();
792                     foreach (SearchResult r in srchResults)
793                     {
794                         ReplicationConnection con = new ReplicationConnection(newContext, r.GetDirectoryEntry(), (string)PropertyManager.GetSearchResultPropertyValue(r, PropertyManager.Cn));
795                         _inbound.Add(con);
796                     }
797                 }
798                 catch (COMException e)
799                 {
800                     throw ExceptionHelper.GetExceptionFromCOMException(newContext, e);
801                 }
802                 finally
803                 {
804                     if (srchResults != null)
805                         srchResults.Dispose();
806 
807                     de.Dispose();
808                 }
809             }
810 
811             return _inbound;
812         }
813 
GetOutboundConnectionsHelper()814         internal ReplicationConnectionCollection GetOutboundConnectionsHelper()
815         {
816             // this is the first time that user tries to retrieve this property, so get it from the directory
817             if (_outbound == null)
818             {
819                 // search base is the site container
820                 string siteName = (this is DomainController) ? ((DomainController)this).SiteObjectName : ((AdamInstance)this).SiteObjectName;
821                 DirectoryEntry de = DirectoryEntryManager.GetDirectoryEntry(Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context), siteName);
822 
823                 string serverName = (this is DomainController) ? ((DomainController)this).ServerObjectName : ((AdamInstance)this).ServerObjectName;
824                 ADSearcher adSearcher = new ADSearcher(de,
825                                                                "(&(objectClass=nTDSConnection)(objectCategory=nTDSConnection)(fromServer=CN=NTDS Settings," + serverName + "))",
826                                                                new string[] { "objectClass", "cn" },
827                                                                SearchScope.Subtree);
828 
829                 SearchResultCollection results = null;
830                 DirectoryContext newContext = Utils.GetNewDirectoryContext(Name, DirectoryContextType.DirectoryServer, context);
831 
832                 try
833                 {
834                     results = adSearcher.FindAll();
835                     _outbound = new ReplicationConnectionCollection();
836 
837                     foreach (SearchResult result in results)
838                     {
839                         ReplicationConnection con = new ReplicationConnection(newContext, result.GetDirectoryEntry(), (string)result.Properties["cn"][0]);
840                         _outbound.Add(con);
841                     }
842                 }
843                 catch (COMException e)
844                 {
845                     throw ExceptionHelper.GetExceptionFromCOMException(newContext, e);
846                 }
847                 finally
848                 {
849                     if (results != null)
850                         results.Dispose();
851 
852                     de.Dispose();
853                 }
854             }
855 
856             return _outbound;
857         }
858 
859         #endregion private methods
860     }
861 }
862