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.

1272 lines
33 KiB

4 years ago
  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 (!ExAcquireResourceShared(&Transport->BrowserServerListResource, FALSE)) {
  111. return STATUS_SUCCESS;
  112. }
  113. if (Transport->PagedTransport->BrowserServerListBuffer != NULL) {
  114. if (!_wcsicmp(ServerName, Transport->PagedTransport->BrowserServerListBuffer[0])) {
  115. ExReleaseResource(&Transport->BrowserServerListResource);
  116. return STATUS_UNSUCCESSFUL;
  117. }
  118. }
  119. ExReleaseResource(&Transport->BrowserServerListResource);
  120. return STATUS_SUCCESS;
  121. }
  122. VOID
  123. BowserShuffleBrowserServerList(
  124. IN PWSTR *BrowserServerList,
  125. IN ULONG BrowserServerListLength,
  126. IN BOOLEAN IsPrimaryDomain
  127. )
  128. /*++
  129. Routine Description:
  130. This routine will shuffle the list of browser servers associated with
  131. a transport.
  132. Arguments:
  133. IN PTRANSPORT Transport - Supplies the transport whose buffer is to be freed
  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(BowserForEachTransport(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 (ExIsResourceAcquiredExclusive(&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. dprintf(DPRT_CLIENT, ("Unable to get browser server list - forcing election on net %wZ\n", &PagedTransport->TransportName));
  325. PagedTransport->Uptime = BowserTimeUp();
  326. if (BowserLogElectionPackets) {
  327. BowserWriteErrorLogEntry(EVENT_BOWSER_ELECTION_SENT_GETBLIST_FAILED, STATUS_SUCCESS, NULL, 0, 1, PagedTransport->TransportName.Buffer);
  328. }
  329. }
  330. //
  331. // If, after all this, we still timed out, return an error.
  332. //
  333. if (Status == STATUS_TIMEOUT) {
  334. //
  335. // If it has been less than the maximum amount of time for an election plus some
  336. // slop to allow the WfW machine to add the transport, don't
  337. // send the election packet.
  338. //
  339. if ((PagedTransport->Role == None)
  340. ||
  341. ((DomainName != NULL) &&
  342. !RtlEqualUnicodeString(DomainName, &Transport->PrimaryDomain->PagedTransportName->Name->Name, TRUE)
  343. )
  344. ||
  345. ((BowserTimeUp() - PagedTransport->LastElectionSeen) > ELECTION_TIME )
  346. ) {
  347. dprintf(DPRT_ELECT, ("Starting election on transport %wZ, domain %wZ. Time Up: %lx, LastElectionSeen: %lx\n", &PagedTransport->TransportName,
  348. (DomainName != NULL ? DomainName : &Transport->PrimaryDomain->PagedTransportName->Name->Name),
  349. BowserTimeUp(),
  350. Transport->PagedTransport->LastElectionSeen));
  351. BowserSendElection(DomainName,
  352. BrowserElection,
  353. Transport,
  354. FALSE);
  355. }
  356. try_return(Status = STATUS_NO_BROWSER_SERVERS_FOUND);
  357. }
  358. if (!NT_SUCCESS(Status)) {
  359. try_return(Status);
  360. }
  361. //
  362. // We now have a valid list of servers from the net.
  363. //
  364. // Massage this list into a form that we can return.
  365. //
  366. BackupList = BowserGetBackupServerListFromTransport(Transport);
  367. *BrowserServerListLength = BackupList->BackupServerCount;
  368. *BrowserServerList = ALLOCATE_POOL(PagedPool, *BrowserServerListLength*sizeof(PWSTR), POOL_BROWSERSERVERLIST);
  369. if (*BrowserServerList == NULL) {
  370. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  371. }
  372. RtlZeroMemory(*BrowserServerList, *BrowserServerListLength*sizeof(PWSTR));
  373. BackupPointer = BackupList->BackupServerList;
  374. for ( i = 0 ; i < (ULONG)BackupList->BackupServerCount ; i ++ ) {
  375. UNICODE_STRING UServerName;
  376. OEM_STRING AServerName;
  377. RtlInitAnsiString(&AServerName, BackupPointer);
  378. Status = RtlOemStringToUnicodeString(&UServerName, &AServerName, TRUE);
  379. if (!NT_SUCCESS(Status)) {
  380. try_return(Status);
  381. }
  382. (*BrowserServerList)[i] = ALLOCATE_POOL(PagedPool, UServerName.Length+(sizeof(WCHAR)*3), POOL_BROWSERSERVER);
  383. if ((*BrowserServerList)[i] == NULL) {
  384. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  385. }
  386. //
  387. // Put "\\" at the start of the server name.
  388. //
  389. RtlCopyMemory((*BrowserServerList)[i], L"\\\\", 6);
  390. dprintf(DPRT_CLIENT, ("Packing server name %ws to %lx\n", UServerName.Buffer, (*BrowserServerList)[i]));
  391. RtlCopyMemory(&((*BrowserServerList)[i])[2], UServerName.Buffer, UServerName.MaximumLength);
  392. //
  393. // Bump the pointer to the backup server name.
  394. //
  395. BackupPointer += AServerName.Length + sizeof(CHAR);
  396. RtlFreeUnicodeString(&UServerName);
  397. }
  398. //
  399. // Now shuffle the browser server list we got back from the server
  400. // to ensure some degree of randomness in the choice.
  401. //
  402. BowserShuffleBrowserServerList(
  403. *BrowserServerList,
  404. *BrowserServerListLength,
  405. (BOOLEAN)((DomainName == NULL) ||
  406. (Transport->PrimaryDomain != NULL &&
  407. RtlEqualUnicodeString(&Transport->PrimaryDomain->PagedTransportName->Name->Name, DomainName, TRUE))));
  408. try_return(Status = STATUS_SUCCESS);
  409. try_exit:NOTHING;
  410. } finally {
  411. if (!NT_SUCCESS(Status)) {
  412. if (*BrowserServerList != NULL) {
  413. for ( i = 0 ; i < *BrowserServerListLength ; i ++ ) {
  414. if ((*BrowserServerList)[i] != NULL) {
  415. FREE_POOL((*BrowserServerList)[i]);
  416. }
  417. }
  418. FREE_POOL(*BrowserServerList);
  419. *BrowserServerList = NULL;
  420. }
  421. *BrowserServerListLength = 0;
  422. BowserFreeTransportBackupList(Transport);
  423. BackupList = NULL;
  424. }
  425. if (BackupList != NULL) {
  426. FREE_POOL(BackupList);
  427. }
  428. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  429. }
  430. return Status;
  431. }
  432. DATAGRAM_HANDLER(
  433. BowserGetBackupListResponse
  434. )
  435. /*++
  436. Routine Description:
  437. This routine is the indication time processing needed to get a backup
  438. list response.
  439. Arguments:
  440. IN PTRANSPORT_NAME TransportName - Supplies the transport name receiving
  441. the request.
  442. IN PBACKUP_LIST_RESPONSE_1 BackupList - Supplies the backup server list
  443. IN ULONG BytesAvailable - Supplies the # of bytes in the message
  444. OUT PULONG BytesTaken;
  445. Return Value:
  446. None.
  447. --*/
  448. {
  449. PTRANSPORT Transport = TransportName->Transport;
  450. PBACKUP_LIST_RESPONSE_1 BackupList = Buffer;
  451. KIRQL OldIrql;
  452. if (Transport->BowserBackupList == NULL) {
  453. dlog(DPRT_CLIENT, ("BOWSER: Received GetBackupListResponse while not expecting one\n"));
  454. return STATUS_REQUEST_NOT_ACCEPTED;
  455. }
  456. ASSERT ( BytesAvailable <= Transport->DatagramSize );
  457. ACQUIRE_SPIN_LOCK(&BowserBackupListSpinLock, &OldIrql);
  458. //
  459. // This response is for an old request - ignore it.
  460. //
  461. if (BackupList->Token != Transport->BrowserServerListToken) {
  462. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  463. return(STATUS_REQUEST_NOT_ACCEPTED);
  464. }
  465. //
  466. // Bump the token again to invalidate any incoming responses - they are
  467. // no longer valid.
  468. //
  469. Transport->BrowserServerListToken += 1;
  470. if (Transport->BowserBackupList != NULL) {
  471. //
  472. // Copy the received buffer.
  473. //
  474. TdiCopyLookaheadData(Transport->BowserBackupList, BackupList, BytesAvailable, ReceiveFlags);
  475. KeSetEvent(&Transport->GetBackupListComplete, IO_NETWORK_INCREMENT, FALSE);
  476. }
  477. RELEASE_SPIN_LOCK(&BowserBackupListSpinLock, OldIrql);
  478. return(STATUS_SUCCESS);
  479. UNREFERENCED_PARAMETER(BytesTaken);
  480. }
  481. NTSTATUS
  482. BowserSendBackupListRequest(
  483. IN PTRANSPORT Transport,
  484. IN PUNICODE_STRING Domain
  485. )
  486. /*++
  487. Routine Description:
  488. This routine sends a getbackup list request to the master browser server
  489. for a specified domain.
  490. Arguments:
  491. IN PTRANSPORT_NAME TransportName - Supplies the transport name receiving
  492. the request.
  493. IN PBACKUP_LIST_RESPONSE_1 BackupList - Supplies the backup server list
  494. IN ULONG BytesAvailable - Supplies the # of bytes in the message
  495. OUT PULONG BytesTaken;
  496. Return Value:
  497. None.
  498. --*/
  499. {
  500. NTSTATUS Status;
  501. BACKUP_LIST_REQUEST Request;
  502. PAGED_CODE();
  503. Request.Type = GetBackupListReq;
  504. //
  505. // Send this request.
  506. //
  507. Request.BackupListRequest.Token = Transport->BrowserServerListToken;
  508. //
  509. // WinBALL only asks for 4 of these, so that's what I'll ask for.
  510. //
  511. Request.BackupListRequest.RequestedCount = 4;
  512. if (Domain == NULL &&
  513. Transport->PrimaryDomain == NULL) {
  514. return(STATUS_REDIRECTOR_NOT_STARTED);
  515. }
  516. Status = BowserSendSecondClassMailslot(Transport,
  517. (Domain == NULL ?
  518. &Transport->PrimaryDomain->PagedTransportName->Name->Name :
  519. Domain),
  520. MasterBrowser,
  521. &Request, sizeof(Request), TRUE,
  522. MAILSLOT_BROWSER_NAME,
  523. NULL);
  524. if (!NT_SUCCESS(Status)) {
  525. return Status;
  526. }
  527. if (!FlagOn(Transport->PagedTransport->Flags, DIRECT_HOST_IPX)) {
  528. Status = BowserSendSecondClassMailslot(Transport,
  529. (Domain == NULL ?
  530. &Transport->PrimaryDomain->PagedTransportName->Name->Name :
  531. Domain),
  532. PrimaryDomainBrowser,
  533. &Request, sizeof(Request), TRUE,
  534. MAILSLOT_BROWSER_NAME,
  535. NULL);
  536. }
  537. return Status;
  538. }
  539. DATAGRAM_HANDLER(
  540. BowserGetBackupListRequest
  541. )
  542. {
  543. NTSTATUS status;
  544. //
  545. // We need to have at least enough bytes of data to read in
  546. // a BACKUP_LIST_REQUEST_1 structure.
  547. //
  548. if (BytesAvailable < sizeof(BACKUP_LIST_REQUEST_1)) {
  549. return STATUS_REQUEST_NOT_ACCEPTED;
  550. }
  551. BowserStatistics.NumberOfGetBrowserServerListRequests += 1;
  552. status = BowserPostDatagramToWorkerThread(
  553. TransportName,
  554. Buffer,
  555. BytesAvailable,
  556. BytesTaken,
  557. SourceAddress,
  558. SourceAddressLength,
  559. SourceName,
  560. SourceNameLength,
  561. BowserGetBackupListWorker,
  562. NonPagedPool,
  563. DelayedWorkQueue,
  564. ReceiveFlags,
  565. TRUE); // Response will be sent.
  566. if (!NT_SUCCESS(status)) {
  567. BowserNumberOfMissedGetBrowserServerListRequests += 1;
  568. BowserStatistics.NumberOfMissedGetBrowserServerListRequests += 1;
  569. return status;
  570. }
  571. return status;
  572. }
  573. VOID
  574. BowserGetBackupListWorker(
  575. IN PVOID Ctx
  576. )
  577. {
  578. PPOST_DATAGRAM_CONTEXT Context = Ctx;
  579. PBACKUP_LIST_REQUEST_1 BackupListRequest = Context->Buffer;
  580. PIRP Irp = NULL;
  581. PTRANSPORT Transport = Context->TransportName->Transport;
  582. STRING ClientAddress;
  583. NTSTATUS Status;
  584. PBACKUP_LIST_RESPONSE BackupListResponse = NULL;
  585. PCHAR ClientName = Context->ClientName;
  586. UNICODE_STRING UClientName;
  587. OEM_STRING AClientName;
  588. WCHAR ClientNameBuffer[LM20_CNLEN+1];
  589. PAGED_CODE();
  590. ClientAddress.Buffer = Context->TdiClientAddress;
  591. ClientAddress.Length = ClientAddress.MaximumLength = Context->ClientAddressLength;
  592. UClientName.Buffer = ClientNameBuffer;
  593. UClientName.MaximumLength = (LM20_CNLEN+1)*sizeof(WCHAR);
  594. RtlInitAnsiString(&AClientName, Context->ClientName);
  595. Status = RtlOemStringToUnicodeString(&UClientName, &AClientName, FALSE);
  596. if (!NT_SUCCESS(Status)) {
  597. BowserWriteErrorLogEntry(EVENT_BOWSER_NAME_CONVERSION_FAILED, Status, AClientName.Buffer, AClientName.Length, 0);
  598. BowserDereferenceTransportName(Context->TransportName);
  599. BowserDereferenceTransport(Transport);
  600. InterlockedDecrement( &BowserPostedDatagramCount );
  601. FREE_POOL(Context);
  602. return;
  603. }
  604. //
  605. // Lock the transport to allow us access to the list. This prevents
  606. // any role changes while we're responding to the caller.
  607. //
  608. LOCK_TRANSPORT_SHARED(Transport);
  609. //
  610. // Do nothing if we're not a master browser. This can happen if
  611. // we're running on the PDC, and aren't the master for some reason (for
  612. // instance, if the master browser is running a newer version of the
  613. // browser).
  614. //
  615. if ( Transport->PagedTransport->Role != Master ) {
  616. UNLOCK_TRANSPORT(Transport);
  617. BowserDereferenceTransportName(Context->TransportName);
  618. BowserDereferenceTransport(Transport);
  619. InterlockedDecrement( &BowserPostedDatagramCount );
  620. FREE_POOL(Context);
  621. return;
  622. }
  623. LOCK_ANNOUNCE_DATABASE_SHARED(Transport);
  624. try {
  625. PUCHAR BackupPointer;
  626. PLIST_ENTRY BackupEntry;
  627. PLIST_ENTRY TraverseStart;
  628. USHORT Count;
  629. UCHAR NumberOfBackupServers = 0;
  630. ULONG EntriesInList;
  631. PPAGED_TRANSPORT PagedTransport = Transport->PagedTransport;
  632. BackupListResponse = ALLOCATE_POOL(PagedPool, BOWSER_BACKUP_LIST_RESPONSE_SIZE, POOL_BACKUPLIST_RESP);
  633. //
  634. // If we can't allocate the buffer, just bail out.
  635. //
  636. if (BackupListResponse == NULL) {
  637. try_return(NOTHING);
  638. }
  639. BackupListResponse->Type = GetBackupListResp;
  640. BackupListResponse->BackupListResponse.BackupServerCount = 0;
  641. //
  642. // Set the token to the clients requested value
  643. //
  644. SmbPutUlong(&BackupListResponse->BackupListResponse.Token, BackupListRequest->Token);
  645. BackupPointer = BackupListResponse->BackupListResponse.BackupServerList;
  646. //
  647. // Since we're a backup browser, make sure that at least our name is
  648. // in the list.
  649. //
  650. {
  651. OEM_STRING AnsiBackupPointer;
  652. AnsiBackupPointer.Buffer = BackupPointer;
  653. AnsiBackupPointer.MaximumLength =
  654. ((PUCHAR)BackupListResponse + BOWSER_BACKUP_LIST_RESPONSE_SIZE) -
  655. BackupPointer;
  656. Status = RtlUpcaseUnicodeStringToOemString(&AnsiBackupPointer, &Transport->ComputerName->PagedTransportName->Name->Name, FALSE);
  657. if (!NT_SUCCESS(Status)) {
  658. InternalError(("Unable to convert browser name %wZ to ansi: %lx\n", &Transport->ComputerName->PagedTransportName->Name->Name, Status));
  659. try_return(NOTHING);
  660. }
  661. //
  662. // Bump pointer by size of string.
  663. //
  664. BackupPointer += AnsiBackupPointer.Length+sizeof(CHAR);
  665. }
  666. Count = BackupListRequest->RequestedCount - 1;
  667. NumberOfBackupServers += 1;
  668. //
  669. // Walk the list of servers forward by the Last DC returned # of elements
  670. //
  671. BackupEntry = PagedTransport->BackupBrowserList.Flink;
  672. EntriesInList = PagedTransport->NumberOfBackupServerListEntries;
  673. // KdPrint(("There are %ld entries in the list\n", EntriesInList));
  674. TraverseStart = BackupEntry;
  675. //
  676. // Try to find DC's and BDC's to satisfy the users request
  677. // first. They presumably are more appropriate to be returned
  678. // anyway.
  679. //
  680. dlog(DPRT_MASTER, ("Advanced servers: "));
  681. while ( Count && EntriesInList -- ) {
  682. PANNOUNCE_ENTRY ServerEntry = CONTAINING_RECORD(BackupEntry, ANNOUNCE_ENTRY, BackupLink);
  683. // KdPrint(("Check entry %ws. Flags: %lx\n", ServerEntry->ServerName, ServerEntry->ServerType));
  684. //
  685. // If this machine was a backup, and is now a master, it is
  686. // possible we might return ourselves in the list of backups.
  687. //
  688. // While this is not fatal, it can possibly cause problems,
  689. // so remove ourselves from the list and skip to the next server
  690. // in the list.
  691. //
  692. //
  693. // Since WfW machines don't support "double hops", we can't
  694. // return them to clients as legitimate backup servers.
  695. //
  696. if (
  697. (ServerEntry->ServerType & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL))
  698. &&
  699. (ServerEntry->ServerBrowserVersion >= (BROWSER_VERSION_MAJOR<<8)+BROWSER_VERSION_MINOR)
  700. &&
  701. (!(PagedTransport->Wannish)
  702. ||
  703. (ServerEntry->ServerType & SV_TYPE_NT))
  704. &&
  705. _wcsicmp(ServerEntry->ServerName, Transport->ComputerName->PagedTransportName->Name->Name.Buffer)
  706. ) {
  707. Status = AddBackupToBackupList(&BackupPointer, (PCHAR)BackupListResponse, ServerEntry);
  708. if (!NT_SUCCESS(Status)) {
  709. break;
  710. }
  711. //
  712. // And indicate we've packed another server entry.
  713. //
  714. NumberOfBackupServers += 1;
  715. //
  716. // We've packed another entry in the buffer, so decrement the
  717. // count.
  718. //
  719. Count -= 1;
  720. }
  721. //
  722. // Skip to the next entry in the list.
  723. //
  724. BackupEntry = BackupEntry->Flink;
  725. if (BackupEntry == &PagedTransport->BackupBrowserList) {
  726. BackupEntry = BackupEntry->Flink;
  727. }
  728. if (BackupEntry == TraverseStart) {
  729. break;
  730. }
  731. }
  732. dlog(DPRT_MASTER, ("\n"));
  733. //
  734. // If we've not satisfied the users request with our DC's, then
  735. // we want to fill the remainder of the list with ordinary backup
  736. // browsers.
  737. //
  738. BackupEntry = PagedTransport->BackupBrowserList.Flink;
  739. EntriesInList = PagedTransport->NumberOfBackupServerListEntries;
  740. // KdPrint(("There are %ld entries in the list\n", EntriesInList));
  741. dlog(DPRT_MASTER, ("Other servers: "));
  742. TraverseStart = BackupEntry;
  743. while ( Count && EntriesInList--) {
  744. PANNOUNCE_ENTRY ServerEntry = CONTAINING_RECORD(BackupEntry, ANNOUNCE_ENTRY, BackupLink);
  745. // KdPrint(("Check entry %ws. Flags: %lx\n", ServerEntry->ServerName, ServerEntry->ServerType));
  746. //
  747. // If this machine was a backup, and is now a master, it is
  748. // possible we might return ourselves in the list of backups.
  749. //
  750. // While this is not fatal, it can possibly cause problems,
  751. // so remove ourselves from the list and skip to the next server
  752. // in the list.
  753. //
  754. //
  755. // Since WfW machines don't support "double hops", we can't
  756. // return them to clients as legitimate backup servers.
  757. //
  758. //
  759. // Please note that we DO NOT include BDC's in this scan, since
  760. // we already included them in the previous pass.
  761. //
  762. if (
  763. (!(PagedTransport->Wannish)
  764. ||
  765. (ServerEntry->ServerType & SV_TYPE_NT))
  766. &&
  767. (ServerEntry->ServerBrowserVersion >= (BROWSER_VERSION_MAJOR<<8)+BROWSER_VERSION_MINOR)
  768. &&
  769. !(ServerEntry->ServerType & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL))
  770. &&
  771. _wcsicmp(ServerEntry->ServerName, Transport->ComputerName->PagedTransportName->Name->Name.Buffer)
  772. ) {
  773. Status = AddBackupToBackupList(&BackupPointer, (PCHAR)BackupListResponse, ServerEntry);
  774. if (!NT_SUCCESS(Status)) {
  775. break;
  776. }
  777. //
  778. // And indicate we've packed another server entry.
  779. //
  780. NumberOfBackupServers += 1;
  781. //
  782. // We've packed another entry in the buffer, so decrement the
  783. // count.
  784. //
  785. Count -= 1;
  786. }
  787. //
  788. // Skip to the next entry in the list.
  789. //
  790. BackupEntry = BackupEntry->Flink;
  791. if (BackupEntry == &PagedTransport->BackupBrowserList) {
  792. BackupEntry = BackupEntry->Flink;
  793. }
  794. if (BackupEntry == TraverseStart) {
  795. break;
  796. }
  797. }
  798. dlog(DPRT_MASTER, ("\n"));
  799. BackupListResponse->BackupListResponse.BackupServerCount = NumberOfBackupServers;
  800. // dlog(DPRT_MASTER, ("Responding to server %wZ on %ws with %lx (length %lx)\n", &UClientName,
  801. // PagedTransport->TransportName.Buffer,
  802. // BackupListResponse,
  803. // BackupPointer-(PUCHAR)BackupListResponse));
  804. //
  805. // Now send the response to the poor guy who requested it (finally)
  806. //
  807. Status = BowserSendSecondClassMailslot(Transport,
  808. &UClientName, // Name receiving data
  809. ComputerName, // Name type of destination
  810. BackupListResponse, // Datagram Buffer
  811. BackupPointer-(PUCHAR)BackupListResponse, // Length.
  812. TRUE,
  813. MAILSLOT_BROWSER_NAME,
  814. &ClientAddress);
  815. try_exit:NOTHING;
  816. } finally {
  817. if (BackupListResponse != NULL) {
  818. FREE_POOL(BackupListResponse);
  819. }
  820. UNLOCK_ANNOUNCE_DATABASE(Transport);
  821. UNLOCK_TRANSPORT(Transport);
  822. BowserDereferenceTransportName(Context->TransportName);
  823. BowserDereferenceTransport(Transport);
  824. InterlockedDecrement( &BowserPostedDatagramCount );
  825. FREE_POOL(Context);
  826. }
  827. return;
  828. }
  829. NTSTATUS
  830. AddBackupToBackupList(
  831. IN PCHAR *BackupPointer,
  832. IN PCHAR BackupListStart,
  833. IN PANNOUNCE_ENTRY ServerEntry
  834. )
  835. {
  836. OEM_STRING OemBackupPointer;
  837. UNICODE_STRING UnicodeBackupPointer;
  838. NTSTATUS Status;
  839. PAGED_CODE();
  840. //
  841. // If we can't fit this entry in the list, then we've packed all we
  842. // can.
  843. //
  844. if (((*BackupPointer)+wcslen(ServerEntry->ServerName)+1)-BackupListStart >= BOWSER_BACKUP_LIST_RESPONSE_SIZE ) {
  845. return (STATUS_BUFFER_OVERFLOW);
  846. }
  847. dlog(DPRT_MASTER, ("%ws ", ServerEntry->ServerName));
  848. // KdPrint(("Add server %ws to list\n", ServerEntry->ServerName));
  849. OemBackupPointer.Buffer = (*BackupPointer);
  850. OemBackupPointer.MaximumLength = (USHORT)((ULONG)(*BackupPointer) -
  851. (ULONG)(BackupListStart + BOWSER_BACKUP_LIST_RESPONSE_SIZE));
  852. RtlInitUnicodeString(&UnicodeBackupPointer, ServerEntry->ServerName);
  853. Status = RtlUnicodeStringToOemString(&OemBackupPointer, &UnicodeBackupPointer, FALSE);
  854. if (!NT_SUCCESS(Status)) {
  855. return(Status);
  856. }
  857. (*BackupPointer) += OemBackupPointer.Length + 1;
  858. return STATUS_SUCCESS;
  859. }
  860. VOID
  861. BowserpInitializeGetBrowserServerList(
  862. VOID
  863. )
  864. {
  865. //
  866. // We want to delay for the average amount of time it takes to force an
  867. // election.
  868. //
  869. BowserGetBrowserListTimeout.QuadPart = Int32x32To64( 1000, -10000 );
  870. KeInitializeSpinLock(&BowserBackupListSpinLock);
  871. }
  872. VOID
  873. BowserpUninitializeGetBrowserServerList(
  874. VOID
  875. )
  876. {
  877. PAGED_CODE();
  878. return;
  879. }