Source code of Windows XP (NT5)
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.

2663 lines
75 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. announce.c
  5. Abstract:
  6. This module implements the routines needed to manage the bowser's
  7. announcement table.
  8. Author:
  9. Larry Osterman (larryo) 18-Oct-1991
  10. Revision History:
  11. 18-Oct-1991 larryo
  12. Created
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. //
  17. // List containing the chain of server announcement buffers. These structures
  18. // are allocated out of paged pool and are used to while transfering the
  19. // contents of the server announcement from the datagram reception indication
  20. // routine to the Bowser's FSP where they will be added to the announcement
  21. // database.
  22. //
  23. LIST_ENTRY
  24. BowserViewBufferHead = {0};
  25. KSPIN_LOCK
  26. BowserViewBufferListSpinLock = {0};
  27. LONG
  28. BowserNumberOfServerAnnounceBuffers = {0};
  29. BOOLEAN
  30. PackServerAnnouncement (
  31. IN ULONG Level,
  32. IN ULONG ServerTypeMask,
  33. IN OUT LPTSTR *BufferStart,
  34. IN OUT LPTSTR *BufferEnd,
  35. IN ULONG_PTR BufferDisplacment,
  36. IN PANNOUNCE_ENTRY Announcement,
  37. OUT PULONG TotalBytesNeeded
  38. );
  39. NTSTATUS
  40. AgeServerAnnouncements(
  41. PTRANSPORT Transport,
  42. PVOID Context
  43. );
  44. VOID
  45. BowserPromoteToBackup(
  46. IN PTRANSPORT Transport,
  47. IN PWSTR ServerName
  48. );
  49. VOID
  50. BowserShutdownRemoteBrowser(
  51. IN PTRANSPORT Transport,
  52. IN PWSTR ServerName
  53. );
  54. typedef struct _ENUM_SERVERS_CONTEXT {
  55. ULONG Level;
  56. PLUID LogonId;
  57. ULONG ServerTypeMask;
  58. PUNICODE_STRING DomainName OPTIONAL;
  59. PVOID OutputBuffer;
  60. PVOID OutputBufferEnd;
  61. ULONG OutputBufferSize;
  62. ULONG EntriesRead;
  63. ULONG TotalEntries;
  64. ULONG TotalBytesNeeded;
  65. ULONG_PTR OutputBufferDisplacement;
  66. ULONG ResumeKey;
  67. ULONG OriginalResumeKey;
  68. } ENUM_SERVERS_CONTEXT, *PENUM_SERVERS_CONTEXT;
  69. NTSTATUS
  70. EnumerateServersWorker(
  71. IN PTRANSPORT Transport,
  72. IN OUT PVOID Ctx
  73. );
  74. #ifdef ALLOC_PRAGMA
  75. #pragma alloc_text(PAGE, BowserCompareAnnouncement)
  76. #pragma alloc_text(PAGE, BowserAllocateAnnouncement)
  77. #pragma alloc_text(PAGE, BowserFreeAnnouncement)
  78. #pragma alloc_text(PAGE, BowserProcessHostAnnouncement)
  79. #pragma alloc_text(PAGE, BowserProcessDomainAnnouncement)
  80. #pragma alloc_text(PAGE, BowserAgeServerAnnouncements)
  81. #pragma alloc_text(PAGE, AgeServerAnnouncements)
  82. #pragma alloc_text(PAGE, BowserPromoteToBackup)
  83. #pragma alloc_text(PAGE, BowserShutdownRemoteBrowser)
  84. #pragma alloc_text(PAGE, BowserEnumerateServers)
  85. #pragma alloc_text(PAGE, EnumerateServersWorker)
  86. #pragma alloc_text(PAGE, PackServerAnnouncement)
  87. #pragma alloc_text(PAGE, BowserDeleteGenericTable)
  88. #pragma alloc_text(PAGE, BowserpInitializeAnnounceTable)
  89. #pragma alloc_text(PAGE, BowserpUninitializeAnnounceTable)
  90. #pragma alloc_text(PAGE4BROW, BowserFreeViewBuffer)
  91. #pragma alloc_text(PAGE4BROW, BowserAllocateViewBuffer)
  92. #pragma alloc_text(PAGE4BROW, BowserHandleServerAnnouncement)
  93. #pragma alloc_text(PAGE4BROW, BowserHandleDomainAnnouncement)
  94. #endif
  95. INLINE
  96. ULONG
  97. BowserSafeStrlen(
  98. IN PSZ String,
  99. IN ULONG MaximumStringLength
  100. )
  101. {
  102. ULONG Length = 0;
  103. while (MaximumStringLength-- && *String++) {
  104. Length += 1;
  105. }
  106. return Length;
  107. }
  108. DATAGRAM_HANDLER(
  109. BowserHandleServerAnnouncement
  110. )
  111. /*++
  112. Routine Description:
  113. This routine will process receive datagram indication messages, and
  114. process them as appropriate.
  115. Arguments:
  116. IN PTRANSPORT Transport - The transport provider for this request.
  117. IN ULONG BytesAvailable - number of bytes in complete Tsdu
  118. IN PHOST_ANNOUNCE_PACKET_1 HostAnnouncement - the server announcement.
  119. IN ULONG BytesAvailable - The number of bytes in the announcement.
  120. OUT ULONG *BytesTaken - number of bytes used
  121. IN UCHAR Opcode - the mailslot write opcode.
  122. Return Value:
  123. NTSTATUS - Status of operation.
  124. --*/
  125. {
  126. PVIEW_BUFFER ViewBuffer;
  127. ULONG HostNameLength = 0;
  128. ULONG CommentLength = 0;
  129. ULONG NameCommentMaxLength = 0;
  130. PHOST_ANNOUNCE_PACKET_1 HostAnnouncement = Buffer;
  131. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  132. #ifdef ENABLE_PSEUDO_BROWSER
  133. if ( BowserData.PseudoServerLevel == BROWSER_PSEUDO ) {
  134. // no-op for black hole server
  135. return STATUS_SUCCESS;
  136. }
  137. #endif
  138. ExInterlockedAddLargeStatistic(&BowserStatistics.NumberOfServerAnnouncements, 1);
  139. ViewBuffer = BowserAllocateViewBuffer();
  140. //
  141. // If we are unable to allocate a view buffer, ditch this datagram on
  142. // the floor.
  143. //
  144. if (ViewBuffer == NULL) {
  145. return STATUS_REQUEST_NOT_ACCEPTED;
  146. }
  147. if ((TransportName->NameType == MasterBrowser) ||
  148. (TransportName->NameType == BrowserElection)) {
  149. ULONG ServerElectionVersion;
  150. //
  151. // If this server announcement is sent to the master name, then
  152. // it is a BROWSE_ANNOUNCE packet, not a HOST_ANNOUNCE (ie, it's an
  153. // NT/WinBALL server, not a Lan Manager server.
  154. //
  155. // We need to grovel the bits out of the packet in an appropriate
  156. // manner.
  157. //
  158. PBROWSE_ANNOUNCE_PACKET_1 BrowseAnnouncement = (PBROWSE_ANNOUNCE_PACKET_1)HostAnnouncement;
  159. //
  160. // If this packet was smaller than a minimal server announcement,
  161. // ignore the request, it cannot be a legal request.
  162. //
  163. if (BytesAvailable < FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, Comment)) {
  164. BowserFreeViewBuffer(ViewBuffer);
  165. return STATUS_REQUEST_NOT_ACCEPTED;
  166. }
  167. //
  168. // This is a Lan Manager style server announcement.
  169. //
  170. #if DBG
  171. ViewBuffer->ServerType = 0xffffffff;
  172. #endif
  173. //
  174. // Verify that this announcement is not going to blow away the view
  175. // buffer.
  176. //
  177. HostNameLength = BowserSafeStrlen(BROWSE_ANNC_NAME(BrowseAnnouncement),
  178. BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, ServerName));
  179. if (HostNameLength > NETBIOS_NAME_LEN-1) {
  180. BowserFreeViewBuffer(ViewBuffer);
  181. return STATUS_REQUEST_NOT_ACCEPTED;
  182. }
  183. if (BowserSafeStrlen(BROWSE_ANNC_COMMENT(BrowseAnnouncement),
  184. BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, Comment)) > LM20_MAXCOMMENTSZ) {
  185. BowserFreeViewBuffer(ViewBuffer);
  186. return STATUS_REQUEST_NOT_ACCEPTED;
  187. }
  188. strncpy(ViewBuffer->ServerName, BROWSE_ANNC_NAME(BrowseAnnouncement),
  189. min(BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, ServerName),
  190. NETBIOS_NAME_LEN));
  191. ViewBuffer->ServerName[NETBIOS_NAME_LEN] = '\0';
  192. strncpy(ViewBuffer->ServerComment, BROWSE_ANNC_COMMENT(BrowseAnnouncement),
  193. min(BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, Comment), LM20_MAXCOMMENTSZ));
  194. ViewBuffer->ServerComment[LM20_MAXCOMMENTSZ] = '\0';
  195. ServerElectionVersion = SmbGetUlong(&BrowseAnnouncement->CommentPointer);
  196. //
  197. // Save away the election version of this server.
  198. //
  199. if ((ServerElectionVersion >> 16) == 0xaa55) {
  200. ViewBuffer->ServerBrowserVersion = (USHORT)(ServerElectionVersion & 0xffff);
  201. } else {
  202. if (!(BrowseAnnouncement->Type & SV_TYPE_NT)) {
  203. ViewBuffer->ServerBrowserVersion = (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR;
  204. } else {
  205. ViewBuffer->ServerBrowserVersion = 0;
  206. }
  207. }
  208. ViewBuffer->ServerType = SmbGetUlong(&BrowseAnnouncement->Type);
  209. dprintf(DPRT_ANNOUNCE, ("Received announcement from %s on transport %lx. Server type: %lx\n", ViewBuffer->ServerName, TransportName->Transport, ViewBuffer->ServerType));
  210. ViewBuffer->ServerVersionMajor = BrowseAnnouncement->VersionMajor;
  211. ViewBuffer->ServerVersionMinor = BrowseAnnouncement->VersionMinor;
  212. ViewBuffer->ServerPeriodicity = (USHORT)((SmbGetUlong(&BrowseAnnouncement->Periodicity) + 999) / 1000);
  213. } else {
  214. //
  215. // If this packet was smaller than a minimal server announcement,
  216. // ignore the request, it cannot be a legal request.
  217. //
  218. if (BytesAvailable < FIELD_OFFSET(HOST_ANNOUNCE_PACKET_1, NameComment)) {
  219. BowserFreeViewBuffer(ViewBuffer);
  220. return STATUS_REQUEST_NOT_ACCEPTED;
  221. }
  222. //
  223. // This is a Lan Manager style server announcement.
  224. //
  225. #if DBG
  226. ViewBuffer->ServerType = 0xffffffff;
  227. #endif
  228. //
  229. // Verify that this announcement is not going to blow away the view
  230. // buffer.
  231. //
  232. NameCommentMaxLength = BytesAvailable - FIELD_OFFSET(HOST_ANNOUNCE_PACKET_1, NameComment);
  233. HostNameLength = BowserSafeStrlen(HOST_ANNC_NAME(HostAnnouncement),
  234. NameCommentMaxLength);
  235. if (HostNameLength > NETBIOS_NAME_LEN) {
  236. BowserFreeViewBuffer(ViewBuffer);
  237. return STATUS_REQUEST_NOT_ACCEPTED;
  238. }
  239. //
  240. // We need to make sure that the hostname string was properly terminated
  241. // before using HOST_ANNC_COMMENT (which calls strlen on the hostname string).
  242. // The BowserSafeStrlen call above may have been terminated by the end of the
  243. // input buffer. If the length was terminated by the end of the buffer, the
  244. // conditional below will fail.
  245. //
  246. if (HostNameLength < NameCommentMaxLength) {
  247. CommentLength = BowserSafeStrlen(HOST_ANNC_COMMENT(HostAnnouncement),
  248. NameCommentMaxLength - HostNameLength - 1);
  249. if (CommentLength > LM20_MAXCOMMENTSZ) {
  250. BowserFreeViewBuffer(ViewBuffer);
  251. return STATUS_REQUEST_NOT_ACCEPTED;
  252. }
  253. }
  254. if (HostNameLength) {
  255. RtlCopyMemory(ViewBuffer->ServerName,HOST_ANNC_NAME(HostAnnouncement),HostNameLength);
  256. }
  257. ViewBuffer->ServerName[HostNameLength] = '\0';
  258. if (CommentLength) {
  259. RtlCopyMemory(ViewBuffer->ServerComment,HOST_ANNC_COMMENT(HostAnnouncement),CommentLength);
  260. }
  261. ViewBuffer->ServerComment[CommentLength] = '\0';
  262. ViewBuffer->ServerBrowserVersion = (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR;
  263. ViewBuffer->ServerType = SmbGetUlong(&HostAnnouncement->Type);
  264. dprintf(DPRT_ANNOUNCE, ("Received announcement from %s on transport %lx. Server type: %lx\n", ViewBuffer->ServerName, TransportName->Transport, ViewBuffer->ServerType));
  265. ViewBuffer->ServerVersionMajor = HostAnnouncement->VersionMajor;
  266. ViewBuffer->ServerVersionMinor = HostAnnouncement->VersionMinor;
  267. ViewBuffer->ServerPeriodicity = SmbGetUshort(&HostAnnouncement->Periodicity);
  268. }
  269. ViewBuffer->TransportName = TransportName;
  270. BowserReferenceTransportName(TransportName);
  271. dprintf(DPRT_REF, ("Call Reference transport %lx from BowserHandlerServerAnnouncement.\n", TransportName->Transport));
  272. BowserReferenceTransport( TransportName->Transport );
  273. ExInitializeWorkItem(&ViewBuffer->Overlay.WorkHeader, BowserProcessHostAnnouncement, ViewBuffer);
  274. BowserQueueDelayedWorkItem( &ViewBuffer->Overlay.WorkHeader );
  275. *BytesTaken = BytesAvailable;
  276. return STATUS_SUCCESS;
  277. }
  278. DATAGRAM_HANDLER(
  279. BowserHandleDomainAnnouncement
  280. )
  281. /*++
  282. Routine Description:
  283. This routine will process receive datagram indication messages, and
  284. process them as appropriate.
  285. Arguments:
  286. IN PTRANSPORT Transport - The transport provider for this request.
  287. IN ULONG BytesAvailable, - number of bytes in complete Tsdu
  288. IN PBROWSE_ANNOUNCE_PACKET_1 HostAnnouncement - the server announcement.
  289. IN ULONG BytesAvailable - The number of bytes in the announcement.
  290. OUT ULONG *BytesTaken, - number of bytes used
  291. Return Value:
  292. NTSTATUS - Status of operation.
  293. --*/
  294. {
  295. PVIEW_BUFFER ViewBuffer;
  296. PBROWSE_ANNOUNCE_PACKET_1 DomainAnnouncement = Buffer;
  297. ULONG HostNameLength;
  298. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  299. #ifdef ENABLE_PSEUDO_BROWSER
  300. if ( BowserData.PseudoServerLevel == BROWSER_PSEUDO ) {
  301. // no-op for black hole server
  302. return STATUS_SUCCESS;
  303. }
  304. #endif
  305. //
  306. // If we are not processing host announcements for this
  307. // name, ignore this request.
  308. //
  309. if (!TransportName->ProcessHostAnnouncements) {
  310. return STATUS_REQUEST_NOT_ACCEPTED;
  311. }
  312. ExInterlockedAddLargeStatistic(&BowserStatistics.NumberOfDomainAnnouncements, 1);
  313. //
  314. // If this packet was smaller than a minimal server announcement,
  315. // ignore the request, it cannot be a legal request.
  316. //
  317. if (BytesAvailable < FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, Comment)) {
  318. return STATUS_REQUEST_NOT_ACCEPTED;
  319. }
  320. //
  321. // Verify that this announcement is not going to blow away the view
  322. // buffer.
  323. //
  324. HostNameLength = BowserSafeStrlen(BROWSE_ANNC_NAME(DomainAnnouncement),
  325. BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, ServerName));
  326. if (HostNameLength > NETBIOS_NAME_LEN) {
  327. return STATUS_REQUEST_NOT_ACCEPTED;
  328. }
  329. ViewBuffer = BowserAllocateViewBuffer();
  330. //
  331. // If we are unable to allocate a view buffer, ditch this datagram on
  332. // the floor.
  333. //
  334. if (ViewBuffer == NULL) {
  335. return STATUS_REQUEST_NOT_ACCEPTED;
  336. }
  337. #if DBG
  338. ViewBuffer->ServerType = 0xffffffff;
  339. #endif
  340. strncpy(ViewBuffer->ServerName, BROWSE_ANNC_NAME(DomainAnnouncement),
  341. min(BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, ServerName),
  342. NETBIOS_NAME_LEN));
  343. ViewBuffer->ServerName[CNLEN] = '\0';
  344. //
  345. // The comment on a server announcement is the computer name.
  346. //
  347. // ASSERT (strlen(BROWSE_ANNC_COMMENT(DomainAnnouncement)) <= CNLEN);
  348. strncpy(ViewBuffer->ServerComment, BROWSE_ANNC_COMMENT(DomainAnnouncement),
  349. min(BytesAvailable - FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET_1, Comment),
  350. CNLEN));
  351. //
  352. // Force a null termination at the appropriate time.
  353. //
  354. ViewBuffer->ServerComment[CNLEN] = '\0';
  355. ViewBuffer->TransportName = TransportName;
  356. if (SmbGetUlong(&DomainAnnouncement->Type) & SV_TYPE_DOMAIN_ENUM) {
  357. ViewBuffer->ServerType = SmbGetUlong(&DomainAnnouncement->Type);
  358. } else {
  359. ViewBuffer->ServerType = SV_TYPE_DOMAIN_ENUM;
  360. }
  361. ViewBuffer->ServerVersionMajor = DomainAnnouncement->VersionMajor;
  362. ViewBuffer->ServerVersionMinor = DomainAnnouncement->VersionMinor;
  363. ViewBuffer->ServerPeriodicity = (USHORT)((SmbGetUlong(&DomainAnnouncement->Periodicity) + 999) / 1000);
  364. BowserReferenceTransportName(TransportName);
  365. dprintf(DPRT_REF, ("Call Reference transport %lx from BowserHandlerDomainAnnouncement.\n", TransportName->Transport));
  366. BowserReferenceTransport( TransportName->Transport );
  367. ExInitializeWorkItem(&ViewBuffer->Overlay.WorkHeader, BowserProcessDomainAnnouncement, ViewBuffer);
  368. BowserQueueDelayedWorkItem( &ViewBuffer->Overlay.WorkHeader );
  369. *BytesTaken = BytesAvailable;
  370. return STATUS_SUCCESS;
  371. }
  372. RTL_GENERIC_COMPARE_RESULTS
  373. BowserCompareAnnouncement(
  374. IN PRTL_GENERIC_TABLE Table,
  375. IN PVOID FirstStruct,
  376. IN PVOID SecondStruct
  377. )
  378. /*++
  379. Routine Description:
  380. This routine will compare two server announcements to see how they compare
  381. Arguments:
  382. IN PRTL_GENERIC_TABLE - Supplies the table containing the announcements
  383. IN PVOID FirstStuct - The first structure to compare.
  384. IN PVOID SecondStruct - The second structure to compare.
  385. Return Value:
  386. Result of the comparison.
  387. --*/
  388. {
  389. UNICODE_STRING ServerName1, ServerName2;
  390. PANNOUNCE_ENTRY Server1 = FirstStruct;
  391. PANNOUNCE_ENTRY Server2 = SecondStruct;
  392. LONG CompareResult;
  393. PAGED_CODE();
  394. RtlInitUnicodeString(&ServerName1, Server1->ServerName);
  395. RtlInitUnicodeString(&ServerName2, Server2->ServerName);
  396. CompareResult = RtlCompareUnicodeString(&ServerName1, &ServerName2, FALSE);
  397. if (CompareResult < 0) {
  398. return GenericLessThan;
  399. } else if (CompareResult > 0) {
  400. return GenericGreaterThan;
  401. } else {
  402. return GenericEqual;
  403. }
  404. UNREFERENCED_PARAMETER(Table);
  405. }
  406. PVOID
  407. BowserAllocateAnnouncement(
  408. IN PRTL_GENERIC_TABLE Table,
  409. IN CLONG ByteSize
  410. )
  411. /*++
  412. Routine Description:
  413. This routine will allocate space to hold an entry in a generic table.
  414. Arguments:
  415. IN PRTL_GENERIC_TABLE Table - Supplies the table to allocate entries for.
  416. IN CLONG ByteSize - Supplies the number of bytes to allocate for the entry.
  417. Return Value:
  418. None.
  419. --*/
  420. {
  421. PAGED_CODE();
  422. return ALLOCATE_POOL(PagedPool, ByteSize, POOL_ANNOUNCEMENT);
  423. UNREFERENCED_PARAMETER(Table);
  424. }
  425. VOID
  426. BowserFreeAnnouncement (
  427. IN PRTL_GENERIC_TABLE Table,
  428. IN PVOID Buffer
  429. )
  430. /*++
  431. Routine Description:
  432. This routine will free an entry in a generic table that is too old.
  433. Arguments:
  434. IN PRTL_GENERIC_TABLE Table - Supplies the table to allocate entries for.
  435. IN PVOID Buffer - Supplies the buffer to free.
  436. Return Value:
  437. None.
  438. --*/
  439. {
  440. PAGED_CODE();
  441. FREE_POOL(Buffer);
  442. UNREFERENCED_PARAMETER(Table);
  443. }
  444. INLINE
  445. BOOLEAN
  446. BowserIsLegalBackupBrowser(
  447. IN PANNOUNCE_ENTRY Announcement,
  448. IN PUNICODE_STRING ComputerName
  449. )
  450. {
  451. //
  452. // If we received this announcement on an "otherdomain", we will ignore
  453. // it.
  454. //
  455. if (Announcement->Name->NameType == OtherDomain) {
  456. return FALSE;
  457. }
  458. //
  459. // If the server doesn't indicate that it's a legal backup browser, we
  460. // want to ignore it.
  461. //
  462. if (!FlagOn(Announcement->ServerType, SV_TYPE_BACKUP_BROWSER)) {
  463. return FALSE;
  464. }
  465. //
  466. // If the server is the master browser, then we want to ignore it.
  467. //
  468. if (FlagOn(Announcement->ServerType, SV_TYPE_MASTER_BROWSER)) {
  469. return FALSE;
  470. }
  471. //
  472. // If the server is too old, we want to ignore it.
  473. //
  474. if (Announcement->ServerBrowserVersion < (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR) {
  475. return FALSE;
  476. }
  477. //
  478. // If the machine we're looking at is the current machine, then it cannot
  479. // be a legal backup - it must be a stale announcement sent before we
  480. // actually became the master.
  481. //
  482. if (RtlCompareMemory(Announcement->ServerName,
  483. ComputerName->Buffer,
  484. ComputerName->Length) == ComputerName->Length) {
  485. return FALSE;
  486. }
  487. return TRUE;
  488. }
  489. VOID
  490. BowserProcessHostAnnouncement(
  491. IN PVOID Context
  492. )
  493. /*++
  494. Routine Description:
  495. This routine will put a server announcement into the server announcement
  496. table
  497. Arguments:
  498. IN PWORK_HEADER Header - Supplies a pointer to a work header in a view buffer
  499. Return Value:
  500. None.
  501. --*/
  502. {
  503. PVIEW_BUFFER ViewBuffer = Context;
  504. ANNOUNCE_ENTRY ProtoEntry;
  505. UNICODE_STRING TempUString;
  506. OEM_STRING TempAString;
  507. PANNOUNCE_ENTRY Announcement;
  508. BOOLEAN NewElement = FALSE;
  509. ULONG Periodicity;
  510. ULONG ExpirationTime;
  511. NTSTATUS Status;
  512. PPAGED_TRANSPORT PagedTransport;
  513. PTRANSPORT_NAME TransportName = ViewBuffer->TransportName;
  514. PTRANSPORT Transport = TransportName->Transport;
  515. PAGED_CODE();
  516. // DbgBreakPoint();
  517. ASSERT (ViewBuffer->Signature == STRUCTURE_SIGNATURE_VIEW_BUFFER);
  518. //
  519. // If we're not a master browser on this transport, don't process the
  520. // announcement.
  521. // Or no-op for black hole server
  522. //
  523. #ifdef ENABLE_PSEUDO_BROWSER
  524. ASSERT( BowserData.PseudoServerLevel != BROWSER_PSEUDO );
  525. #endif
  526. if (Transport->PagedTransport->Role != Master) {
  527. BowserFreeViewBuffer(ViewBuffer);
  528. BowserDereferenceTransportName(TransportName);
  529. BowserDereferenceTransport(Transport);
  530. return;
  531. }
  532. //
  533. // Convert the computername to unicode.
  534. //
  535. TempUString.Buffer = ProtoEntry.ServerName;
  536. TempUString.MaximumLength = sizeof(ProtoEntry.ServerName);
  537. RtlInitAnsiString(&TempAString, ViewBuffer->ServerName);
  538. Status = RtlOemStringToUnicodeString(&TempUString, &TempAString, FALSE);
  539. if (!NT_SUCCESS(Status)) {
  540. BowserLogIllegalName( Status, TempAString.Buffer, TempAString.Length );
  541. BowserFreeViewBuffer(ViewBuffer);
  542. BowserDereferenceTransportName(TransportName);
  543. BowserDereferenceTransport(Transport);
  544. return;
  545. }
  546. //
  547. // Convert the comment to unicode.
  548. //
  549. TempUString.Buffer = ProtoEntry.ServerComment;
  550. TempUString.MaximumLength = sizeof(ProtoEntry.ServerComment);
  551. RtlInitAnsiString(&TempAString, ViewBuffer->ServerComment);
  552. Status = RtlOemStringToUnicodeString(&TempUString, &TempAString, FALSE);
  553. if (!NT_SUCCESS(Status)) {
  554. BowserLogIllegalName( Status, TempAString.Buffer, TempAString.Length );
  555. BowserFreeViewBuffer(ViewBuffer);
  556. BowserDereferenceTransportName(TransportName);
  557. BowserDereferenceTransport(Transport);
  558. return;
  559. }
  560. ProtoEntry.Signature = STRUCTURE_SIGNATURE_ANNOUNCE_ENTRY;
  561. ProtoEntry.Size = sizeof(ProtoEntry) -
  562. sizeof(ProtoEntry.ServerComment) +
  563. TempUString.Length + sizeof(WCHAR);
  564. ProtoEntry.ServerType = ViewBuffer->ServerType;
  565. ProtoEntry.ServerVersionMajor = ViewBuffer->ServerVersionMajor;
  566. ProtoEntry.ServerVersionMinor = ViewBuffer->ServerVersionMinor;
  567. ProtoEntry.Name = ViewBuffer->TransportName->PagedTransportName->Name;
  568. //
  569. // Initialize the forward and backward link to NULL.
  570. //
  571. ProtoEntry.BackupLink.Flink = NULL;
  572. ProtoEntry.BackupLink.Blink = NULL;
  573. ProtoEntry.ServerPeriodicity = ViewBuffer->ServerPeriodicity;
  574. ProtoEntry.Flags = 0;
  575. ProtoEntry.ServerBrowserVersion = ViewBuffer->ServerBrowserVersion;
  576. PagedTransport = Transport->PagedTransport;
  577. //
  578. // We're done with the view buffer, now free it.
  579. //
  580. BowserFreeViewBuffer(ViewBuffer);
  581. LOCK_ANNOUNCE_DATABASE(Transport);
  582. try {
  583. //
  584. // If this guy isn't a server, then we're supposed to remove this
  585. // guy from our list of servers. We do this because the server (NT,
  586. // WfW, and OS/2) will issue a dummy announcement with the
  587. // appropriate bit turned off when they stop.
  588. //
  589. if (!FlagOn(ProtoEntry.ServerType, SV_TYPE_SERVER)) {
  590. //
  591. // Look up this entry in the table.
  592. //
  593. Announcement = RtlLookupElementGenericTable(&PagedTransport->AnnouncementTable, &ProtoEntry);
  594. //
  595. // The entry wasn't found, so just return, we got rid of it
  596. // some other way (maybe from a timeout scan, etc).
  597. //
  598. if (Announcement == NULL) {
  599. try_return(NOTHING);
  600. }
  601. //
  602. // If this element is on the backup list, remove it from the
  603. // backup list.
  604. //
  605. if (Announcement->BackupLink.Flink != NULL) {
  606. ASSERT (Announcement->BackupLink.Blink != NULL);
  607. RemoveEntryList(&Announcement->BackupLink);
  608. PagedTransport->NumberOfBackupServerListEntries -= 1;
  609. Announcement->BackupLink.Flink = NULL;
  610. Announcement->BackupLink.Blink = NULL;
  611. }
  612. //
  613. // Now delete the element from the announcement table.
  614. //
  615. BowserDereferenceName( Announcement->Name );
  616. if (!RtlDeleteElementGenericTable(&PagedTransport->AnnouncementTable, Announcement)) {
  617. KdPrint(("Unable to delete server element %ws\n", Announcement->ServerName));
  618. }
  619. try_return(NOTHING);
  620. }
  621. Announcement = RtlInsertElementGenericTable(&PagedTransport->AnnouncementTable,
  622. &ProtoEntry, ProtoEntry.Size, &NewElement);
  623. if (Announcement == NULL) {
  624. //
  625. // We couldn't allocate pool for this announcement. Skip it.
  626. //
  627. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  628. try_return(NOTHING);
  629. }
  630. // Indicate the name is referenced by the announce entry we just inserted.
  631. BowserReferenceName( ProtoEntry.Name );
  632. if (!NewElement) {
  633. ULONG NumberOfPromotionAttempts = Announcement->NumberOfPromotionAttempts;
  634. //
  635. // If this announcement was a backup browser, remove it from the
  636. // list of backup browsers.
  637. //
  638. if (Announcement->BackupLink.Flink != NULL) {
  639. ASSERT (Announcement->ServerType & SV_TYPE_BACKUP_BROWSER);
  640. ASSERT (Announcement->BackupLink.Blink != NULL);
  641. RemoveEntryList(&Announcement->BackupLink);
  642. PagedTransport->NumberOfBackupServerListEntries -= 1;
  643. Announcement->BackupLink.Flink = NULL;
  644. Announcement->BackupLink.Blink = NULL;
  645. }
  646. //
  647. // If this is not a new announcement, copy the announcement entry
  648. // with the new information.
  649. //
  650. // The Previous entry no longer references the name
  651. BowserDereferenceName( Announcement->Name );
  652. if ( Announcement->Size >= ProtoEntry.Size ) {
  653. CSHORT TempSize;
  654. TempSize = Announcement->Size;
  655. RtlCopyMemory( Announcement, &ProtoEntry, ProtoEntry.Size );
  656. Announcement->Size = TempSize;
  657. } else {
  658. if (!RtlDeleteElementGenericTable(
  659. &PagedTransport->AnnouncementTable,
  660. Announcement)) {
  661. KdPrint(("Unable to delete server element %ws\n", Announcement->ServerName));
  662. } else {
  663. Announcement = RtlInsertElementGenericTable(
  664. &PagedTransport->AnnouncementTable,
  665. &ProtoEntry,
  666. ProtoEntry.Size,
  667. &NewElement);
  668. if (Announcement == NULL) {
  669. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  670. try_return(NOTHING);
  671. }
  672. ASSERT( NewElement );
  673. }
  674. }
  675. if (ProtoEntry.ServerType & SV_TYPE_BACKUP_BROWSER) {
  676. Announcement->NumberOfPromotionAttempts = 0;
  677. } else {
  678. Announcement->NumberOfPromotionAttempts = NumberOfPromotionAttempts;
  679. }
  680. } else {
  681. //
  682. // This is a new entry. Initialize the number of promotion
  683. // attempts to 0.
  684. //
  685. Announcement->NumberOfPromotionAttempts = 0;
  686. dlog( DPRT_MASTER,
  687. ("%s: %ws: New server: %ws. Periodicity: %ld\n",
  688. Transport->DomainInfo->DomOemDomainName,
  689. PagedTransport->TransportName.Buffer,
  690. Announcement->ServerName,
  691. Announcement->ServerPeriodicity));
  692. //
  693. // If there are too many entries,
  694. // ditch this one.
  695. //
  696. if ( RtlNumberGenericTableElements(&PagedTransport->AnnouncementTable) > BowserMaximumBrowseEntries ) {
  697. dlog( DPRT_MASTER,
  698. ("%s: %ws: New server (Deleted because too many): %ws. Periodicity: %ld\n",
  699. Transport->DomainInfo->DomOemDomainName,
  700. PagedTransport->TransportName.Buffer,
  701. Announcement->ServerName,
  702. Announcement->ServerPeriodicity));
  703. BowserDereferenceName( Announcement->Name );
  704. if (!RtlDeleteElementGenericTable(&PagedTransport->AnnouncementTable, Announcement)) {
  705. KdPrint(("Unable to delete server element %ws\n", Announcement->ServerName));
  706. }
  707. //
  708. // Chaulk it up as a missed announcement
  709. //
  710. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  711. try_return(NOTHING);
  712. }
  713. }
  714. //
  715. // If this new server is a legal backup browser (but not a master
  716. // browser, link it into the announcement database).
  717. //
  718. //
  719. ASSERT (Announcement->BackupLink.Flink == NULL);
  720. ASSERT (Announcement->BackupLink.Blink == NULL);
  721. if (BowserIsLegalBackupBrowser(Announcement, &Transport->DomainInfo->DomUnicodeComputerName)) {
  722. InsertHeadList(&PagedTransport->BackupBrowserList, &Announcement->BackupLink);
  723. PagedTransport->NumberOfBackupServerListEntries += 1;
  724. }
  725. Periodicity = Announcement->ServerPeriodicity;
  726. ExpirationTime = BowserCurrentTime+(Periodicity*HOST_ANNOUNCEMENT_AGE);
  727. Announcement->ExpirationTime = ExpirationTime;
  728. try_exit:NOTHING;
  729. } finally {
  730. UNLOCK_ANNOUNCE_DATABASE(Transport);
  731. BowserDereferenceTransportName(TransportName);
  732. BowserDereferenceTransport(Transport);
  733. }
  734. return;
  735. }
  736. VOID
  737. BowserProcessDomainAnnouncement(
  738. IN PVOID Context
  739. )
  740. /*++
  741. Routine Description:
  742. This routine will put a server announcement into the server announcement
  743. table
  744. Arguments:
  745. IN PWORK_HEADER Header - Supplies a pointer to a work header in a view buffer
  746. Return Value:
  747. None.
  748. --*/
  749. {
  750. PVIEW_BUFFER ViewBuffer = Context;
  751. ANNOUNCE_ENTRY ProtoEntry;
  752. UNICODE_STRING TempUString;
  753. OEM_STRING TempAString;
  754. PANNOUNCE_ENTRY Announcement;
  755. BOOLEAN NewElement = FALSE;
  756. ULONG Periodicity;
  757. ULONG ExpirationTime;
  758. NTSTATUS Status;
  759. PPAGED_TRANSPORT PagedTransport;
  760. PTRANSPORT_NAME TransportName = ViewBuffer->TransportName;
  761. PTRANSPORT Transport = TransportName->Transport;
  762. PAGED_CODE();
  763. // DbgBreakPoint();
  764. ASSERT (ViewBuffer->Signature == STRUCTURE_SIGNATURE_VIEW_BUFFER);
  765. //
  766. // If we're not a master browser on this transport, don't process the
  767. // announcement.
  768. // Or no-op for black hole server
  769. //
  770. #ifdef ENABLE_PSEUDO_BROWSER
  771. ASSERT( BowserData.PseudoServerLevel != BROWSER_PSEUDO );
  772. #endif
  773. if (ViewBuffer->TransportName->Transport->PagedTransport->Role != Master) {
  774. BowserFreeViewBuffer(ViewBuffer);
  775. BowserDereferenceTransportName(TransportName);
  776. BowserDereferenceTransport(Transport);
  777. return;
  778. }
  779. //
  780. // Convert the computername to unicode.
  781. //
  782. TempUString.Buffer = ProtoEntry.ServerName;
  783. TempUString.MaximumLength = sizeof(ProtoEntry.ServerName);
  784. RtlInitAnsiString(&TempAString, ViewBuffer->ServerName);
  785. Status = RtlOemStringToUnicodeString(&TempUString, &TempAString, FALSE);
  786. if (!NT_SUCCESS(Status)) {
  787. BowserFreeViewBuffer(ViewBuffer);
  788. BowserDereferenceTransportName(TransportName);
  789. BowserDereferenceTransport(Transport);
  790. return;
  791. }
  792. //
  793. // Convert the comment to unicode.
  794. //
  795. TempUString.Buffer = ProtoEntry.ServerComment;
  796. TempUString.MaximumLength = sizeof(ProtoEntry.ServerComment);
  797. RtlInitAnsiString(&TempAString, ViewBuffer->ServerComment);
  798. Status = RtlOemStringToUnicodeString(&TempUString, &TempAString, FALSE);
  799. if (!NT_SUCCESS(Status)) {
  800. BowserFreeViewBuffer(ViewBuffer);
  801. BowserDereferenceTransportName(TransportName);
  802. BowserDereferenceTransport(Transport);
  803. return;
  804. }
  805. ProtoEntry.Signature = STRUCTURE_SIGNATURE_ANNOUNCE_ENTRY;
  806. ProtoEntry.Size = sizeof(ProtoEntry) -
  807. sizeof(ProtoEntry.ServerComment) +
  808. TempUString.Length + sizeof(WCHAR);
  809. ProtoEntry.ServerType = ViewBuffer->ServerType;
  810. ProtoEntry.ServerVersionMajor = ViewBuffer->ServerVersionMajor;
  811. ProtoEntry.ServerVersionMinor = ViewBuffer->ServerVersionMinor;
  812. ProtoEntry.Name = ViewBuffer->TransportName->PagedTransportName->Name;
  813. ProtoEntry.ServerPeriodicity = ViewBuffer->ServerPeriodicity;
  814. ProtoEntry.BackupLink.Flink = NULL;
  815. ProtoEntry.BackupLink.Blink = NULL;
  816. ProtoEntry.Flags = 0;
  817. PagedTransport = Transport->PagedTransport;
  818. //
  819. // We're done with the view buffer, now free it.
  820. //
  821. BowserFreeViewBuffer(ViewBuffer);
  822. LOCK_ANNOUNCE_DATABASE(Transport);
  823. try {
  824. Announcement = RtlInsertElementGenericTable(&PagedTransport->DomainTable,
  825. &ProtoEntry, ProtoEntry.Size, &NewElement);
  826. if (Announcement == NULL) {
  827. //
  828. // We couldn't allocate pool for this announcement. Skip it.
  829. //
  830. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  831. try_return(NOTHING);
  832. }
  833. // Indicate the name is referenced by the announce entry we just inserted.
  834. BowserReferenceName( ProtoEntry.Name );
  835. if (!NewElement) {
  836. //
  837. // If this is not a new announcement, copy the announcement entry
  838. // with the new information.
  839. //
  840. // The Previous entry no longer references the name
  841. BowserDereferenceName( Announcement->Name );
  842. if ( Announcement->Size >= ProtoEntry.Size ) {
  843. CSHORT TempSize;
  844. TempSize = Announcement->Size;
  845. RtlCopyMemory( Announcement, &ProtoEntry, ProtoEntry.Size );
  846. Announcement->Size = TempSize;
  847. } else {
  848. if (!RtlDeleteElementGenericTable(
  849. &PagedTransport->DomainTable,
  850. Announcement)) {
  851. KdPrint(("Unable to delete server element %ws\n", Announcement->ServerName));
  852. } else {
  853. Announcement = RtlInsertElementGenericTable(
  854. &PagedTransport->DomainTable,
  855. &ProtoEntry,
  856. ProtoEntry.Size,
  857. &NewElement);
  858. if (Announcement == NULL) {
  859. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  860. try_return(NOTHING);
  861. }
  862. ASSERT( NewElement );
  863. }
  864. }
  865. dlog( DPRT_MASTER,
  866. ("%s: %ws Domain:%ws P: %ld\n",
  867. Transport->DomainInfo->DomOemDomainName,
  868. PagedTransport->TransportName.Buffer,
  869. Announcement->ServerName,
  870. Announcement->ServerPeriodicity));
  871. } else {
  872. dlog( DPRT_MASTER,
  873. ("%s: %ws New domain:%ws P: %ld\n",
  874. Transport->DomainInfo->DomOemDomainName,
  875. PagedTransport->TransportName.Buffer,
  876. Announcement->ServerName,
  877. Announcement->ServerPeriodicity));
  878. //
  879. // If there are too many entries,
  880. // ditch this one.
  881. //
  882. if ( RtlNumberGenericTableElements(&PagedTransport->DomainTable) > BowserMaximumBrowseEntries ) {
  883. dlog( DPRT_MASTER,
  884. ("%s: %ws New domain (deleted because too many):%ws P: %ld\n",
  885. Transport->DomainInfo->DomOemDomainName,
  886. PagedTransport->TransportName.Buffer,
  887. Announcement->ServerName,
  888. Announcement->ServerPeriodicity));
  889. BowserDereferenceName( Announcement->Name );
  890. if (!RtlDeleteElementGenericTable(&PagedTransport->DomainTable, Announcement)) {
  891. // KdPrint(("Unable to delete element %ws\n", Announcement->ServerName));
  892. }
  893. //
  894. // Chaulk it up as a missed announcement
  895. //
  896. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  897. try_return(NOTHING);
  898. }
  899. }
  900. Periodicity = Announcement->ServerPeriodicity;
  901. ExpirationTime = BowserCurrentTime+(Periodicity*HOST_ANNOUNCEMENT_AGE);
  902. Announcement->ExpirationTime = ExpirationTime;
  903. try_exit:NOTHING;
  904. } finally {
  905. UNLOCK_ANNOUNCE_DATABASE(Transport);
  906. BowserDereferenceTransportName(TransportName);
  907. BowserDereferenceTransport(Transport);
  908. }
  909. return;
  910. }
  911. VOID
  912. BowserAgeServerAnnouncements(
  913. VOID
  914. )
  915. /*++
  916. Routine Description:
  917. This routine will age server announcements in the server announce table.
  918. Arguments:
  919. None.
  920. Return Value:
  921. None.
  922. --*/
  923. {
  924. PAGED_CODE();
  925. BowserForEachTransport(AgeServerAnnouncements, NULL);
  926. }
  927. INLINE
  928. BOOLEAN
  929. BowserIsValidPotentialBrowser(
  930. IN PTRANSPORT Transport,
  931. IN PANNOUNCE_ENTRY Announcement
  932. )
  933. {
  934. if (Announcement->Name->NameType != MasterBrowser) {
  935. return FALSE;
  936. }
  937. //
  938. // If this guy is a potential browser, and is not
  939. // currently a backup or master browser, promote
  940. // him to a browser.
  941. //
  942. if (!(Announcement->ServerType & SV_TYPE_POTENTIAL_BROWSER)) {
  943. return FALSE;
  944. }
  945. //
  946. // And this guy isn't either a master or backup browser already
  947. //
  948. if (Announcement->ServerType & (SV_TYPE_BACKUP_BROWSER | SV_TYPE_MASTER_BROWSER)) {
  949. return FALSE;
  950. }
  951. //
  952. // If this guy is running a current version of the browser.
  953. //
  954. if (Announcement->ServerBrowserVersion < (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR) {
  955. return FALSE;
  956. }
  957. //
  958. // If this machine is ourselves, and we've not yet announced ourselves as
  959. // a master, don't promote ourselves.
  960. //
  961. if (!_wcsicmp(Announcement->ServerName, Transport->DomainInfo->DomUnicodeComputerNameBuffer)) {
  962. return FALSE;
  963. }
  964. //
  965. // If we've tried to promote this machine more than # of ignored promotions,
  966. // we don't want to consider it either.
  967. //
  968. if (Announcement->NumberOfPromotionAttempts >= NUMBER_IGNORED_PROMOTIONS) {
  969. return FALSE;
  970. }
  971. return TRUE;
  972. }
  973. INLINE
  974. BOOLEAN
  975. BowserIsValidBackupBrowser(
  976. IN PTRANSPORT Transport,
  977. IN PANNOUNCE_ENTRY Announcement
  978. )
  979. /*++
  980. Routine Description:
  981. This routine determines if a server is eligable for demotion.
  982. Arguments:
  983. PTRANSPORT Transport - Transport we are scanning.
  984. PANNOUNCE_ENTRY Announcement - Announce entry for server to check.
  985. Return Value:
  986. BOOLEAN - True if browser is eligable for demotion
  987. --*/
  988. {
  989. PUNICODE_STRING PagedComputerName = &Transport->DomainInfo->DomUnicodeComputerName;
  990. //
  991. // If the name came in on the master browser name
  992. //
  993. if (Announcement->Name->NameType != MasterBrowser) {
  994. return FALSE;
  995. }
  996. //
  997. // And this guy is currently a backup browser,
  998. //
  999. if (!(Announcement->ServerType & SV_TYPE_BACKUP_BROWSER)) {
  1000. return FALSE;
  1001. }
  1002. //
  1003. // And this guy was a promoted browser,
  1004. //
  1005. if (!(Announcement->ServerType & SV_TYPE_POTENTIAL_BROWSER)) {
  1006. return FALSE;
  1007. }
  1008. //
  1009. // And this guy isn't an NTAS machine,
  1010. //
  1011. if (Announcement->ServerType & (SV_TYPE_DOMAIN_BAKCTRL | SV_TYPE_DOMAIN_CTRL)) {
  1012. return FALSE;
  1013. }
  1014. //
  1015. // And this isn't ourselves.
  1016. //
  1017. if (RtlCompareMemory(Announcement->ServerName,
  1018. PagedComputerName->Buffer,
  1019. PagedComputerName->Length) == PagedComputerName->Length) {
  1020. return FALSE;
  1021. }
  1022. //
  1023. // Then it's a valid backup browser to demote.
  1024. //
  1025. return TRUE;
  1026. }
  1027. NTSTATUS
  1028. AgeServerAnnouncements(
  1029. PTRANSPORT Transport,
  1030. PVOID Context
  1031. )
  1032. /*++
  1033. Routine Description:
  1034. This routine is the worker routine for BowserAgeServerAnnouncements.
  1035. It is called for each of the serviced transports in the bowser and
  1036. ages the servers received on each transport.
  1037. Arguments:
  1038. None.
  1039. Return Value:
  1040. None.
  1041. --*/
  1042. {
  1043. PANNOUNCE_ENTRY Announcement;
  1044. ULONG BackupsNeeded;
  1045. ULONG BackupsFound;
  1046. ULONG NumberOfConfiguredBrowsers;
  1047. PVOID ResumeKey = NULL;
  1048. PVOID PreviousResumeKey = NULL;
  1049. ULONG NumberOfServersDeleted = 0;
  1050. ULONG NumberOfDomainsDeleted = 0;
  1051. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  1052. PAGED_CODE();
  1053. LOCK_TRANSPORT(Transport);
  1054. //
  1055. // If we're not a master, don't bother.
  1056. //
  1057. if (PagedTransport->Role != Master) {
  1058. UNLOCK_TRANSPORT(Transport);
  1059. return STATUS_SUCCESS;
  1060. }
  1061. UNLOCK_TRANSPORT(Transport);
  1062. LOCK_ANNOUNCE_DATABASE(Transport);
  1063. try {
  1064. BackupsFound = 0;
  1065. NumberOfConfiguredBrowsers = 0;
  1066. dlog(DPRT_MASTER,
  1067. ("%s: %ws: Scavenge Servers:",
  1068. Transport->DomainInfo->DomOemDomainName,
  1069. PagedTransport->TransportName.Buffer));
  1070. for (Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ;
  1071. Announcement != NULL ;
  1072. Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ) {
  1073. if (BowserCurrentTime > Announcement->ExpirationTime) {
  1074. if (Announcement->Name->NameType != OtherDomain) {
  1075. if (Announcement->ServerType & SV_TYPE_BACKUP_BROWSER) {
  1076. //
  1077. // This guy was a backup - indicate that we're not tracking
  1078. // him any more.
  1079. //
  1080. PagedTransport->NumberOfBrowserServers -= 1;
  1081. }
  1082. }
  1083. dlog(DPRT_MASTER, ("%ws ", Announcement->ServerName));
  1084. // Continue the search from where we found this entry.
  1085. ResumeKey = PreviousResumeKey;
  1086. BackupsFound = 0;
  1087. NumberOfConfiguredBrowsers = 0;
  1088. NumberOfServersDeleted += 1;
  1089. //
  1090. // If this announcement was a backup browser, remove it from the
  1091. // list of backup browsers.
  1092. //
  1093. if (Announcement->BackupLink.Flink != NULL) {
  1094. ASSERT (Announcement->BackupLink.Blink != NULL);
  1095. ASSERT (Announcement->ServerType & SV_TYPE_BACKUP_BROWSER);
  1096. RemoveEntryList(&Announcement->BackupLink);
  1097. PagedTransport->NumberOfBackupServerListEntries -= 1;
  1098. Announcement->BackupLink.Flink = NULL;
  1099. Announcement->BackupLink.Blink = NULL;
  1100. }
  1101. BowserDereferenceName( Announcement->Name );
  1102. if (!RtlDeleteElementGenericTable(&PagedTransport->AnnouncementTable, Announcement)) {
  1103. KdPrint(("Unable to delete server element %ws\n", Announcement->ServerName));
  1104. }
  1105. } else {
  1106. if (BowserIsLegalBackupBrowser(Announcement, &Transport->DomainInfo->DomUnicodeComputerName )) {
  1107. //
  1108. // This announcement should be on the backup list.
  1109. //
  1110. ASSERT (Announcement->BackupLink.Flink != NULL);
  1111. ASSERT (Announcement->BackupLink.Blink != NULL);
  1112. //
  1113. // Found a backup that has not timed out.
  1114. //
  1115. BackupsFound++;
  1116. }
  1117. //
  1118. // If this machine is a DC or BDC and is an NT machine, then
  1119. // assume it's a Lanman/NT machine.
  1120. //
  1121. if (Announcement->ServerType & (SV_TYPE_DOMAIN_CTRL|SV_TYPE_DOMAIN_BAKCTRL)) {
  1122. //
  1123. // If this DC is an NT DC, it is running the browser
  1124. // service, and it is NOT the master, we consider it a
  1125. // configured browser.
  1126. //
  1127. if ((Announcement->ServerType & SV_TYPE_NT)
  1128. &&
  1129. (Announcement->ServerType & SV_TYPE_BACKUP_BROWSER)
  1130. &&
  1131. !(Announcement->ServerType & SV_TYPE_MASTER_BROWSER)) {
  1132. NumberOfConfiguredBrowsers += 1;
  1133. }
  1134. } else {
  1135. //
  1136. // If this guy isn't a DC, then if it is a backup browser
  1137. // but not a potential browser, then it's a configured
  1138. // browser (non-configured browsers get promoted).
  1139. //
  1140. if ((Announcement->ServerType & SV_TYPE_BACKUP_BROWSER) &&
  1141. !(Announcement->ServerType & SV_TYPE_POTENTIAL_BROWSER)) {
  1142. NumberOfConfiguredBrowsers += 1;
  1143. }
  1144. }
  1145. //
  1146. // Remember where this valid entry was found.
  1147. //
  1148. PreviousResumeKey = ResumeKey;
  1149. }
  1150. }
  1151. dlog(DPRT_MASTER, ("\n"));
  1152. //
  1153. // If we've found enough configured backup servers, we don't need to
  1154. // promote any more backups.
  1155. //
  1156. // Also don't attempt a promotion scan for the first MASTER_TIME_UP
  1157. // milliseconds (15 minutes) we are the master.
  1158. //
  1159. if ((BowserTimeUp() - PagedTransport->TimeMaster) > MASTER_TIME_UP) {
  1160. //
  1161. // If there are fewer than the minimum configured browsers,
  1162. // rely only on the configured browsers.
  1163. //
  1164. if (NumberOfConfiguredBrowsers < BowserMinimumConfiguredBrowsers) {
  1165. //
  1166. // We will need 1 backup for every SERVERS_PER_BACKUP servers in the domain.
  1167. //
  1168. PagedTransport->NumberOfBrowserServers = BackupsFound;
  1169. BackupsNeeded = (RtlNumberGenericTableElements(&PagedTransport->AnnouncementTable) + (SERVERS_PER_BACKUP-1)) / SERVERS_PER_BACKUP;
  1170. dlog(DPRT_MASTER,
  1171. ("%s: %ws: We need %lx backups, and have %lx.\n",
  1172. Transport->DomainInfo->DomOemDomainName,
  1173. PagedTransport->TransportName.Buffer,
  1174. BackupsNeeded,
  1175. PagedTransport->NumberOfBrowserServers));
  1176. if (PagedTransport->NumberOfBrowserServers < BackupsNeeded) {
  1177. //
  1178. // We only need this many more backup browsers.
  1179. //
  1180. BackupsNeeded = BackupsNeeded - PagedTransport->NumberOfBrowserServers;
  1181. //
  1182. // We need to promote a machine to a backup if possible.
  1183. //
  1184. ResumeKey = NULL;
  1185. for (Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ;
  1186. Announcement != NULL ;
  1187. Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ) {
  1188. //
  1189. // If this announcement came from the master browser name
  1190. //
  1191. if (BowserIsValidPotentialBrowser(Transport, Announcement)) {
  1192. dlog(DPRT_MASTER,
  1193. ("%s: %ws: Found browser to promote: %ws.\n",
  1194. Transport->DomainInfo->DomOemDomainName,
  1195. PagedTransport->TransportName.Buffer,
  1196. Announcement->ServerName));
  1197. BowserPromoteToBackup(Transport, Announcement->ServerName);
  1198. //
  1199. // Flag that we've attempted to promote this
  1200. // browser.
  1201. //
  1202. Announcement->NumberOfPromotionAttempts += 1;
  1203. BackupsNeeded -= 1;
  1204. //
  1205. // If we've promoted all the people we need to promote,
  1206. // we're done, and can stop looping now.
  1207. //
  1208. if (BackupsNeeded == 0) {
  1209. break;
  1210. }
  1211. } else if ((Announcement->ServerType & SV_TYPE_BACKUP_BROWSER) &&
  1212. (Announcement->ServerBrowserVersion < (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR)) {
  1213. //
  1214. // If this guy is out of revision, shut him down.
  1215. //
  1216. BowserShutdownRemoteBrowser(Transport, Announcement->ServerName);
  1217. }
  1218. }
  1219. }
  1220. } else {
  1221. //
  1222. // If we have enough configured browsers that we won't have
  1223. // any more backups, then demote all the non-configured
  1224. // browsers.
  1225. //
  1226. ResumeKey = NULL;
  1227. for (Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ;
  1228. Announcement != NULL ;
  1229. Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->AnnouncementTable, &ResumeKey) ) {
  1230. //
  1231. // If this machine is a valid machine to demote, do it.
  1232. //
  1233. if (BowserIsValidBackupBrowser(Transport, Announcement)) {
  1234. //
  1235. // This machine shouldn't be a backup, since we
  1236. // already have enough machines to be backups.
  1237. // Demote this backup browser.
  1238. //
  1239. BowserShutdownRemoteBrowser(Transport, Announcement->ServerName);
  1240. }
  1241. }
  1242. }
  1243. }
  1244. ResumeKey = NULL;
  1245. PreviousResumeKey = NULL;
  1246. dlog(DPRT_MASTER,
  1247. ("%s: %ws: Scavenge Domains:",
  1248. Transport->DomainInfo->DomOemDomainName,
  1249. PagedTransport->TransportName.Buffer));
  1250. for (Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->DomainTable, &ResumeKey) ;
  1251. Announcement != NULL ;
  1252. Announcement = RtlEnumerateGenericTableWithoutSplaying(&PagedTransport->DomainTable, &ResumeKey) ) {
  1253. if (BowserCurrentTime > Announcement->ExpirationTime) {
  1254. NumberOfDomainsDeleted += 1;
  1255. // Continue the search from where we found this entry.
  1256. ResumeKey = PreviousResumeKey;
  1257. dlog(DPRT_MASTER, ("%ws ", Announcement->ServerName));
  1258. BowserDereferenceName( Announcement->Name );
  1259. if (!RtlDeleteElementGenericTable(&PagedTransport->DomainTable, Announcement)) {
  1260. // KdPrint(("Unable to delete element %ws\n", Announcement->ServerName));
  1261. }
  1262. } else {
  1263. //
  1264. // Remember where this valid entry was found.
  1265. //
  1266. PreviousResumeKey = ResumeKey;
  1267. }
  1268. }
  1269. dlog(DPRT_MASTER, ("\n", Announcement->ServerName));
  1270. } finally {
  1271. #if DBG
  1272. //
  1273. // Log an indication that we might have deleted too many servers.
  1274. //
  1275. if (NumberOfServersDeleted > BowserServerDeletionThreshold) {
  1276. dlog(DPRT_MASTER,
  1277. ("%s: %ws: Aged out %ld servers.\n",
  1278. Transport->DomainInfo->DomOemDomainName,
  1279. PagedTransport->TransportName.Buffer,
  1280. NumberOfServersDeleted ));
  1281. }
  1282. if (NumberOfDomainsDeleted > BowserDomainDeletionThreshold) {
  1283. dlog(DPRT_MASTER,
  1284. ("%s: %ws: Aged out %ld domains.\n",
  1285. Transport->DomainInfo->DomOemDomainName,
  1286. PagedTransport->TransportName.Buffer,
  1287. NumberOfDomainsDeleted ));
  1288. }
  1289. #endif
  1290. UNLOCK_ANNOUNCE_DATABASE(Transport);
  1291. }
  1292. UNREFERENCED_PARAMETER(Context);
  1293. return STATUS_SUCCESS;
  1294. }
  1295. VOID
  1296. BowserShutdownRemoteBrowser(
  1297. IN PTRANSPORT Transport,
  1298. IN PWSTR ServerName
  1299. )
  1300. /*++
  1301. Routine Description:
  1302. This routine will send a request to the remote machine to make it become
  1303. a browser server.
  1304. Arguments:
  1305. None.
  1306. Return Value:
  1307. None.
  1308. --*/
  1309. {
  1310. RESET_STATE ResetStateRequest;
  1311. UNICODE_STRING Name;
  1312. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  1313. PAGED_CODE();
  1314. dlog(DPRT_BROWSER,
  1315. ("%s: %ws: Demoting server %ws\n",
  1316. Transport->DomainInfo->DomOemDomainName,
  1317. PagedTransport->TransportName.Buffer,
  1318. ServerName ));
  1319. RtlInitUnicodeString(&Name, ServerName);
  1320. ResetStateRequest.Type = ResetBrowserState;
  1321. ResetStateRequest.ResetStateRequest.Options = RESET_STATE_CLEAR_ALL;
  1322. //
  1323. // Send this reset state (tickle) packet to the computer specified.
  1324. //
  1325. BowserSendSecondClassMailslot(Transport,
  1326. &Name,
  1327. ComputerName,
  1328. &ResetStateRequest,
  1329. sizeof(ResetStateRequest),
  1330. TRUE,
  1331. MAILSLOT_BROWSER_NAME,
  1332. NULL);
  1333. }
  1334. VOID
  1335. BowserPromoteToBackup(
  1336. IN PTRANSPORT Transport,
  1337. IN PWSTR ServerName
  1338. )
  1339. /*++
  1340. Routine Description:
  1341. This routine will send a request to the remote machine to make it become
  1342. a browser server.
  1343. Arguments:
  1344. None.
  1345. Return Value:
  1346. None.
  1347. --*/
  1348. {
  1349. UCHAR Buffer[LM20_CNLEN+1+sizeof(BECOME_BACKUP)];
  1350. PBECOME_BACKUP BecomeBackup = (PBECOME_BACKUP)Buffer;
  1351. UNICODE_STRING UString;
  1352. OEM_STRING AString;
  1353. NTSTATUS Status;
  1354. ULONG BufferSize;
  1355. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  1356. PAGED_CODE();
  1357. dlog(DPRT_BROWSER,
  1358. ("%s: %ws: Promoting server %ws to backup on %wZ\n",
  1359. Transport->DomainInfo->DomOemDomainName,
  1360. PagedTransport->TransportName.Buffer,
  1361. ServerName ));
  1362. BecomeBackup->Type = BecomeBackupServer;
  1363. RtlInitUnicodeString(&UString, ServerName);
  1364. AString.Buffer = BecomeBackup->BecomeBackup.BrowserToPromote;
  1365. AString.MaximumLength = (USHORT)(sizeof(Buffer)-FIELD_OFFSET(BECOME_BACKUP, BecomeBackup.BrowserToPromote));
  1366. Status = RtlUnicodeStringToOemString(&AString, &UString, FALSE);
  1367. if (!NT_SUCCESS(Status)) {
  1368. BowserLogIllegalName( Status, UString.Buffer, UString.Length );
  1369. return;
  1370. }
  1371. BufferSize = FIELD_OFFSET(BECOME_BACKUP, BecomeBackup.BrowserToPromote) +
  1372. AString.Length + sizeof(CHAR);
  1373. BowserSendSecondClassMailslot(Transport,
  1374. NULL,
  1375. BrowserElection,
  1376. BecomeBackup,
  1377. BufferSize,
  1378. TRUE,
  1379. MAILSLOT_BROWSER_NAME,
  1380. NULL);
  1381. }
  1382. NTSTATUS
  1383. BowserEnumerateServers(
  1384. IN ULONG Level,
  1385. IN PLUID LogonId OPTIONAL,
  1386. IN OUT PULONG ResumeKey,
  1387. IN ULONG ServerTypeMask,
  1388. IN PUNICODE_STRING TransportName OPTIONAL,
  1389. IN PUNICODE_STRING EmulatedDomainName,
  1390. IN PUNICODE_STRING DomainName OPTIONAL,
  1391. OUT PVOID OutputBuffer,
  1392. IN ULONG OutputBufferSize,
  1393. OUT PULONG EntriesRead,
  1394. OUT PULONG TotalEntries,
  1395. OUT PULONG TotalBytesNeeded,
  1396. IN ULONG_PTR OutputBufferDisplacement
  1397. )
  1398. /*++
  1399. Routine Description:
  1400. This routine will enumerate the servers in the bowsers current announcement
  1401. table.
  1402. Arguments:
  1403. Level - The level of information to return
  1404. LogonId - An optional logon id to indicate which user requested this info
  1405. ResumeKey - The resume key (we return all entries after this one)
  1406. ServerTypeMask - Mask of servers to return.
  1407. TransportName - Name of the transport to enumerated on
  1408. EmulatedDomainName - Name of the domain being emulated.
  1409. DomainName OPTIONAL - Domain to filter (all if not specified)
  1410. OutputBuffer - Buffer to fill with server info.
  1411. OutputBufferSize - Filled in with size of buffer.
  1412. EntriesRead - Filled in with the # of entries returned.
  1413. TotalEntries - Filled in with the total # of entries.
  1414. TotalBytesNeeded - Filled in with the # of bytes needed.
  1415. Return Value:
  1416. None.
  1417. --*/
  1418. {
  1419. LPTSTR OutputBufferEnd;
  1420. NTSTATUS Status;
  1421. ENUM_SERVERS_CONTEXT Context;
  1422. PVOID OriginalOutputBuffer = OutputBuffer;
  1423. PAGED_CODE();
  1424. OutputBuffer = ALLOCATE_POOL(PagedPool,OutputBufferSize,POOL_SERVER_ENUM_BUFFER);
  1425. if (OutputBuffer == NULL) {
  1426. return(STATUS_INSUFFICIENT_RESOURCES);
  1427. }
  1428. OutputBufferEnd = (LPTSTR)((PCHAR)OutputBuffer+OutputBufferSize);
  1429. Context.EntriesRead = 0;
  1430. Context.TotalEntries = 0;
  1431. Context.TotalBytesNeeded = 0;
  1432. Context.Level = Level;
  1433. Context.LogonId = LogonId;
  1434. Context.OriginalResumeKey = *ResumeKey;
  1435. Context.ServerTypeMask = ServerTypeMask;
  1436. Context.DomainName = DomainName;
  1437. Context.OutputBufferSize = OutputBufferSize;
  1438. Context.OutputBuffer = OutputBuffer;
  1439. Context.OutputBufferDisplacement =
  1440. ((PCHAR)OutputBuffer - ((PCHAR)OriginalOutputBuffer - OutputBufferDisplacement));
  1441. Context.OutputBufferEnd = OutputBufferEnd;
  1442. dlog(DPRT_SRVENUM, ("Enumerate Servers: Buffer: %lx, BufferSize: %lx, BufferEnd: %lx\n",
  1443. OutputBuffer, OutputBufferSize, OutputBufferEnd));
  1444. if (TransportName == NULL) {
  1445. Status = STATUS_INVALID_PARAMETER;
  1446. } else {
  1447. PTRANSPORT Transport;
  1448. Transport = BowserFindTransport(TransportName, EmulatedDomainName );
  1449. dprintf(DPRT_REF, ("Called Find transport %lx from BowserEnumerateServers.\n", Transport));
  1450. if (Transport == NULL) {
  1451. return(STATUS_OBJECT_NAME_NOT_FOUND);
  1452. }
  1453. dlog(DPRT_SRVENUM,
  1454. ("%s: %ws: Enumerate Servers: Buffer: %lx, BufferSize: %lx, BufferEnd: %lx\n",
  1455. Transport->DomainInfo->DomOemDomainName,
  1456. Transport->PagedTransport->TransportName.Buffer,
  1457. OutputBuffer, OutputBufferSize, OutputBufferEnd));
  1458. Status = EnumerateServersWorker(Transport, &Context);
  1459. //
  1460. // Dereference the transport..
  1461. BowserDereferenceTransport(Transport);
  1462. }
  1463. *EntriesRead = Context.EntriesRead;
  1464. *TotalEntries = Context.TotalEntries;
  1465. *TotalBytesNeeded = Context.TotalBytesNeeded;
  1466. *ResumeKey = Context.ResumeKey;
  1467. try {
  1468. RtlCopyMemory(OriginalOutputBuffer,OutputBuffer,OutputBufferSize);
  1469. } except (BR_EXCEPTION) {
  1470. FREE_POOL(OutputBuffer);
  1471. return(GetExceptionCode());
  1472. }
  1473. FREE_POOL(OutputBuffer);
  1474. dlog(DPRT_SRVENUM, ("TotalEntries: %lx EntriesRead: %lx, TotalBytesNeeded: %lx\n", *TotalEntries, *EntriesRead, *TotalBytesNeeded));
  1475. if (*EntriesRead == *TotalEntries) {
  1476. return STATUS_SUCCESS;
  1477. } else {
  1478. return STATUS_MORE_ENTRIES;
  1479. }
  1480. }
  1481. NTSTATUS
  1482. EnumerateServersWorker(
  1483. IN PTRANSPORT Transport,
  1484. IN OUT PVOID Ctx
  1485. )
  1486. /*++
  1487. Routine Description:
  1488. This routine is the worker routine for GowserGetAnnounceTableSize.
  1489. It is called for each of the serviced transports in the bowser and
  1490. returns the size needed to enumerate the servers received on each transport.
  1491. Arguments:
  1492. None.
  1493. Return Value:
  1494. None.
  1495. --*/
  1496. {
  1497. PENUM_SERVERS_CONTEXT Context = Ctx;
  1498. PANNOUNCE_ENTRY Announcement;
  1499. NTSTATUS Status;
  1500. ULONG AnnouncementIndex;
  1501. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  1502. PUNICODE_STRING DomainName;
  1503. PAGED_CODE();
  1504. LOCK_ANNOUNCE_DATABASE_SHARED(Transport);
  1505. if (Context->DomainName == NULL) {
  1506. DomainName = &Transport->DomainInfo->DomUnicodeDomainName;
  1507. } else {
  1508. DomainName = Context->DomainName;
  1509. }
  1510. try {
  1511. PVOID ResumeKey = NULL;
  1512. for (AnnouncementIndex = 1,
  1513. Announcement = RtlEnumerateGenericTableWithoutSplaying((Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ?
  1514. &PagedTransport->DomainTable :
  1515. &PagedTransport->AnnouncementTable),
  1516. &ResumeKey) ;
  1517. Announcement != NULL ;
  1518. AnnouncementIndex += 1,
  1519. Announcement = RtlEnumerateGenericTableWithoutSplaying((Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ?
  1520. &PagedTransport->DomainTable :
  1521. &PagedTransport->AnnouncementTable),
  1522. &ResumeKey) ) {
  1523. //
  1524. // If the type mask matches, check the domain supplied to make sure
  1525. // that this announcement is acceptable to the caller.
  1526. //
  1527. //
  1528. // If we are doing a domain enumeration, we want to use domains
  1529. // received on all names, otherwise we want to use names only
  1530. // seen on the domain being queried.
  1531. //
  1532. if ((AnnouncementIndex > Context->OriginalResumeKey) &&
  1533. ((Announcement->ServerType & Context->ServerTypeMask) != 0) &&
  1534. (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ||
  1535. RtlEqualUnicodeString(DomainName, &Announcement->Name->Name, TRUE))
  1536. ) {
  1537. try {
  1538. //
  1539. // We have an entry we can return to the user.
  1540. //
  1541. Context->TotalEntries += 1;
  1542. if (PackServerAnnouncement(Context->Level,
  1543. Context->ServerTypeMask,
  1544. (LPTSTR *)&Context->OutputBuffer,
  1545. (LPTSTR *)&Context->OutputBufferEnd,
  1546. Context->OutputBufferDisplacement,
  1547. Announcement,
  1548. &Context->TotalBytesNeeded)) {
  1549. Context->EntriesRead += 1;
  1550. //
  1551. // Set the resume key in the structure to point to
  1552. // the last entry we returned.
  1553. //
  1554. Context->ResumeKey = AnnouncementIndex;
  1555. }
  1556. } except (BR_EXCEPTION) {
  1557. try_return(Status = GetExceptionCode());
  1558. }
  1559. #if 0
  1560. } else {
  1561. if (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ||
  1562. Context->ServerTypeMask == SV_TYPE_ALL ) {
  1563. KdPrint(("Skipping Announce entry %ws. Index: %ld, ResumeKey: %ld, Domain: %wZ, %wZ\n",
  1564. Announcement->ServerName,
  1565. AnnouncementIndex,
  1566. Context->OriginalResumeKey,
  1567. &Announcement->Name->Name,
  1568. DomainName));
  1569. }
  1570. #endif
  1571. }
  1572. }
  1573. try_return(Status = STATUS_SUCCESS);
  1574. try_exit: {
  1575. #if 0
  1576. if (Context->ServerTypeMask == SV_TYPE_ALL) {
  1577. if (AnnouncementIndex-1 != RtlNumberGenericTableElements(&Transport->AnnouncementTable) ) {
  1578. KdPrint(("Bowser: Announcement index != Number of elements in table (%ld, %ld) on transport %wZ\n", AnnouncementIndex-1, RtlNumberGenericTableElements(&Transport->AnnouncementTable), &Transport->TransportName ));
  1579. }
  1580. } else if (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM) {
  1581. if (AnnouncementIndex-1 != RtlNumberGenericTableElements(&Transport->DomainTable) ) {
  1582. KdPrint(("Bowser: Announcement index != Number of domains in table (%ld, %ld) on transport %wZ\n", AnnouncementIndex-1, RtlNumberGenericTableElements(&Transport->DomainTable), &Transport->TransportName ));
  1583. }
  1584. }
  1585. if (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM) {
  1586. if (Context->TotalEntries != RtlNumberGenericTableElements(&Transport->DomainTable)) {
  1587. KdPrint(("Bowser: Returned EntriesRead == %ld, But %ld entries in table on transport %wZ\n", Context->TotalEntries, RtlNumberGenericTableElements(&Transport->DomainTable), &Transport->TransportName ));
  1588. }
  1589. } else if (Context->ServerTypeMask == SV_TYPE_ALL) {
  1590. if (Context->TotalEntries != RtlNumberGenericTableElements(&Transport->AnnouncementTable)) {
  1591. KdPrint(("Bowser: Returned EntriesRead == %ld, But %ld entries in table on transport %wZ\n", Context->TotalEntries, RtlNumberGenericTableElements(&Transport->AnnouncementTable), &Transport->TransportName ));
  1592. }
  1593. }
  1594. if (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM || Context->ServerTypeMask == SV_TYPE_ALL) {
  1595. if (Context->EntriesRead <= 20) {
  1596. KdPrint(("Bowser: Returned %s: EntriesRead == %ld (%ld/%ld) on transport %wZ. Resume handle: %lx, %lx\n",
  1597. (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ? "domain" : "server"),
  1598. Context->EntriesRead,
  1599. RtlNumberGenericTableElements(&Transport->AnnouncementTable),
  1600. RtlNumberGenericTableElements(&Transport->DomainTable),
  1601. &Transport->TransportName,
  1602. Context->ResumeKey,
  1603. Context->OriginalResumeKey ));
  1604. }
  1605. if (Context->TotalEntries <= 20) {
  1606. KdPrint(("Bowser: Returned %s: TotalEntries == %ld (%ld/%ld) on transport %wZ. Resume handle: %lx, %lx\n",
  1607. (Context->ServerTypeMask == SV_TYPE_DOMAIN_ENUM ? "domain" : "server"),
  1608. Context->TotalEntries,
  1609. RtlNumberGenericTableElements(&Transport->AnnouncementTable),
  1610. RtlNumberGenericTableElements(&Transport->DomainTable),
  1611. &Transport->TransportName,
  1612. Context->ResumeKey,
  1613. Context->OriginalResumeKey ));
  1614. }
  1615. }
  1616. #endif
  1617. }
  1618. } finally {
  1619. UNLOCK_ANNOUNCE_DATABASE(Transport);
  1620. }
  1621. return(Status);
  1622. }
  1623. BOOLEAN
  1624. PackServerAnnouncement (
  1625. IN ULONG Level,
  1626. IN ULONG ServerTypeMask,
  1627. IN OUT LPTSTR *BufferStart,
  1628. IN OUT LPTSTR *BufferEnd,
  1629. IN ULONG_PTR BufferDisplacment,
  1630. IN PANNOUNCE_ENTRY Announcement,
  1631. OUT PULONG TotalBytesNeeded
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. This routine packs a server announcement into the buffer provided updating
  1636. all relevant pointers.
  1637. Arguments:
  1638. IN ULONG Level - Level of information requested.
  1639. IN OUT PCHAR *BufferStart - Supplies the output buffer.
  1640. Updated to point to the next buffer
  1641. IN OUT PCHAR *BufferEnd - Supplies the end of the buffer. Updated to
  1642. point before the start of the
  1643. strings being packed.
  1644. IN PVOID UsersBufferStart - Supplies the start of the buffer in the users
  1645. address space
  1646. IN PANNOUNCE_ENTRY Announcement - Supplies the announcement to enumerate.
  1647. IN OUT PULONG TotalBytesNeeded - Updated to account for the length of this
  1648. entry
  1649. Return Value:
  1650. BOOLEAN - True if the entry was successfully packed into the buffer.
  1651. --*/
  1652. {
  1653. ULONG BufferSize;
  1654. UNICODE_STRING UnicodeNameString, UnicodeCommentString;
  1655. PSERVER_INFO_101 ServerInfo = (PSERVER_INFO_101 )*BufferStart;
  1656. PAGED_CODE();
  1657. switch (Level) {
  1658. case 100:
  1659. BufferSize = sizeof(SERVER_INFO_100);
  1660. break;
  1661. case 101:
  1662. BufferSize = sizeof(SERVER_INFO_101);
  1663. break;
  1664. default:
  1665. return FALSE;
  1666. }
  1667. *BufferStart = (LPTSTR)(((PUCHAR)*BufferStart) + BufferSize);
  1668. dlog(DPRT_SRVENUM, ("Pack Announcement %ws (%lx) - %ws :", Announcement->ServerName, Announcement, Announcement->ServerComment));
  1669. dlog(DPRT_SRVENUM, ("BufferStart: %lx, BufferEnd: %lx\n", ServerInfo, *BufferEnd));
  1670. //
  1671. // Compute the length of the name.
  1672. //
  1673. RtlInitUnicodeString(&UnicodeNameString, Announcement->ServerName);
  1674. ASSERT (UnicodeNameString.Length <= CNLEN*sizeof(WCHAR));
  1675. RtlInitUnicodeString(&UnicodeCommentString, Announcement->ServerComment);
  1676. ASSERT (UnicodeCommentString.Length <= LM20_MAXCOMMENTSZ*sizeof(WCHAR));
  1677. #if DBG
  1678. if (ServerTypeMask == SV_TYPE_DOMAIN_ENUM) {
  1679. ASSERT (UnicodeCommentString.Length <= CNLEN*sizeof(WCHAR));
  1680. }
  1681. #endif
  1682. //
  1683. // Update the total number of bytes needed for this structure.
  1684. //
  1685. *TotalBytesNeeded += UnicodeNameString.Length + BufferSize + sizeof(WCHAR);
  1686. if (Level == 101) {
  1687. *TotalBytesNeeded += UnicodeCommentString.Length + sizeof(WCHAR);
  1688. if (ServerTypeMask == SV_TYPE_BACKUP_BROWSER) {
  1689. *TotalBytesNeeded += 2;
  1690. }
  1691. }
  1692. if (*BufferStart >= *BufferEnd) {
  1693. return FALSE;
  1694. }
  1695. //
  1696. // Assume an OS/2 platform ID, unless an NT server
  1697. //
  1698. if (Announcement->ServerType & SV_TYPE_NT) {
  1699. ServerInfo->sv101_platform_id = PLATFORM_ID_NT;
  1700. } else {
  1701. ServerInfo->sv101_platform_id = PLATFORM_ID_OS2;
  1702. }
  1703. ServerInfo->sv101_name = UnicodeNameString.Buffer;
  1704. ASSERT (UnicodeNameString.Length / sizeof(WCHAR) <= CNLEN);
  1705. if (!BowserPackUnicodeString(
  1706. &ServerInfo->sv101_name,
  1707. UnicodeNameString.Length,
  1708. BufferDisplacment,
  1709. *BufferStart,
  1710. BufferEnd)) {
  1711. dlog(DPRT_SRVENUM, ("Unable to pack name %ws into buffer\n", Announcement->ServerName));
  1712. return FALSE;
  1713. }
  1714. if (Level > 100) {
  1715. PUSHORT VersionPointer;
  1716. ServerInfo->sv101_version_major = Announcement->ServerVersionMajor;
  1717. ServerInfo->sv101_version_minor = Announcement->ServerVersionMinor;
  1718. ServerInfo->sv101_type = Announcement->ServerType;
  1719. ServerInfo->sv101_comment = UnicodeCommentString.Buffer;
  1720. ASSERT (UnicodeCommentString.Length / sizeof(WCHAR) <= LM20_MAXCOMMENTSZ);
  1721. if (!BowserPackUnicodeString(
  1722. &ServerInfo->sv101_comment,
  1723. UnicodeCommentString.Length,
  1724. BufferDisplacment,
  1725. *BufferStart,
  1726. BufferEnd)) {
  1727. dlog(DPRT_SRVENUM, ("Unable to pack comment %ws into buffer\n", Announcement->ServerComment));
  1728. return FALSE;
  1729. }
  1730. if (ServerTypeMask == SV_TYPE_BACKUP_BROWSER) {
  1731. //
  1732. // If we can't fit a ushort into the buffer, return an error.
  1733. //
  1734. if ((*BufferEnd - *BufferStart) <= sizeof(USHORT)) {
  1735. return FALSE;
  1736. }
  1737. //
  1738. // Back the buffer end by the size of a USHORT (to make room for
  1739. // this value).
  1740. //
  1741. (ULONG_PTR)*BufferEnd -= sizeof(USHORT);
  1742. VersionPointer = (PUSHORT)*BufferEnd;
  1743. *VersionPointer = Announcement->ServerBrowserVersion;
  1744. }
  1745. }
  1746. return TRUE;
  1747. }
  1748. PVIEW_BUFFER
  1749. BowserAllocateViewBuffer(
  1750. VOID
  1751. )
  1752. /*++
  1753. Routine Description:
  1754. This routine will allocate a view buffer from the view buffer pool.
  1755. If it is unable to allocate a buffer, it will allocate the buffer from
  1756. non-paged pool (up to the maximum configured by the user).
  1757. Arguments:
  1758. None.
  1759. Return Value:
  1760. ViewBuffr - The allocated buffer.
  1761. --*/
  1762. {
  1763. KIRQL OldIrql;
  1764. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  1765. ACQUIRE_SPIN_LOCK(&BowserViewBufferListSpinLock, &OldIrql);
  1766. if (!IsListEmpty(&BowserViewBufferHead)) {
  1767. PLIST_ENTRY Entry = RemoveHeadList(&BowserViewBufferHead);
  1768. RELEASE_SPIN_LOCK(&BowserViewBufferListSpinLock, OldIrql);
  1769. return CONTAINING_RECORD(Entry, VIEW_BUFFER, Overlay.NextBuffer);
  1770. }
  1771. if (BowserNumberOfServerAnnounceBuffers <=
  1772. BowserData.NumberOfServerAnnounceBuffers) {
  1773. PVIEW_BUFFER ViewBuffer = NULL;
  1774. BowserNumberOfServerAnnounceBuffers += 1;
  1775. RELEASE_SPIN_LOCK(&BowserViewBufferListSpinLock, OldIrql);
  1776. ViewBuffer = ALLOCATE_POOL(NonPagedPool, sizeof(VIEW_BUFFER), POOL_VIEWBUFFER);
  1777. if (ViewBuffer == NULL) {
  1778. ACQUIRE_SPIN_LOCK(&BowserViewBufferListSpinLock, &OldIrql);
  1779. BowserNumberOfServerAnnounceBuffers -= 1;
  1780. BowserStatistics.NumberOfFailedServerAnnounceAllocations += 1;
  1781. RELEASE_SPIN_LOCK(&BowserViewBufferListSpinLock, OldIrql);
  1782. return NULL;
  1783. }
  1784. ViewBuffer->Signature = STRUCTURE_SIGNATURE_VIEW_BUFFER;
  1785. ViewBuffer->Size = sizeof(VIEW_BUFFER);
  1786. return ViewBuffer;
  1787. }
  1788. RELEASE_SPIN_LOCK(&BowserViewBufferListSpinLock, OldIrql);
  1789. BowserStatistics.NumberOfMissedServerAnnouncements += 1;
  1790. // run out of buffers.
  1791. return NULL;
  1792. }
  1793. VOID
  1794. BowserFreeViewBuffer(
  1795. IN PVIEW_BUFFER Buffer
  1796. )
  1797. /*++
  1798. Routine Description:
  1799. This routine will return a view buffer to the view buffer pool.
  1800. Arguments:
  1801. IN PVIEW_BUFFER Buffer - Supplies the buffer to free
  1802. Return Value:
  1803. None.
  1804. --*/
  1805. {
  1806. KIRQL OldIrql;
  1807. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  1808. ASSERT (Buffer->Signature == STRUCTURE_SIGNATURE_VIEW_BUFFER);
  1809. ACQUIRE_SPIN_LOCK(&BowserViewBufferListSpinLock, &OldIrql);
  1810. InsertTailList(&BowserViewBufferHead, &Buffer->Overlay.NextBuffer);
  1811. RELEASE_SPIN_LOCK(&BowserViewBufferListSpinLock, OldIrql);
  1812. }
  1813. NTSTATUS
  1814. BowserpInitializeAnnounceTable(
  1815. VOID
  1816. )
  1817. /*++
  1818. Routine Description:
  1819. This routine will allocate a transport descriptor and bind the bowser
  1820. to the transport.
  1821. Arguments:
  1822. Return Value:
  1823. NTSTATUS - Status of operation.
  1824. --*/
  1825. {
  1826. PAGED_CODE();
  1827. InitializeListHead(&BowserViewBufferHead);
  1828. //
  1829. // Allocate a spin lock to protect the view buffer chain.
  1830. //
  1831. KeInitializeSpinLock(&BowserViewBufferListSpinLock);
  1832. BowserNumberOfServerAnnounceBuffers = 0;
  1833. return STATUS_SUCCESS;
  1834. }
  1835. NTSTATUS
  1836. BowserpUninitializeAnnounceTable(
  1837. VOID
  1838. )
  1839. /*++
  1840. Routine Description:
  1841. Arguments:
  1842. Return Value:
  1843. NTSTATUS - Status of operation.
  1844. --*/
  1845. {
  1846. PVIEW_BUFFER Buffer;
  1847. PAGED_CODE();
  1848. //
  1849. // Note: We don't need to protect this list while stopping because
  1850. // we have already unbound from all the loaded transports, thus no
  1851. // other announcements are being processed.
  1852. //
  1853. while (!IsListEmpty(&BowserViewBufferHead)) {
  1854. PLIST_ENTRY Entry = RemoveHeadList(&BowserViewBufferHead);
  1855. Buffer = CONTAINING_RECORD(Entry, VIEW_BUFFER, Overlay.NextBuffer);
  1856. FREE_POOL(Buffer);
  1857. }
  1858. ASSERT (IsListEmpty(&BowserViewBufferHead));
  1859. BowserNumberOfServerAnnounceBuffers = 0;
  1860. return STATUS_SUCCESS;
  1861. }
  1862. VOID
  1863. BowserDeleteGenericTable(
  1864. IN PRTL_GENERIC_TABLE GenericTable
  1865. )
  1866. {
  1867. PVOID TableElement;
  1868. PAGED_CODE();
  1869. //
  1870. // Enumerate the elements in the table, deleting them as we go.
  1871. //
  1872. // KdPrint("Delete Generic Table %lx\n", GenericTable));
  1873. for (TableElement = RtlEnumerateGenericTable(GenericTable, TRUE) ;
  1874. TableElement != NULL ;
  1875. TableElement = RtlEnumerateGenericTable(GenericTable, TRUE)) {
  1876. PANNOUNCE_ENTRY Announcement = TableElement;
  1877. if (Announcement->BackupLink.Flink != NULL) {
  1878. ASSERT (Announcement->BackupLink.Blink != NULL);
  1879. ASSERT (Announcement->ServerType & SV_TYPE_BACKUP_BROWSER);
  1880. RemoveEntryList(&Announcement->BackupLink);
  1881. Announcement->BackupLink.Flink = NULL;
  1882. Announcement->BackupLink.Blink = NULL;
  1883. }
  1884. BowserDereferenceName( Announcement->Name );
  1885. RtlDeleteElementGenericTable(GenericTable, TableElement);
  1886. }
  1887. ASSERT (RtlNumberGenericTableElements(GenericTable) == 0);
  1888. }