Windows NT 4.0 source code leak
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.

2560 lines
71 KiB

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