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

1309 lines
36 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. brsrvlst.c.
  5. Abstract:
  6. This module implements the routines to manipulate WinBALL browser server
  7. lists.
  8. Author:
  9. Larry Osterman (larryo) 6-May-1991
  10. Revision History:
  11. 6-May-1991 larryo
  12. Created
  13. --*/
  14. #define INCLUDE_SMB_TRANSACTION
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #define SECONDS_PER_ELECTION (((((ELECTION_DELAY_MAX - ELECTION_DELAY_MIN) / 2)*ELECTION_COUNT) + 999) / 1000)
  18. LARGE_INTEGER
  19. BowserGetBrowserListTimeout = {0};
  20. VOID
  21. BowserGetBackupListWorker(
  22. IN PVOID Ctx
  23. );
  24. NTSTATUS
  25. BowserSendBackupListRequest(
  26. IN PTRANSPORT Transport,
  27. IN PUNICODE_STRING Domain
  28. );
  29. NTSTATUS
  30. AddBackupToBackupList(
  31. IN PCHAR *BackupPointer,
  32. IN PCHAR BackupListStart,
  33. IN PANNOUNCE_ENTRY ServerEntry
  34. );
  35. KSPIN_LOCK
  36. BowserBackupListSpinLock = {0};
  37. #define BOWSER_BACKUP_LIST_RESPONSE_SIZE 1024
  38. NTSTATUS
  39. BowserCheckForPrimaryBrowserServer(
  40. IN PTRANSPORT Transport,
  41. IN PVOID Context
  42. );
  43. PVOID
  44. BowserGetBackupServerListFromTransport(
  45. IN PTRANSPORT Transport
  46. );
  47. VOID
  48. BowserFreeTransportBackupList(
  49. IN PTRANSPORT Transport
  50. );
  51. #ifdef ALLOC_PRAGMA
  52. #pragma alloc_text(PAGE, BowserFreeBrowserServerList)
  53. #pragma alloc_text(PAGE, BowserShuffleBrowserServerList)
  54. #pragma alloc_text(INIT, BowserpInitializeGetBrowserServerList)
  55. #pragma alloc_text(PAGE, BowserpUninitializeGetBrowserServerList)
  56. #pragma alloc_text(PAGE, BowserSendBackupListRequest)
  57. #pragma alloc_text(PAGE, BowserGetBackupListWorker)
  58. #pragma alloc_text(PAGE, AddBackupToBackupList)
  59. #pragma alloc_text(PAGE, BowserGetBrowserServerList)
  60. #pragma alloc_text(PAGE, BowserCheckForPrimaryBrowserServer)
  61. #pragma alloc_text(PAGE4BROW, BowserGetBackupServerListFromTransport)
  62. #pragma alloc_text(PAGE4BROW, BowserFreeTransportBackupList)
  63. #endif
  64. VOID
  65. BowserFreeBrowserServerList (
  66. IN PWSTR *BrowserServerList,
  67. IN ULONG BrowserServerListLength
  68. )
  69. /*++
  70. Routine Description:
  71. This routine will free the list of browser servers associated with
  72. a transport.
  73. Arguments:
  74. IN PTRANSPORT Transport - Supplies the transport whose buffer is to be freed
  75. Return Value:
  76. None.
  77. --*/
  78. {
  79. ULONG i;
  80. PAGED_CODE();
  81. for (i = 0; i < BrowserServerListLength ; i++) {
  82. FREE_POOL(BrowserServerList[i]);
  83. }
  84. FREE_POOL(BrowserServerList);
  85. }
  86. #define Swap(a,b) \
  87. { \
  88. PWSTR t = a; \
  89. a = b; \
  90. b = t; \
  91. }
  92. NTSTATUS
  93. BowserCheckForPrimaryBrowserServer(
  94. IN PTRANSPORT Transport,
  95. IN PVOID Context
  96. )
  97. {
  98. PWSTR ServerName = Context;
  99. PAGED_CODE();
  100. //
  101. // Grab a lock on the BrowserServerList for this transport.
  102. //
  103. // Since this call is made with the BrowserServerList exclusively locked for one of the
  104. // transports, we can't wait for the lock (there would be an implicit violation of the
  105. // locking order).
  106. //
  107. // However, since this call is simply being used as an optimization, we'll simply skip
  108. // the check when we have contention.
  109. //
  110. if (!ExAcquireResourceSharedLite(&Transport->BrowserServerListResource, FALSE)) {
  111. return STATUS_SUCCESS;
  112. }
  113. if (Transport->PagedTransport->BrowserServerListBuffer != NULL) {
  114. if (!_wcsicmp(ServerName, Transport->PagedTransport->BrowserServerListBuffer[0])) {
  115. ExReleaseResourceLite(&Transport->BrowserServerListResource);
  116. return STATUS_UNSUCCESSFUL;
  117. }
  118. }
  119. ExReleaseResourceLite(&Transport->BrowserServerListResource);
  120. return STATUS_SUCCESS;
  121. }
  122. VOID
  123. BowserShuffleBrowserServerList(
  124. IN PWSTR *BrowserServerList,
  125. IN ULONG BrowserServerListLength,
  126. IN BOOLEAN IsPrimaryDomain,
  127. IN PDOMAIN_INFO DomainInfo
  128. )
  129. /*++
  130. Routine Description:
  131. This routine will shuffle the list of browser servers associated with
  132. a transport.
  133. Arguments:
  134. Return Value:
  135. None.
  136. Note:
  137. We rely on the fact that the DLL will always pick the 0th entry in the
  138. list for the server to remote the API to. We will first shuffle the
  139. list completely, then, if this is our primary domain, we will
  140. walk the list of domains and check to see if this entry is the 0th
  141. entry on any of the transports. If it isn't, then we swap this entry
  142. with the 0th entry and return, since we've guaranteed that it's ok on
  143. all the other transports.
  144. --*/
  145. {
  146. ULONG NewIndex;
  147. ULONG i;
  148. PAGED_CODE();
  149. ASSERT ( BrowserServerListLength != 0 );
  150. //
  151. // First thoroughly shuffle the list.
  152. //
  153. for (i = 0 ; i < BrowserServerListLength ; i++ ) {
  154. NewIndex = BowserRandom(BrowserServerListLength);
  155. Swap(BrowserServerList[i], BrowserServerList[NewIndex]);
  156. }
  157. //
  158. // If we are querying our primary domain, we want to make sure that we
  159. // don't have this server as the primary server for any other transports.
  160. //
  161. //
  162. // The reason for this is that the NT product 1 redirector cannot connect
  163. // to the same server on different transports, so it has to disconnect and
  164. // reconnect to that server. We can avoid this disconnect/reconnect
  165. // overhead by making sure that the primary browse server (the 0th entry
  166. // in the browse list) is different for all transports.
  167. //
  168. if (IsPrimaryDomain) {
  169. //
  170. // Now walk through the server list and if the server at this index
  171. // is the 0th entry for another transport, we want to swap it with the
  172. // ith entry and keep on going.
  173. //
  174. for (i = 0 ; i < BrowserServerListLength ; i++ ) {
  175. if (NT_SUCCESS(BowserForEachTransportInDomain(DomainInfo, BowserCheckForPrimaryBrowserServer, BrowserServerList[i]))) {
  176. Swap(BrowserServerList[0], BrowserServerList[i]);
  177. //
  178. // This server isn't the primary browser server for any other
  179. // transports, we can return now, since we're done.
  180. //
  181. break;
  182. }
  183. }
  184. }
  185. }
  186. PVOID
  187. BowserGetBackupServerListFromTransport(
  188. IN PTRANSPORT Transport
  189. )
  190. {
  191. KIRQL OldIrql;
  192. PVOID BackupList;
  193. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  194. ACQUIRE_SPIN_LOCK(&BowserBackupListSpinLock, &OldIrql);
  195. BackupList = Transport->BowserBackupList;
  196. Transport->BowserBackupList = NULL;
  197. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  198. return BackupList;
  199. }
  200. VOID
  201. BowserFreeTransportBackupList(
  202. IN PTRANSPORT Transport
  203. )
  204. {
  205. KIRQL OldIrql;
  206. PVOID BackupList;
  207. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  208. ACQUIRE_SPIN_LOCK(&BowserBackupListSpinLock, &OldIrql);
  209. if (Transport->BowserBackupList != NULL) {
  210. BackupList = Transport->BowserBackupList;
  211. Transport->BowserBackupList = NULL;
  212. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  213. FREE_POOL(BackupList);
  214. } else {
  215. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  216. }
  217. }
  218. NTSTATUS
  219. BowserGetBrowserServerList(
  220. IN PIRP Irp,
  221. IN PTRANSPORT Transport,
  222. IN PUNICODE_STRING DomainName OPTIONAL,
  223. OUT PWSTR **BrowserServerList,
  224. OUT PULONG BrowserServerListLength
  225. )
  226. /*++
  227. Routine Description:
  228. This routine is the indication time processing needed to get a backup
  229. list response.
  230. Arguments:
  231. IN PTRANSPORT_NAME TransportName - Supplies the transport name receiving
  232. the request.
  233. IN PBACKUP_LIST_RESPONSE_1 BackupList - Supplies the backup server list
  234. IN ULONG BytesAvailable - Supplies the # of bytes in the message
  235. OUT PULONG BytesTaken;
  236. Return Value:
  237. None.
  238. --*/
  239. {
  240. NTSTATUS Status;
  241. PUCHAR BackupPointer;
  242. ULONG i;
  243. PBACKUP_LIST_RESPONSE_1 BackupList = NULL;
  244. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  245. PAGED_CODE();
  246. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  247. // ASSERT (ExIsResourceAcquiredExclusiveLite(&Transport->BrowserServerListResource));
  248. //
  249. // Initialize the browser server list to a known state.
  250. //
  251. *BrowserServerList = NULL;
  252. *BrowserServerListLength = 0;
  253. ASSERT (Transport->BowserBackupList == NULL);
  254. try {
  255. ULONG RetryCount = BOWSER_GETBROWSERLIST_RETRY_COUNT;
  256. //
  257. // Allocate and save a buffer to hold the response server names.
  258. //
  259. Transport->BowserBackupList = ALLOCATE_POOL(NonPagedPool, Transport->DatagramSize, POOL_BACKUPLIST);
  260. if (Transport->BowserBackupList == NULL) {
  261. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  262. }
  263. //
  264. // This is a new request, so bump the token to indicate that this is
  265. // a new GetBrowserServerList request.
  266. //
  267. ExInterlockedAddUlong(&Transport->BrowserServerListToken, 1, &BowserBackupListSpinLock);
  268. //
  269. // We retry for 3 times, and we timeout the wait after 1 seconds.
  270. // This means that in the worse case this routine takes 4 seconds
  271. // to execute.
  272. //
  273. //
  274. while (RetryCount --) {
  275. ULONG Count = 0;
  276. //
  277. // Set the completion event to the not-signalled state.
  278. //
  279. KeResetEvent(&Transport->GetBackupListComplete);
  280. //
  281. // Send the backup server list query.
  282. //
  283. Status = BowserSendBackupListRequest(Transport, DomainName);
  284. if (!NT_SUCCESS(Status)) {
  285. //
  286. // If the send datagram failed, return a more browser like
  287. // error.
  288. //
  289. try_return(Status = STATUS_NO_BROWSER_SERVERS_FOUND);
  290. }
  291. do {
  292. //
  293. // Wait until either the server has responded to the request,
  294. // or we give up.
  295. //
  296. Status = KeWaitForSingleObject(&Transport->GetBackupListComplete,
  297. Executive,
  298. KernelMode,
  299. FALSE,
  300. &BowserGetBrowserListTimeout);
  301. if (Status == STATUS_TIMEOUT) {
  302. //
  303. // If this thread is terminating, then give up and return
  304. // a reasonable error to the caller.
  305. //
  306. if (PsIsThreadTerminating(Irp->Tail.Overlay.Thread)) {
  307. Status = STATUS_CANCELLED;
  308. break;
  309. }
  310. }
  311. } while ( (Status == STATUS_TIMEOUT)
  312. &&
  313. (Count++ < BOWSER_GETBROWSERLIST_TIMEOUT) );
  314. //
  315. // If the request succeeded, we can return
  316. // right away.
  317. //
  318. if (Status != STATUS_TIMEOUT) {
  319. break;
  320. }
  321. //
  322. // Force an election - We couldn't find a browser server.
  323. //
  324. dlog(DPRT_CLIENT,
  325. ("%s: %ws: Unable to get browser server list - forcing election\n",
  326. Transport->DomainInfo->DomOemDomainName,
  327. PagedTransport->TransportName.Buffer ));
  328. PagedTransport->Uptime = BowserTimeUp();
  329. if (BowserLogElectionPackets) {
  330. BowserWriteErrorLogEntry(EVENT_BOWSER_ELECTION_SENT_GETBLIST_FAILED, STATUS_SUCCESS, NULL, 0, 1, PagedTransport->TransportName.Buffer);
  331. }
  332. }
  333. //
  334. // If, after all this, we still timed out, return an error.
  335. //
  336. if (Status == STATUS_TIMEOUT) {
  337. //
  338. // If it has been less than the maximum amount of time for an election plus some
  339. // slop to allow the WfW machine to add the transport, don't
  340. // send the election packet.
  341. //
  342. if ((PagedTransport->Role == None)
  343. ||
  344. ((DomainName != NULL) &&
  345. !RtlEqualUnicodeString(DomainName, &Transport->DomainInfo->DomUnicodeDomainName, TRUE)
  346. )
  347. ||
  348. ((BowserTimeUp() - PagedTransport->LastElectionSeen) > ELECTION_TIME )
  349. ) {
  350. dlog(DPRT_ELECT,
  351. ("%s: %ws: Starting election, domain %wZ. Time Up: %lx, LastElectionSeen: %lx\n",
  352. Transport->DomainInfo->DomOemDomainName,
  353. PagedTransport->TransportName.Buffer,
  354. (DomainName != NULL ? DomainName : &Transport->DomainInfo->DomUnicodeDomainName),
  355. BowserTimeUp(),
  356. Transport->PagedTransport->LastElectionSeen));
  357. BowserSendElection(DomainName,
  358. BrowserElection,
  359. Transport,
  360. FALSE);
  361. }
  362. try_return(Status = STATUS_NO_BROWSER_SERVERS_FOUND);
  363. }
  364. if (!NT_SUCCESS(Status)) {
  365. try_return(Status);
  366. }
  367. //
  368. // We now have a valid list of servers from the net.
  369. //
  370. // Massage this list into a form that we can return.
  371. //
  372. BackupList = BowserGetBackupServerListFromTransport(Transport);
  373. *BrowserServerListLength = BackupList->BackupServerCount;
  374. *BrowserServerList = ALLOCATE_POOL(PagedPool | POOL_COLD_ALLOCATION, *BrowserServerListLength*sizeof(PWSTR), POOL_BROWSERSERVERLIST);
  375. if (*BrowserServerList == NULL) {
  376. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  377. }
  378. RtlZeroMemory(*BrowserServerList, *BrowserServerListLength*sizeof(PWSTR));
  379. BackupPointer = BackupList->BackupServerList;
  380. for ( i = 0 ; i < (ULONG)BackupList->BackupServerCount ; i ++ ) {
  381. UNICODE_STRING UServerName;
  382. OEM_STRING AServerName;
  383. RtlInitAnsiString(&AServerName, BackupPointer);
  384. Status = RtlOemStringToUnicodeString(&UServerName, &AServerName, TRUE);
  385. if (!NT_SUCCESS(Status)) {
  386. try_return(Status);
  387. }
  388. (*BrowserServerList)[i] = ALLOCATE_POOL(PagedPool | POOL_COLD_ALLOCATION, UServerName.Length+(sizeof(WCHAR)*3), POOL_BROWSERSERVER);
  389. if ((*BrowserServerList)[i] == NULL) {
  390. RtlFreeUnicodeString(&UServerName);
  391. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  392. }
  393. //
  394. // Put "\\" at the start of the server name.
  395. //
  396. RtlCopyMemory((*BrowserServerList)[i], L"\\\\", 4);
  397. dlog(DPRT_CLIENT,
  398. ("Packing server name %ws to %lx\n",
  399. UServerName.Buffer, (*BrowserServerList)[i]));
  400. RtlCopyMemory(&((*BrowserServerList)[i])[2], UServerName.Buffer, UServerName.MaximumLength);
  401. //
  402. // Bump the pointer to the backup server name.
  403. //
  404. BackupPointer += AServerName.Length + sizeof(CHAR);
  405. RtlFreeUnicodeString(&UServerName);
  406. }
  407. //
  408. // Now shuffle the browser server list we got back from the server
  409. // to ensure some degree of randomness in the choice.
  410. //
  411. BowserShuffleBrowserServerList(
  412. *BrowserServerList,
  413. *BrowserServerListLength,
  414. (BOOLEAN)(DomainName == NULL ||
  415. RtlEqualUnicodeString(&Transport->DomainInfo->DomUnicodeDomainName, DomainName, TRUE)),
  416. Transport->DomainInfo );
  417. try_return(Status = STATUS_SUCCESS);
  418. try_exit:NOTHING;
  419. } finally {
  420. if (!NT_SUCCESS(Status)) {
  421. if (*BrowserServerList != NULL) {
  422. for ( i = 0 ; i < *BrowserServerListLength ; i ++ ) {
  423. if ((*BrowserServerList)[i] != NULL) {
  424. FREE_POOL((*BrowserServerList)[i]);
  425. }
  426. }
  427. FREE_POOL(*BrowserServerList);
  428. *BrowserServerList = NULL;
  429. }
  430. *BrowserServerListLength = 0;
  431. BowserFreeTransportBackupList(Transport);
  432. }
  433. if (BackupList != NULL) {
  434. FREE_POOL(BackupList);
  435. }
  436. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  437. }
  438. return Status;
  439. }
  440. DATAGRAM_HANDLER(
  441. BowserGetBackupListResponse
  442. )
  443. /*++
  444. Routine Description:
  445. This routine is the indication time processing needed to get a backup
  446. list response.
  447. Arguments:
  448. IN PTRANSPORT_NAME TransportName - Supplies the transport name receiving
  449. the request.
  450. IN PBACKUP_LIST_RESPONSE_1 BackupList - Supplies the backup server list
  451. IN ULONG BytesAvailable - Supplies the # of bytes in the message
  452. OUT PULONG BytesTaken;
  453. Return Value:
  454. None.
  455. --*/
  456. {
  457. PTRANSPORT Transport = TransportName->Transport;
  458. PBACKUP_LIST_RESPONSE_1 BackupList = Buffer;
  459. KIRQL OldIrql;
  460. ULONG StringCount = 0;
  461. PUCHAR Walker = BackupList->BackupServerList;
  462. PUCHAR BufferEnd = ((PUCHAR)Buffer) + BytesAvailable;
  463. if (Transport->BowserBackupList == NULL) {
  464. dprintf(DPRT_CLIENT,("BOWSER: Received GetBackupListResponse while not expecting one\n"));
  465. return STATUS_REQUEST_NOT_ACCEPTED;
  466. }
  467. ASSERT ( BytesAvailable <= Transport->DatagramSize );
  468. ACQUIRE_SPIN_LOCK(&BowserBackupListSpinLock, &OldIrql);
  469. //
  470. // This response is for an old request - ignore it.
  471. //
  472. if (BackupList->Token != Transport->BrowserServerListToken) {
  473. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  474. return(STATUS_REQUEST_NOT_ACCEPTED);
  475. }
  476. //
  477. // Verify that the incoming buffer is a series of valid strings, and
  478. // that the number indicated are actually present in the buffer.
  479. //
  480. while (StringCount < BackupList->BackupServerCount &&
  481. Walker < BufferEnd) {
  482. if (*Walker == '\0') {
  483. StringCount++;
  484. }
  485. Walker++;
  486. }
  487. if (Walker == BufferEnd) {
  488. if (StringCount < BackupList->BackupServerCount) {
  489. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  490. return(STATUS_REQUEST_NOT_ACCEPTED);
  491. }
  492. }
  493. //
  494. // Bump the token again to invalidate any incoming responses - they are
  495. // no longer valid.
  496. //
  497. Transport->BrowserServerListToken += 1;
  498. if (Transport->BowserBackupList != NULL) {
  499. //
  500. // Copy the received buffer.
  501. //
  502. TdiCopyLookaheadData(Transport->BowserBackupList, BackupList, BytesAvailable, ReceiveFlags);
  503. KeSetEvent(&Transport->GetBackupListComplete, IO_NETWORK_INCREMENT, FALSE);
  504. }
  505. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  506. return(STATUS_SUCCESS);
  507. UNREFERENCED_PARAMETER(BytesTaken);
  508. }
  509. NTSTATUS
  510. BowserSendBackupListRequest(
  511. IN PTRANSPORT Transport,
  512. IN PUNICODE_STRING Domain
  513. )
  514. /*++
  515. Routine Description:
  516. This routine sends a getbackup list request to the master browser server
  517. for a specified domain.
  518. Arguments:
  519. IN PTRANSPORT_NAME TransportName - Supplies the transport name receiving
  520. the request.
  521. IN PBACKUP_LIST_RESPONSE_1 BackupList - Supplies the backup server list
  522. IN ULONG BytesAvailable - Supplies the # of bytes in the message
  523. OUT PULONG BytesTaken;
  524. Return Value:
  525. None.
  526. --*/
  527. {
  528. NTSTATUS Status, Status2;
  529. BACKUP_LIST_REQUEST Request;
  530. PAGED_CODE();
  531. Request.Type = GetBackupListReq;
  532. //
  533. // Send this request.
  534. //
  535. Request.BackupListRequest.Token = Transport->BrowserServerListToken;
  536. //
  537. // WinBALL only asks for 4 of these, so that's what I'll ask for.
  538. //
  539. Request.BackupListRequest.RequestedCount = 4;
  540. // ask for Master Browser
  541. Status = BowserSendSecondClassMailslot(Transport,
  542. (Domain == NULL ?
  543. &Transport->DomainInfo->DomUnicodeDomainName :
  544. Domain),
  545. MasterBrowser,
  546. &Request, sizeof(Request), TRUE,
  547. MAILSLOT_BROWSER_NAME,
  548. NULL);
  549. #ifdef ENABLE_PSEUDO_BROWSER
  550. if (!FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX) &&
  551. BowserData.PseudoServerLevel != BROWSER_SEMI_PSEUDO_NO_DMB) {
  552. #else
  553. if (!FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) {
  554. #endif
  555. // search for PDC
  556. // In some configurations, it is valid not to have a PDC, thus,
  557. // ignore status code (do not propagate up).
  558. // Do not talk to the DMB (PDC name) directly if we're semi-pseudo
  559. Status2 = BowserSendSecondClassMailslot(Transport,
  560. (Domain == NULL ?
  561. &Transport->DomainInfo->DomUnicodeDomainName :
  562. Domain),
  563. PrimaryDomainBrowser,
  564. &Request, sizeof(Request), TRUE,
  565. MAILSLOT_BROWSER_NAME,
  566. NULL);
  567. // if either succeeded, we'll return success.
  568. Status = NT_SUCCESS(Status2) ? Status2: Status;
  569. }
  570. return (Status);
  571. }
  572. DATAGRAM_HANDLER(
  573. BowserGetBackupListRequest
  574. )
  575. {
  576. NTSTATUS status;
  577. //
  578. // We need to have at least enough bytes of data to read in
  579. // a BACKUP_LIST_REQUEST_1 structure.
  580. //
  581. if (BytesAvailable < sizeof(BACKUP_LIST_REQUEST_1)) {
  582. return STATUS_REQUEST_NOT_ACCEPTED;
  583. }
  584. BowserStatistics.NumberOfGetBrowserServerListRequests += 1;
  585. status = BowserPostDatagramToWorkerThread(
  586. TransportName,
  587. Buffer,
  588. BytesAvailable,
  589. BytesTaken,
  590. SourceAddress,
  591. SourceAddressLength,
  592. SourceName,
  593. SourceNameLength,
  594. BowserGetBackupListWorker,
  595. NonPagedPool,
  596. DelayedWorkQueue,
  597. ReceiveFlags,
  598. TRUE); // Response will be sent.
  599. if (!NT_SUCCESS(status)) {
  600. BowserNumberOfMissedGetBrowserServerListRequests += 1;
  601. BowserStatistics.NumberOfMissedGetBrowserServerListRequests += 1;
  602. return status;
  603. }
  604. return status;
  605. }
  606. VOID
  607. BowserGetBackupListWorker(
  608. IN PVOID Ctx
  609. )
  610. {
  611. PPOST_DATAGRAM_CONTEXT Context = Ctx;
  612. PBACKUP_LIST_REQUEST_1 BackupListRequest = Context->Buffer;
  613. PIRP Irp = NULL;
  614. PTRANSPORT Transport = Context->TransportName->Transport;
  615. STRING ClientAddress;
  616. NTSTATUS Status;
  617. PBACKUP_LIST_RESPONSE BackupListResponse = NULL;
  618. PCHAR ClientName = Context->ClientName;
  619. UNICODE_STRING UClientName;
  620. OEM_STRING AClientName;
  621. WCHAR ClientNameBuffer[LM20_CNLEN+1];
  622. PAGED_CODE();
  623. ClientAddress.Buffer = Context->TdiClientAddress;
  624. ClientAddress.Length = ClientAddress.MaximumLength =
  625. (USHORT)Context->ClientAddressLength;
  626. UClientName.Buffer = ClientNameBuffer;
  627. UClientName.MaximumLength = (LM20_CNLEN+1)*sizeof(WCHAR);
  628. RtlInitAnsiString(&AClientName, Context->ClientName);
  629. Status = RtlOemStringToUnicodeString(&UClientName, &AClientName, FALSE);
  630. if (!NT_SUCCESS(Status)) {
  631. BowserLogIllegalName( Status, AClientName.Buffer, AClientName.Length );
  632. BowserDereferenceTransportName(Context->TransportName);
  633. BowserDereferenceTransport(Transport);
  634. InterlockedDecrement( &BowserPostedDatagramCount );
  635. FREE_POOL(Context);
  636. return;
  637. }
  638. //
  639. // Lock the transport to allow us access to the list. This prevents
  640. // any role changes while we're responding to the caller.
  641. //
  642. LOCK_TRANSPORT_SHARED(Transport);
  643. //
  644. // Do nothing if we're not a master browser. This can happen if
  645. // we're running on the PDC, and aren't the master for some reason (for
  646. // instance, if the master browser is running a newer version of the
  647. // browser).
  648. //
  649. if ( Transport->PagedTransport->Role != Master ) {
  650. UNLOCK_TRANSPORT(Transport);
  651. BowserDereferenceTransportName(Context->TransportName);
  652. BowserDereferenceTransport(Transport);
  653. InterlockedDecrement( &BowserPostedDatagramCount );
  654. FREE_POOL(Context);
  655. return;
  656. }
  657. LOCK_ANNOUNCE_DATABASE_SHARED(Transport);
  658. try {
  659. PUCHAR BackupPointer;
  660. PLIST_ENTRY BackupEntry;
  661. PLIST_ENTRY TraverseStart;
  662. USHORT Count;
  663. UCHAR NumberOfBackupServers = 0;
  664. ULONG EntriesInList;
  665. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  666. BackupListResponse = ALLOCATE_POOL(PagedPool, BOWSER_BACKUP_LIST_RESPONSE_SIZE, POOL_BACKUPLIST_RESP);
  667. //
  668. // If we can't allocate the buffer, just bail out.
  669. //
  670. if (BackupListResponse == NULL) {
  671. try_return(NOTHING);
  672. }
  673. BackupListResponse->Type = GetBackupListResp;
  674. BackupListResponse->BackupListResponse.BackupServerCount = 0;
  675. //
  676. // Set the token to the clients requested value
  677. //
  678. SmbPutUlong(&BackupListResponse->BackupListResponse.Token, BackupListRequest->Token);
  679. BackupPointer = BackupListResponse->BackupListResponse.BackupServerList;
  680. //
  681. // Since we're a backup browser, make sure that at least our name is
  682. // in the list.
  683. //
  684. {
  685. RtlCopyMemory( BackupPointer,
  686. Transport->DomainInfo->DomOemComputerName.Buffer,
  687. Transport->DomainInfo->DomOemComputerName.MaximumLength );
  688. //
  689. // Bump pointer by size of string.
  690. //
  691. BackupPointer += Transport->DomainInfo->DomOemComputerName.MaximumLength;
  692. }
  693. NumberOfBackupServers += 1;
  694. #ifdef ENABLE_PSEUDO_BROWSER
  695. //
  696. // Pseudo Server should not advertise any backup server but itself.
  697. //
  698. if (BowserData.PseudoServerLevel != BROWSER_PSEUDO) {
  699. #endif
  700. //
  701. // Walk the list of servers forward by the Last DC returned # of elements
  702. //
  703. Count = BackupListRequest->RequestedCount;
  704. BackupEntry = PagedTransport->BackupBrowserList.Flink;
  705. EntriesInList = PagedTransport->NumberOfBackupServerListEntries;
  706. // KdPrint(("There are %ld entries in the list\n", EntriesInList));
  707. TraverseStart = BackupEntry;
  708. //
  709. // Try to find DC's and BDC's to satisfy the users request
  710. // first. They presumably are more appropriate to be returned
  711. // anyway.
  712. //
  713. dlog(DPRT_MASTER, ("Advanced servers: "));
  714. while (Count && EntriesInList -- ) {
  715. PANNOUNCE_ENTRY ServerEntry = CONTAINING_RECORD(BackupEntry, ANNOUNCE_ENTRY, BackupLink);
  716. // KdPrint(("Check entry %ws. Flags: %lx\n", ServerEntry->ServerName, ServerEntry->ServerType));
  717. //
  718. // If this machine was a backup, and is now a master, it is
  719. // possible we might return ourselves in the list of backups.
  720. //
  721. // While this is not fatal, it can possibly cause problems,
  722. // so remove ourselves from the list and skip to the next server
  723. // in the list.
  724. //
  725. //
  726. // Since WfW machines don't support "double hops", we can't
  727. // return them to clients as legitimate backup servers.
  728. //
  729. if (
  730. (ServerEntry->ServerType & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL))
  731. &&
  732. (ServerEntry->ServerBrowserVersion >= (BROWSER_VERSION_MAJOR<<8)+BROWSER_VERSION_MINOR)
  733. &&
  734. (!(PagedTransport->Wannish)
  735. ||
  736. (ServerEntry->ServerType & SV_TYPE_NT))
  737. &&
  738. _wcsicmp(ServerEntry->ServerName, Transport->DomainInfo->DomUnicodeComputerNameBuffer)
  739. ) {
  740. Status = AddBackupToBackupList(&BackupPointer, (PCHAR)BackupListResponse, ServerEntry);
  741. if (!NT_SUCCESS(Status)) {
  742. break;
  743. }
  744. //
  745. // And indicate we've packed another server entry.
  746. //
  747. NumberOfBackupServers += 1;
  748. //
  749. // We've packed another entry in the buffer, so decrement the
  750. // count.
  751. //
  752. Count -= 1;
  753. }
  754. //
  755. // Skip to the next entry in the list.
  756. //
  757. BackupEntry = BackupEntry->Flink;
  758. if (BackupEntry == &PagedTransport->BackupBrowserList) {
  759. BackupEntry = BackupEntry->Flink;
  760. }
  761. if (BackupEntry == TraverseStart) {
  762. break;
  763. }
  764. }
  765. dlog(DPRT_MASTER, ("\n"));
  766. //
  767. // If we've not satisfied the users request with our DC's, then
  768. // we want to fill the remainder of the list with ordinary backup
  769. // browsers.
  770. //
  771. BackupEntry = PagedTransport->BackupBrowserList.Flink;
  772. EntriesInList = PagedTransport->NumberOfBackupServerListEntries;
  773. // KdPrint(("There are %ld entries in the list\n", EntriesInList));
  774. dlog(DPRT_MASTER, ("Other servers: "));
  775. TraverseStart = BackupEntry;
  776. while ( Count && EntriesInList--) {
  777. PANNOUNCE_ENTRY ServerEntry = CONTAINING_RECORD(BackupEntry, ANNOUNCE_ENTRY, BackupLink);
  778. // KdPrint(("Check entry %ws. Flags: %lx\n", ServerEntry->ServerName, ServerEntry->ServerType));
  779. //
  780. // If this machine was a backup, and is now a master, it is
  781. // possible we might return ourselves in the list of backups.
  782. //
  783. // While this is not fatal, it can possibly cause problems,
  784. // so remove ourselves from the list and skip to the next server
  785. // in the list.
  786. //
  787. //
  788. // Since WfW machines don't support "double hops", we can't
  789. // return them to clients as legitimate backup servers.
  790. //
  791. //
  792. // Please note that we DO NOT include BDC's in this scan, since
  793. // we already included them in the previous pass.
  794. //
  795. if (
  796. (!(PagedTransport->Wannish)
  797. ||
  798. (ServerEntry->ServerType & SV_TYPE_NT))
  799. &&
  800. (ServerEntry->ServerBrowserVersion >= (BROWSER_VERSION_MAJOR<<8)+BROWSER_VERSION_MINOR)
  801. &&
  802. !(ServerEntry->ServerType & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL))
  803. &&
  804. _wcsicmp(ServerEntry->ServerName, Transport->DomainInfo->DomUnicodeComputerNameBuffer)
  805. ) {
  806. Status = AddBackupToBackupList(&BackupPointer, (PCHAR)BackupListResponse, ServerEntry);
  807. if (!NT_SUCCESS(Status)) {
  808. break;
  809. }
  810. //
  811. // And indicate we've packed another server entry.
  812. //
  813. NumberOfBackupServers += 1;
  814. //
  815. // We've packed another entry in the buffer, so decrement the
  816. // count.
  817. //
  818. Count -= 1;
  819. }
  820. //
  821. // Skip to the next entry in the list.
  822. //
  823. BackupEntry = BackupEntry->Flink;
  824. if (BackupEntry == &PagedTransport->BackupBrowserList) {
  825. BackupEntry = BackupEntry->Flink;
  826. }
  827. if (BackupEntry == TraverseStart) {
  828. break;
  829. }
  830. }
  831. dlog(DPRT_MASTER, ("\n"));
  832. #ifdef ENABLE_PSEUDO_BROWSER
  833. }
  834. #endif
  835. BackupListResponse->BackupListResponse.BackupServerCount = NumberOfBackupServers;
  836. // dlog(DPRT_MASTER, ("Responding to server %wZ on %ws with %lx (length %lx)\n", &UClientName,
  837. // PagedTransport->TransportName.Buffer,
  838. // BackupListResponse,
  839. // BackupPointer-(PUCHAR)BackupListResponse));
  840. //
  841. // Now send the response to the poor guy who requested it (finally)
  842. //
  843. Status = BowserSendSecondClassMailslot(Transport,
  844. &UClientName, // Name receiving data
  845. ComputerName, // Name type of destination
  846. BackupListResponse, // Datagram Buffer
  847. (ULONG)(BackupPointer-(PUCHAR)BackupListResponse), // Length.
  848. TRUE,
  849. MAILSLOT_BROWSER_NAME,
  850. &ClientAddress);
  851. try_exit:NOTHING;
  852. } finally {
  853. if (BackupListResponse != NULL) {
  854. FREE_POOL(BackupListResponse);
  855. }
  856. UNLOCK_ANNOUNCE_DATABASE(Transport);
  857. UNLOCK_TRANSPORT(Transport);
  858. BowserDereferenceTransportName(Context->TransportName);
  859. BowserDereferenceTransport(Transport);
  860. InterlockedDecrement( &BowserPostedDatagramCount );
  861. FREE_POOL(Context);
  862. }
  863. return;
  864. }
  865. NTSTATUS
  866. AddBackupToBackupList(
  867. IN PCHAR *BackupPointer,
  868. IN PCHAR BackupListStart,
  869. IN PANNOUNCE_ENTRY ServerEntry
  870. )
  871. {
  872. OEM_STRING OemBackupPointer;
  873. UNICODE_STRING UnicodeBackupPointer;
  874. NTSTATUS Status;
  875. PAGED_CODE();
  876. //
  877. // If we can't fit this entry in the list, then we've packed all we
  878. // can.
  879. //
  880. if (((*BackupPointer)+wcslen(ServerEntry->ServerName)+1)-BackupListStart >= BOWSER_BACKUP_LIST_RESPONSE_SIZE ) {
  881. return (STATUS_BUFFER_OVERFLOW);
  882. }
  883. dlog(DPRT_MASTER, ("%ws ", ServerEntry->ServerName));
  884. // KdPrint(("Add server %ws to list\n", ServerEntry->ServerName));
  885. OemBackupPointer.Buffer = (*BackupPointer);
  886. OemBackupPointer.MaximumLength = (USHORT)((ULONG_PTR)(BackupListStart + BOWSER_BACKUP_LIST_RESPONSE_SIZE) -
  887. (ULONG_PTR)(*BackupPointer));
  888. RtlInitUnicodeString(&UnicodeBackupPointer, ServerEntry->ServerName);
  889. Status = RtlUnicodeStringToOemString(&OemBackupPointer, &UnicodeBackupPointer, FALSE);
  890. if (!NT_SUCCESS(Status)) {
  891. return(Status);
  892. }
  893. (*BackupPointer) += OemBackupPointer.Length + 1;
  894. return STATUS_SUCCESS;
  895. }
  896. VOID
  897. BowserpInitializeGetBrowserServerList(
  898. VOID
  899. )
  900. {
  901. //
  902. // We want to delay for the average amount of time it takes to force an
  903. // election.
  904. //
  905. BowserGetBrowserListTimeout.QuadPart = Int32x32To64( 1000, -10000 );
  906. KeInitializeSpinLock(&BowserBackupListSpinLock);
  907. }
  908. VOID
  909. BowserpUninitializeGetBrowserServerList(
  910. VOID
  911. )
  912. {
  913. PAGED_CODE();
  914. return;
  915. }