Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1072 lines
30 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. announce.c
  5. Abstract:
  6. Routines to handle ssi announcements.
  7. Author:
  8. Ported from Lan Man 2.0
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 21-May-1991 (cliffv)
  15. Ported to NT. Converted to NT style.
  16. 02-Jan-1992 (madana)
  17. added support for builtin/multidomain replication.
  18. --*/
  19. //
  20. // Common include files.
  21. //
  22. #include "logonsrv.h" // Include files common to entire service
  23. #pragma hdrstop
  24. //
  25. // Include files specific to this .c file
  26. //
  27. //
  28. // Maximum number of pulses that we allow a BDC to ignore before ignoring it.
  29. //
  30. #define MAX_PULSE_TIMEOUT 3
  31. VOID
  32. NlRemovePendingBdc(
  33. IN PSERVER_SESSION ServerSession
  34. )
  35. /*++
  36. Routine Description:
  37. Remove the specified Server Session from the list of pending BDCs.
  38. Enter with the ServerSessionTable Sem locked
  39. Arguments:
  40. ServerSession -- Pointer to the server session structure to remove from the
  41. list.
  42. Return Value:
  43. None.
  44. --*/
  45. {
  46. //
  47. // Ensure the server session is really on the list.
  48. //
  49. if ( (ServerSession->SsFlags & SS_PENDING_BDC) == 0 ) {
  50. return;
  51. }
  52. //
  53. // Decrement the count of pending BDCs
  54. //
  55. NlAssert( NlGlobalPendingBdcCount > 0 );
  56. NlGlobalPendingBdcCount --;
  57. //
  58. // If this is the last BDC in the pending list,
  59. // turn off the timer.
  60. //
  61. if ( NlGlobalPendingBdcCount == 0 ) {
  62. NlGlobalPendingBdcTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  63. }
  64. //
  65. // Remove the pending BDC from the list of pending BDCs.
  66. //
  67. RemoveEntryList( &ServerSession->SsPendingBdcList );
  68. //
  69. // Turn off the flag indicating we're in the list.
  70. //
  71. ServerSession->SsFlags &= ~SS_PENDING_BDC;
  72. NlPrint((NL_PULSE_MORE,
  73. "NlRemovePendingBdc: %s: Removed from pending list. Count: %ld\n",
  74. ServerSession->SsComputerName,
  75. NlGlobalPendingBdcCount ));
  76. }
  77. VOID
  78. NlAddPendingBdc(
  79. IN PSERVER_SESSION ServerSession
  80. )
  81. /*++
  82. Routine Description:
  83. Add the specified Server Session to the list of pending BDCs.
  84. Enter with the ServerSessionTable Sem locked
  85. Arguments:
  86. ServerSession -- Pointer to the server session structure to add to the
  87. list.
  88. Return Value:
  89. None.
  90. --*/
  91. {
  92. //
  93. // Ensure the server session is really off the list.
  94. //
  95. if ( ServerSession->SsFlags & SS_PENDING_BDC ) {
  96. return;
  97. }
  98. //
  99. // If this is the first pending BDC,
  100. // start the timer.
  101. //
  102. if ( NlGlobalPendingBdcCount == 0 ) {
  103. // Run the timer at twice the frequency of the timeout to ensure that
  104. // entries don't have to wait nearly twice the timeout period before
  105. // they expire.
  106. NlGlobalPendingBdcTimer.Period = NlGlobalParameters.PulseTimeout1 * 500;
  107. NlQuerySystemTime( &NlGlobalPendingBdcTimer.StartTime );
  108. //
  109. // Tell the main thread that I've changed a timer.
  110. //
  111. if ( !SetEvent( NlGlobalTimerEvent ) ) {
  112. NlPrint(( NL_CRITICAL,
  113. "NlAddPendingBdc: %s: SetEvent2 failed %ld\n",
  114. ServerSession->SsComputerName,
  115. GetLastError() ));
  116. }
  117. }
  118. //
  119. // Increment the count of pending BDCs
  120. //
  121. NlGlobalPendingBdcCount ++;
  122. //
  123. // Add the pending BDC to the list of pending BDCs.
  124. //
  125. InsertTailList( &NlGlobalPendingBdcList, &ServerSession->SsPendingBdcList );
  126. //
  127. // Turn on the flag indicating we're in the list.
  128. //
  129. ServerSession->SsFlags |= SS_PENDING_BDC;
  130. NlPrint((NL_PULSE_MORE,
  131. "NlAddPendingBdc: %s: Added to pending list. Count: %ld\n",
  132. ServerSession->SsComputerName,
  133. NlGlobalPendingBdcCount ));
  134. }
  135. VOID
  136. NetpLogonPutDBInfo(
  137. IN PDB_CHANGE_INFO DBInfo,
  138. IN OUT PCHAR * Where
  139. )
  140. /*++
  141. Routine Description:
  142. Put Database info structure in mailslot buffer.
  143. Arguments:
  144. DBInfo : database info structure pointer.
  145. Where : indirect pointer to mailslot buffer. Database info
  146. is copied over here. When returned this pointer is
  147. updated to point the end of mailslot buffer.
  148. Return Value:
  149. None.
  150. --*/
  151. {
  152. NetpLogonPutBytes( &DBInfo->DBIndex, sizeof(DBInfo->DBIndex), Where);
  153. NetpLogonPutBytes( &(DBInfo->LargeSerialNumber),
  154. sizeof(DBInfo->LargeSerialNumber),
  155. Where);
  156. NetpLogonPutBytes( &(DBInfo->NtDateAndTime),
  157. sizeof(DBInfo->NtDateAndTime),
  158. Where);
  159. }
  160. VOID
  161. NetpLogonUpdateDBInfo(
  162. IN PLARGE_INTEGER SerialNumber,
  163. IN OUT PCHAR * Where
  164. )
  165. /*++
  166. Routine Description:
  167. Update the Serial Number within the already packed mailslot buffer.
  168. Arguments:
  169. SerialNumber: New SerialNumber.
  170. Where : indirect pointer to mailslot buffer. Database info
  171. is copied over here. When returned this pointer is
  172. updated to point the end of mailslot buffer.
  173. Return Value:
  174. None.
  175. --*/
  176. {
  177. *Where += sizeof(DWORD);
  178. NetpLogonPutBytes( SerialNumber, sizeof(LARGE_INTEGER), Where);
  179. *Where += sizeof(LARGE_INTEGER);
  180. }
  181. BOOLEAN
  182. NlAllocatePrimaryAnnouncement(
  183. OUT PNETLOGON_DB_CHANGE *UasChangeBuffer,
  184. OUT LPDWORD UasChangeSize,
  185. OUT PCHAR *DbChangeInfoPointer
  186. )
  187. /*++
  188. Routine Description:
  189. Build and allocate an UAS_CHANGE message which describes the latest
  190. account database changes.
  191. Arguments:
  192. UasChangeBuffer - Returns a pointer to the buffer containing the message.
  193. The caller is responsible for freeing the buffer using NetpMemoryFree.
  194. UasChangeSize - Returns the size (in bytes) of the allocated buffer.
  195. DbChangeInfoPointer - Returns the address of the DB Change info
  196. within the allocated buffer. The field is not properly aligned.
  197. Return Value:
  198. TRUE - iff the buffer could be successfully allocated.
  199. --*/
  200. {
  201. PNETLOGON_DB_CHANGE UasChange;
  202. DB_CHANGE_INFO DBChangeInfo;
  203. ULONG DateAndTime1970;
  204. DWORD NumDBs;
  205. PCHAR Where;
  206. DWORD i;
  207. DWORD DomainSidSize;
  208. //
  209. // allocate space for this message.
  210. //
  211. DomainSidSize = RtlLengthSid( NlGlobalDomainInfo->DomAccountDomainId );
  212. UasChange = NetpMemoryAllocate(
  213. sizeof(NETLOGON_DB_CHANGE)+
  214. (NUM_DBS - 1) * sizeof(DB_CHANGE_INFO) +
  215. (DomainSidSize - 1) +
  216. sizeof(DWORD) // for DWORD alignment of SID
  217. );
  218. if( UasChange == NULL ) {
  219. NlPrint((NL_CRITICAL, "NlAllocatePrimaryAnnouncement can't allocate memory\n" ));
  220. return FALSE;
  221. }
  222. //
  223. // Build the UasChange message using the latest domain modified
  224. // information from SAM.
  225. //
  226. UasChange->Opcode = LOGON_UAS_CHANGE;
  227. LOCK_CHANGELOG();
  228. SmbPutUlong( &UasChange->LowSerialNumber,
  229. NlGlobalChangeLogDesc.SerialNumber[SAM_DB].LowPart);
  230. if (!RtlTimeToSecondsSince1970( &NlGlobalDBInfoArray[SAM_DB].CreationTime,
  231. &DateAndTime1970 )) {
  232. NlPrint((NL_CRITICAL, "DomainCreationTime can't be converted\n" ));
  233. DateAndTime1970 = 0;
  234. }
  235. SmbPutUlong( &UasChange->DateAndTime, DateAndTime1970 );
  236. // Tell the BDC we only intend to send pulses infrequently
  237. SmbPutUlong( &UasChange->Pulse, NlGlobalParameters.PulseMaximum);
  238. // Caller will change this field as appropriate
  239. SmbPutUlong( &UasChange->Random, 0 );
  240. Where = UasChange->PrimaryDCName;
  241. NetpLogonPutOemString( NlGlobalDomainInfo->DomOemComputerName,
  242. sizeof(UasChange->PrimaryDCName),
  243. &Where );
  244. NetpLogonPutOemString( NlGlobalDomainInfo->DomOemDomainName,
  245. sizeof(UasChange->DomainName),
  246. &Where );
  247. //
  248. // builtin domain support
  249. //
  250. NetpLogonPutUnicodeString( NlGlobalDomainInfo->DomUnicodeComputerNameString.Buffer,
  251. sizeof(UasChange->UnicodePrimaryDCName),
  252. &Where );
  253. NetpLogonPutUnicodeString( NlGlobalDomainInfo->DomUnicodeDomainName,
  254. sizeof(UasChange->UnicodeDomainName),
  255. &Where );
  256. //
  257. // number of database info that follow
  258. //
  259. NumDBs = NUM_DBS;
  260. NetpLogonPutBytes( &NumDBs, sizeof(NumDBs), &Where );
  261. *DbChangeInfoPointer = Where;
  262. for( i = 0; i < NUM_DBS; i++) {
  263. DBChangeInfo.DBIndex =
  264. NlGlobalDBInfoArray[i].DBIndex;
  265. DBChangeInfo.LargeSerialNumber =
  266. NlGlobalChangeLogDesc.SerialNumber[i];
  267. DBChangeInfo.NtDateAndTime =
  268. NlGlobalDBInfoArray[i].CreationTime;
  269. NetpLogonPutDBInfo( &DBChangeInfo, &Where );
  270. }
  271. //
  272. // place domain SID in the message.
  273. //
  274. NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
  275. NetpLogonPutDomainSID( NlGlobalDomainInfo->DomAccountDomainId, DomainSidSize, &Where );
  276. NetpLogonPutNtToken( &Where, 0 );
  277. UNLOCK_CHANGELOG();
  278. *UasChangeSize = (DWORD)(Where - (PCHAR)UasChange);
  279. *UasChangeBuffer = UasChange;
  280. return TRUE;
  281. }
  282. VOID
  283. NlPrimaryAnnouncementFinish(
  284. IN PSERVER_SESSION ServerSession,
  285. IN DWORD DatabaseId,
  286. IN PLARGE_INTEGER SerialNumber
  287. )
  288. /*++
  289. Routine Description:
  290. Indicate that the specified BDC has completed replication of the specified
  291. database.
  292. Note: this BDC might not be on the pending list at at if it was doing the
  293. replication on its own accord. This routine is designed to handle that
  294. eventuality.
  295. Arguments:
  296. ServerSession -- Pointer to the server session structure to remove from the
  297. list.
  298. DatabaseID -- Database ID of the database
  299. SerialNumber -- SerialNumber of the latest delta returned to the BDC.
  300. NULL indicates a full sync just completed
  301. Return Value:
  302. None.
  303. --*/
  304. {
  305. BOOLEAN SendPulse = FALSE;
  306. //
  307. // Mark the session that the replication of this particular database
  308. // has finished.
  309. //
  310. LOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
  311. ServerSession->SsFlags &= ~NlGlobalDBInfoArray[DatabaseId].DBSessionFlag;
  312. //
  313. // If all of the databases are now replicated, OR
  314. // the BDC just finished a full sync on one or more of its database,
  315. // remove this BDC from the pending list.
  316. //
  317. if ( (ServerSession->SsFlags & SS_REPL_MASK) == 0 || SerialNumber == NULL ) {
  318. NlPrint((NL_PULSE_MORE,
  319. "NlPrimaryAnnouncementFinish: %s: all databases are now in sync on BDC\n",
  320. ServerSession->SsComputerName ));
  321. NlRemovePendingBdc( ServerSession );
  322. SendPulse = TRUE;
  323. }
  324. //
  325. // If a full sync just completed,
  326. // force a partial sync so we can update our serial numbers.
  327. //
  328. if ( SerialNumber == NULL ) {
  329. ServerSession->SsBdcDbSerialNumber[DatabaseId].QuadPart = 0;
  330. ServerSession->SsFlags |= NlGlobalDBInfoArray[DatabaseId].DBSessionFlag;
  331. //
  332. // Save the current serial number for this BDC.
  333. //
  334. } else {
  335. ServerSession->SsBdcDbSerialNumber[DatabaseId] = *SerialNumber;
  336. }
  337. NlPrint((NL_PULSE_MORE,
  338. "NlPrimaryAnnouncementFinish: %s: " FORMAT_LPWSTR " SerialNumber: %lx %lx\n",
  339. ServerSession->SsComputerName,
  340. NlGlobalDBInfoArray[DatabaseId].DBName,
  341. ServerSession->SsBdcDbSerialNumber[DatabaseId].HighPart,
  342. ServerSession->SsBdcDbSerialNumber[DatabaseId].LowPart ));
  343. UNLOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
  344. //
  345. // If this BDC is finished,
  346. // try to send a pulse to more BDCs.
  347. //
  348. if ( SendPulse ) {
  349. NlPrimaryAnnouncement( ANNOUNCE_CONTINUE );
  350. }
  351. }
  352. VOID
  353. NlPrimaryAnnouncementTimeout(
  354. VOID
  355. )
  356. /*++
  357. Routine Description:
  358. The primary announcement timer has expired.
  359. Handle timing out any BDC's that haven't responded yet.
  360. Arguments:
  361. None.
  362. Return Value:
  363. None.
  364. --*/
  365. {
  366. LARGE_INTEGER TimeNow;
  367. BOOLEAN SendPulse = FALSE;
  368. PLIST_ENTRY ListEntry;
  369. PSERVER_SESSION ServerSession;
  370. //
  371. // Get the current time of day
  372. //
  373. NlQuerySystemTime( &TimeNow );
  374. //
  375. // Handle each BDC that has a pulse pending.
  376. //
  377. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  378. for ( ListEntry = NlGlobalPendingBdcList.Flink ;
  379. ListEntry != &NlGlobalPendingBdcList ;
  380. ListEntry = ListEntry->Flink) {
  381. ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsPendingBdcList );
  382. //
  383. // Ignore entries that haven't timed out yet.
  384. //
  385. if ( ServerSession->SsLastPulseTime.QuadPart +
  386. NlGlobalParameters.PulseTimeout1_100ns.QuadPart >
  387. TimeNow.QuadPart ) {
  388. continue;
  389. }
  390. //
  391. // If the pulse has been sent and there has been no response at all,
  392. // OR there hasn't been another response in a VERY long time
  393. // time this entry out.
  394. //
  395. if ( (ServerSession->SsFlags & SS_PULSE_SENT) ||
  396. (ServerSession->SsLastPulseTime.QuadPart +
  397. NlGlobalParameters.PulseTimeout2_100ns.QuadPart <=
  398. TimeNow.QuadPart) ) {
  399. //
  400. // Increment the count of times this BDC has timed out.
  401. //
  402. if ( ServerSession->SsPulseTimeoutCount < MAX_PULSE_TIMEOUT ) {
  403. ServerSession->SsPulseTimeoutCount++;
  404. }
  405. //
  406. // Remove this entry from the queue.
  407. //
  408. NlPrint((NL_PULSE_MORE,
  409. "NlPrimaryAnnouncementTimeout: %s: BDC didn't respond to pulse.\n",
  410. ServerSession->SsComputerName ));
  411. NlRemovePendingBdc( ServerSession );
  412. //
  413. // Indicate we should send more pulses
  414. //
  415. SendPulse = TRUE;
  416. }
  417. }
  418. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  419. //
  420. // If any entry has timed out,
  421. // try to send a pulse to more BDCs.
  422. //
  423. // Do this in the domain thread as this routine is
  424. // called from the main thread that should not
  425. // be blocked on network I/O.
  426. //
  427. if ( SendPulse ) {
  428. DWORD DomFlags = DOM_PRIMARY_ANNOUNCE_CONTINUE;
  429. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  430. }
  431. }
  432. VOID
  433. NlPrimaryAnnouncement(
  434. IN DWORD AnnounceFlags
  435. )
  436. /*++
  437. Routine Description:
  438. Periodic broadcast of messages to domain containing latest
  439. account database changes.
  440. Arguments:
  441. AnnounceFlags - Flags requesting special handling of the announcement.
  442. ANNOUNCE_FORCE -- set to indicate that the pulse should be forced to
  443. all BDCs in the domain.
  444. ANNOUNCE_CONTINUE -- set to indicate that this call is a
  445. continuation of a previous call to process all entries.
  446. ANNOUNCE_IMMEDIATE -- set to indicate that this call is a result
  447. of a request for immediate replication
  448. Return Value:
  449. None.
  450. --*/
  451. {
  452. NTSTATUS Status;
  453. PNETLOGON_DB_CHANGE UasChange;
  454. DWORD UasChangeSize;
  455. PCHAR DbChangeInfoPointer;
  456. LARGE_INTEGER TimeNow;
  457. DWORD SessionFlags;
  458. PSERVER_SESSION ServerSession;
  459. PLIST_ENTRY ListEntry;
  460. static ULONG EntriesHandled = 0;
  461. static BOOLEAN ImmediateAnnouncement;
  462. NlPrint((NL_PULSE_MORE, "NlPrimaryAnnouncement: Entered %ld\n", AnnounceFlags ));
  463. //
  464. // If the DS is recovering from a backup,
  465. // avoid announcing that we're available.
  466. //
  467. if ( NlGlobalDsPaused ) {
  468. NlPrint((NL_PULSE_MORE, "NlPrimaryAnnouncement: Ds is paused\n" ));
  469. return;
  470. }
  471. //
  472. // Allocate the UAS_CHANGE message to send.
  473. //
  474. if ( !NlAllocatePrimaryAnnouncement( &UasChange,
  475. &UasChangeSize,
  476. &DbChangeInfoPointer ) ) {
  477. return;
  478. }
  479. //
  480. // If we need to force the pulse to all the BDCs,
  481. // mark that we've not done any entries yet, and
  482. // mark each entry that a pulse is needed.
  483. //
  484. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  485. if ( AnnounceFlags & ANNOUNCE_FORCE ) {
  486. EntriesHandled = 0;
  487. for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
  488. ListEntry != &NlGlobalBdcServerSessionList ;
  489. ListEntry = ListEntry->Flink) {
  490. ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
  491. ServerSession->SsFlags |= SS_FORCE_PULSE;
  492. }
  493. }
  494. //
  495. // If this isn't a continuation of a previous request to send out pulses,
  496. // Reset the count of BDCs that have been handled.
  497. //
  498. if ( (AnnounceFlags & ANNOUNCE_CONTINUE) == 0 ) {
  499. EntriesHandled = 0;
  500. //
  501. // Remember whether this was an immediate announcement for the
  502. // initial call and all of the continuations.
  503. //
  504. ImmediateAnnouncement = (AnnounceFlags & ANNOUNCE_IMMEDIATE) != 0;
  505. }
  506. //
  507. // Loop sending announcements until
  508. // we have the maximum number of announcements outstanding, OR
  509. // we've processed all the entries in the list.
  510. //
  511. while ( NlGlobalPendingBdcCount < NlGlobalParameters.PulseConcurrency &&
  512. EntriesHandled < NlGlobalBdcServerSessionCount ) {
  513. BOOLEAN SendPulse;
  514. LPWSTR TransportName;
  515. DWORD MaxSessionFlags;
  516. //
  517. // If netlogon is exitting,
  518. // stop sending announcements
  519. //
  520. if ( NlGlobalTerminate ) {
  521. break;
  522. }
  523. //
  524. // Get the server session entry for the next BDC in the list.
  525. //
  526. // The BDC Server Session list is maintained in the order pulses should
  527. // be sent. As a pulse is sent (or is skipped), the entry is placed
  528. // at the tail of the list. This gives each BDC a chance at a pulse
  529. // before any BDC is repeated.
  530. ListEntry = NlGlobalBdcServerSessionList.Flink ;
  531. ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
  532. SendPulse = FALSE;
  533. SessionFlags = 0;
  534. // Only replicate those databases that negotiation says to replicate
  535. MaxSessionFlags = NlMaxReplMask(ServerSession->SsNegotiatedFlags);
  536. if ( ServerSession->SsTransport == NULL ) {
  537. TransportName = NULL;
  538. } else {
  539. TransportName = ServerSession->SsTransport->TransportName;
  540. }
  541. //
  542. // Determine if we should send an announcement to this BDC.
  543. //
  544. // Send a pluse unconditionally if a pulse is being forced.
  545. //
  546. if ( ServerSession->SsFlags & SS_FORCE_PULSE ) {
  547. NlPrint((NL_PULSE_MORE,
  548. "NlPrimaryAnnouncement: %s: pulse forced to be sent\n",
  549. ServerSession->SsComputerName ));
  550. SendPulse = TRUE;
  551. ServerSession->SsFlags &= ~SS_FORCE_PULSE;
  552. SessionFlags = MaxSessionFlags;
  553. TransportName = NULL; // Send on all transports
  554. //
  555. // Only send to any other BDC if there isn't a pulse outstanding and
  556. // previous announcements haven't been ignored.
  557. //
  558. } else if ( (ServerSession->SsFlags & SS_PENDING_BDC) == 0 &&
  559. ServerSession->SsPulseTimeoutCount < MAX_PULSE_TIMEOUT ) {
  560. ULONG i;
  561. SessionFlags = 0;
  562. //
  563. // Only send an announcement if at least one database is out
  564. // of sync.
  565. //
  566. for( i = 0; i < NUM_DBS; i++) {
  567. //
  568. // If this BDC isn't interested in this database,
  569. // just skip it.
  570. //
  571. if ( (NlGlobalDBInfoArray[i].DBSessionFlag & MaxSessionFlags) == 0 ) {
  572. continue;
  573. }
  574. //
  575. // If we need to know the serial number of the BDC,
  576. // force the replication.
  577. //
  578. if ( ServerSession->SsFlags &
  579. NlGlobalDBInfoArray[i].DBSessionFlag ) {
  580. NlPrint((NL_PULSE_MORE,
  581. "NlPrimaryAnnouncement: %s: " FORMAT_LPWSTR " database serial number needed. Pulse sent.\n",
  582. ServerSession->SsComputerName,
  583. NlGlobalDBInfoArray[i].DBName ));
  584. SendPulse = TRUE;
  585. SessionFlags |= NlGlobalDBInfoArray[i].DBSessionFlag;
  586. //
  587. // If the BDC is out of sync with us,
  588. // tell it.
  589. //
  590. } else if ( NlGlobalChangeLogDesc.SerialNumber[i].QuadPart >
  591. ServerSession->SsBdcDbSerialNumber[i].QuadPart ) {
  592. NlPrint((NL_PULSE_MORE,
  593. "NlPrimaryAnnouncement: %s: " FORMAT_LPWSTR " database is out of sync. Pulse sent.\n",
  594. ServerSession->SsComputerName,
  595. NlGlobalDBInfoArray[i].DBName ));
  596. SendPulse = TRUE;
  597. SessionFlags |= NlGlobalDBInfoArray[i].DBSessionFlag;
  598. }
  599. }
  600. //
  601. // Fix a timing window on NT 3.1 BDCs.
  602. //
  603. // During promotion of a BDC to PDC, the following events occur:
  604. // Two server accounts are changed on the old PDC and
  605. // are marked for immediate replication.
  606. // The Server manager asks the new PDC to partial sync.
  607. //
  608. // If the first immediate replication starts immediately, and the
  609. // second immediate replication pulse is ignored because replication
  610. // is in progress, and the first replication has finished the SAM
  611. // database and is working on the LSA database when the server
  612. // manager partial sync request comes in, then that request will be
  613. // ignored (rightfully) since replication is still in progress.
  614. // However, an NT 3.1 BDC replicator thread will not go back to
  615. // do the SAM database once it finishes with the LSA database. So
  616. // the replicator thread terminates with the SAM database still
  617. // needing replication. The server manager (rightfully) interprets
  618. // this as an error.
  619. //
  620. // Our solution is to set the backoff period on such "immediate"
  621. // replication attempts to the same value an NT 3.1 PDC would use.
  622. // This typically prevents the initial replication from starting in
  623. // the first place.
  624. //
  625. // Only do it for NT 3.1 BDCs since we're risking being overloaded.
  626. //
  627. if ( ImmediateAnnouncement &&
  628. SendPulse &&
  629. (ServerSession->SsFlags & SS_AUTHENTICATED) &&
  630. (ServerSession->SsNegotiatedFlags & NETLOGON_SUPPORTS_PERSISTENT_BDC) == 0 ) {
  631. SessionFlags = 0;
  632. }
  633. }
  634. //
  635. // Send a pulse unconditionally if it has been PulseMaximum since the
  636. // latest pulse.
  637. //
  638. // Avoid this if the BDC is an NT 5 BDC that doesn't use Netlogon to replicate
  639. // from us.
  640. //
  641. if ( !SendPulse &&
  642. MaxSessionFlags != 0 &&
  643. NlTimeHasElapsedEx( &ServerSession->SsLastPulseTime,
  644. &NlGlobalParameters.PulseMaximum_100ns,
  645. NULL ) ) {
  646. NlPrint((NL_PULSE_MORE,
  647. "NlPrimaryAnnouncement: %s: Maximum pulse since previous pulse. Pulse sent.\n",
  648. ServerSession->SsComputerName ));
  649. SendPulse = TRUE;
  650. SessionFlags = 0;
  651. TransportName = NULL; // Send on all transports
  652. }
  653. //
  654. // Put this entry at the tail of the list regardless of whether
  655. // we'll actually send an announcement to it.
  656. //
  657. RemoveEntryList( ListEntry );
  658. InsertTailList( &NlGlobalBdcServerSessionList, ListEntry );
  659. EntriesHandled ++;
  660. //
  661. // Send the announcement.
  662. //
  663. if ( SendPulse ) {
  664. WCHAR LocalComputerName[CNLEN+1];
  665. PCHAR Where;
  666. ULONG i;
  667. //
  668. // Add this BDC to the list of BDCs that have a pulse pending.
  669. //
  670. // Don't add this BDC to the list if we don't expect a response.
  671. // We don't expect a response from an LM BDC. We don't expect
  672. // a response from a BDC that is merely getting its PulseMaximum
  673. // pulse.
  674. //
  675. // If we don't expect a response, set the backoff period to a
  676. // large value to prevent a large load on the PDC in the case
  677. // that the BDC does actually respond.
  678. //
  679. // If we expect a response, set the backoff period to almost
  680. // immediately since we're waiting for them.
  681. //
  682. if ( SessionFlags == 0 ) {
  683. SmbPutUlong( &UasChange->Random,
  684. max(NlGlobalParameters.Randomize,
  685. NlGlobalParameters.Pulse/10) );
  686. } else {
  687. NlAddPendingBdc( ServerSession );
  688. SmbPutUlong( &UasChange->Random, NlGlobalParameters.Randomize );
  689. }
  690. //
  691. // Indicate that the pulse has been sent.
  692. // This flag is set from the time we send the pulse until the
  693. // first time the BDC responds. We use this to detect failed
  694. // BDCs that have a secure channel up.
  695. //
  696. ServerSession->SsFlags &= ~SS_REPL_MASK;
  697. ServerSession->SsFlags |= SS_PULSE_SENT | SessionFlags;
  698. NlQuerySystemTime( &TimeNow );
  699. ServerSession->SsLastPulseTime = TimeNow;
  700. //
  701. // Don't keep the server session locked since sending the mailslot
  702. // message takes a long time.
  703. //
  704. NetpCopyStrToWStr( LocalComputerName,
  705. ServerSession->SsComputerName );
  706. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  707. //
  708. // Update the message to be specific to this BDC.
  709. //
  710. // If we need the BDC to respond,
  711. // set the serial number to make the BDC think it has a lot
  712. // of deltas to pick up.
  713. //
  714. LOCK_CHANGELOG();
  715. Where = DbChangeInfoPointer;
  716. for( i = 0; i < NUM_DBS; i++) {
  717. LARGE_INTEGER SerialNumber;
  718. SerialNumber = NlGlobalChangeLogDesc.SerialNumber[i];
  719. if ( NlGlobalDBInfoArray[i].DBSessionFlag & SessionFlags ) {
  720. //
  721. // Don't change the high part since
  722. // a) NT 3.1 BDCs will do a full sync if there are too
  723. // many changes.
  724. // b) The high part contains the PDC promotion count.
  725. //
  726. SerialNumber.LowPart = 0xFFFFFFFF;
  727. }
  728. NetpLogonUpdateDBInfo( &SerialNumber, &Where );
  729. }
  730. UNLOCK_CHANGELOG();
  731. //
  732. // Send the datagram to this BDC.
  733. // Failure isn't fatal
  734. //
  735. #if NETLOGONDBG
  736. NlPrintDom((NL_MAILSLOT, NlGlobalDomainInfo,
  737. "Sent '%s' message to %ws[%s] on %ws.\n",
  738. NlMailslotOpcode(UasChange->Opcode),
  739. LocalComputerName,
  740. NlDgrNameType(ComputerName),
  741. TransportName ));
  742. #endif // NETLOGONDBG
  743. Status = NlBrowserSendDatagram(
  744. NlGlobalDomainInfo,
  745. 0,
  746. LocalComputerName,
  747. ComputerName,
  748. TransportName,
  749. NETLOGON_LM_MAILSLOT_A,
  750. UasChange,
  751. UasChangeSize,
  752. TRUE, // send synchronously
  753. NULL ); // Don't flush Netbios cache
  754. if ( !NT_SUCCESS(Status) ) {
  755. NlPrint((NL_CRITICAL,
  756. "Cannot send datagram to '%ws' 0x%lx\n",
  757. LocalComputerName,
  758. Status ));
  759. }
  760. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  761. } else {
  762. NlPrint((NL_PULSE_MORE,
  763. "NlPrimaryAnnouncement: %s: pulse not needed at this time.\n",
  764. ServerSession->SsComputerName ));
  765. }
  766. }
  767. //
  768. // If the flags are zero, we are called from
  769. // the domain thread started by the main loop.
  770. // Tell the main loop we are done doing the work.
  771. //
  772. if ( AnnounceFlags == 0 ) {
  773. NlGlobalPrimaryAnnouncementIsRunning = FALSE;
  774. }
  775. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  776. //
  777. // Free up message memory.
  778. //
  779. NetpMemoryFree( UasChange );
  780. NlPrint((NL_PULSE_MORE, "NlPrimaryAnnouncement: Return\n" ));
  781. return;
  782. }