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.

1355 lines
39 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. Scavengr.c
  5. Abstract:
  6. This module contains the code for the server service scavenger
  7. thread. This thread handles announcements and configuration
  8. changes. (Although originally written to run in a separate thread,
  9. this code now runs in the initial thread of the server service.
  10. Author:
  11. David Treadwell (davidtr) 17-Apr-1991
  12. Revision History:
  13. --*/
  14. #include "srvsvcp.h"
  15. #include "ssreg.h"
  16. #include <netlibnt.h>
  17. #include <tstr.h>
  18. #define INCLUDE_SMB_TRANSACTION
  19. #undef NT_PIPE_PREFIX
  20. #include <smbtypes.h>
  21. #include <smb.h>
  22. #include <smbtrans.h>
  23. #include <smbgtpt.h>
  24. #include <hostannc.h>
  25. #include <ntddbrow.h>
  26. #include <lmerr.h>
  27. #define TERMINATION_SIGNALED 0
  28. #define ANNOUNCE_SIGNALED 1
  29. #define STATUS_CHANGED 2
  30. #define DOMAIN_CHANGED 3
  31. #define REGISTRY_CHANGED 4 // Must be the last one
  32. #define NUMBER_OF_EVENTS 5
  33. //
  34. // Bias request announcements by SERVER_REQUEST_ANNOUNCE_DELTA SECONDS
  35. //
  36. #define SERVER_REQUEST_ANNOUNCE_DELTA 30
  37. //
  38. // Forward declarations.
  39. //
  40. VOID
  41. Announce (
  42. IN BOOL DoNtAnnouncement,
  43. IN DWORD NtInterval,
  44. IN BOOL DoLmAnnouncement,
  45. IN BOOL TerminationAnnouncement
  46. );
  47. NET_API_STATUS
  48. SendSecondClassMailslot (
  49. IN LPTSTR Transport OPTIONAL,
  50. IN PVOID Message,
  51. IN DWORD MessageLength,
  52. IN LPTSTR Domain,
  53. IN LPSTR MailslotNameText,
  54. IN UCHAR SignatureByte
  55. );
  56. NET_API_STATUS
  57. SsBrowserIoControl (
  58. IN DWORD IoControlCode,
  59. IN PVOID Buffer,
  60. IN DWORD BufferLength,
  61. IN PLMDR_REQUEST_PACKET Packet,
  62. IN DWORD PacketLength
  63. );
  64. DWORD
  65. ComputeAnnounceTime (
  66. IN DWORD LastAnnouncementTime,
  67. IN DWORD Interval
  68. )
  69. /*++
  70. Routine Description:
  71. Compute the time to wait (in milliseconds) until the next announcement should
  72. be made.
  73. Arguments:
  74. LastAnnouncementTime - Time (in milliseconds since reboot) when the last
  75. announcement was made.
  76. Interval - Interval (in seconds) between announcements
  77. Return Value:
  78. Timeout period (in milliseconds)
  79. --*/
  80. {
  81. DWORD AnnounceDelta;
  82. DWORD Timeout;
  83. DWORD CurrentTime;
  84. //
  85. // Get the current time.
  86. //
  87. CurrentTime = GetTickCount();
  88. //
  89. // If the clock has gone backward,
  90. // send an announcement now.
  91. //
  92. if ( LastAnnouncementTime > CurrentTime ) {
  93. return 0;
  94. }
  95. //
  96. // Convert the announcement period from seconds to milliseconds.
  97. //
  98. Timeout = Interval * 1000;
  99. //
  100. // Add in the random announce delta which helps prevent lots of
  101. // servers from announcing at the same time.
  102. //
  103. AnnounceDelta = SsData.ServerInfo102.sv102_anndelta;
  104. Timeout += ((rand( ) * AnnounceDelta * 2) / RAND_MAX) -
  105. AnnounceDelta;
  106. //
  107. // If our time has expired,
  108. // send an announcement now.
  109. //
  110. if ( (CurrentTime - LastAnnouncementTime) >= Timeout ) {
  111. return 0;
  112. }
  113. //
  114. // Adjust our timeout period for time already elapsed.
  115. //
  116. return Timeout - (CurrentTime - LastAnnouncementTime);
  117. }
  118. DWORD
  119. SsScavengerThread (
  120. IN LPVOID lpThreadParameter
  121. )
  122. /*++
  123. Routine Description:
  124. This routine implements the server service scavenger thread.
  125. Arguments:
  126. lpThreadParameter - ignored.
  127. Return Value:
  128. NET_API_STATUS - thread termination result.
  129. --*/
  130. {
  131. HANDLE events[ NUMBER_OF_EVENTS ];
  132. ULONG numEvents = NUMBER_OF_EVENTS-1;
  133. UNICODE_STRING unicodeEventName;
  134. OBJECT_ATTRIBUTES obja;
  135. DWORD waitStatus;
  136. DWORD timeout;
  137. DWORD LmTimeout;
  138. BOOL DoLmAnnouncement;
  139. DWORD LmLastAnnouncementTime;
  140. DWORD NtTimeout;
  141. BOOL DoNtAnnouncement;
  142. DWORD NtLastAnnouncementTime;
  143. DWORD NtInterval;
  144. NTSTATUS status;
  145. BOOL hidden = TRUE;
  146. HKEY hParameters = INVALID_HANDLE_VALUE;
  147. lpThreadParameter;
  148. //
  149. // Use the scavenger termination event to know when we're supposed
  150. // to wake up and kill ourselves.
  151. //
  152. events[TERMINATION_SIGNALED] = SsData.SsTerminationEvent;
  153. //
  154. // Initialize the NT announcement interval to the LM announcement interval
  155. //
  156. NtInterval = SsData.ServerInfo102.sv102_announce;
  157. DoLmAnnouncement = TRUE;
  158. DoNtAnnouncement = TRUE;
  159. //
  160. // Create the announce event. When this gets signaled, we wake up
  161. // and do an announcement. We use a synchronization event rather
  162. // than a notification event so that we don't have to worry about
  163. // resetting the event after we wake up.
  164. //
  165. //
  166. // Please note that we create this event with OBJ_OPENIF. We do this
  167. // to allow the browser to signal the server to force an announcement.
  168. //
  169. // The bowser will create this event as a part of the bowser
  170. // initialization, and will set it to the signalled state when it needs
  171. // to have the server announce.
  172. //
  173. RtlInitUnicodeString( &unicodeEventName, SERVER_ANNOUNCE_EVENT_W );
  174. InitializeObjectAttributes( &obja, &unicodeEventName, OBJ_OPENIF, NULL, NULL );
  175. status = NtCreateEvent(
  176. &SsData.SsAnnouncementEvent,
  177. SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
  178. &obja,
  179. SynchronizationEvent,
  180. FALSE
  181. );
  182. if ( !NT_SUCCESS(status) ) {
  183. SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n",
  184. status ));
  185. return NetpNtStatusToApiStatus( status );
  186. }
  187. events[ANNOUNCE_SIGNALED] = SsData.SsAnnouncementEvent;
  188. //
  189. // Create an unnamed event to be set to the signalled state when the
  190. // service status changes (or a local application requests an
  191. // announcement)
  192. //
  193. InitializeObjectAttributes( &obja, NULL, OBJ_OPENIF, NULL, NULL );
  194. status = NtCreateEvent(
  195. &SsData.SsStatusChangedEvent,
  196. SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
  197. &obja,
  198. SynchronizationEvent,
  199. FALSE
  200. );
  201. if ( !NT_SUCCESS(status) ) {
  202. SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n",
  203. status ));
  204. NtClose( SsData.SsAnnouncementEvent );
  205. SsData.SsAnnouncementEvent = NULL;
  206. return NetpNtStatusToApiStatus( status );
  207. }
  208. events[STATUS_CHANGED] = SsData.SsStatusChangedEvent;
  209. events[ DOMAIN_CHANGED ] = SsData.SsDomainNameChangeEvent ?
  210. SsData.SsDomainNameChangeEvent : INVALID_HANDLE_VALUE;
  211. //
  212. // Put a watch on the registry for any changes that happen in the
  213. // null session share or pipe list. Don't bail out if this fails,
  214. // because we've done this as a convenience in adding new null
  215. // session-reliant servers. It doesn't really affect the normal
  216. // operation of the server if this doesn't work.
  217. //
  218. events[ REGISTRY_CHANGED ] = INVALID_HANDLE_VALUE;
  219. status = NtCreateEvent(
  220. &events[ REGISTRY_CHANGED ],
  221. SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE,
  222. NULL,
  223. SynchronizationEvent,
  224. FALSE
  225. );
  226. if ( NT_SUCCESS(status) ) {
  227. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  228. FULL_PARAMETERS_REGISTRY_PATH,
  229. 0,
  230. KEY_NOTIFY,
  231. &hParameters
  232. );
  233. if( status == ERROR_SUCCESS ) {
  234. (void)RegNotifyChangeKeyValue( hParameters,
  235. TRUE,
  236. REG_NOTIFY_CHANGE_LAST_SET,
  237. events[ REGISTRY_CHANGED ],
  238. TRUE
  239. );
  240. //
  241. // Add this event to the list of events we're waiting for
  242. //
  243. ++numEvents;
  244. }
  245. }
  246. //
  247. // Seed the random number generator. We use it to generate random
  248. // announce deltas.
  249. //
  250. srand( PtrToUlong(SsData.SsAnnouncementEvent) );
  251. //
  252. // Do an announcement immediately for startup, then loop announcing
  253. // based on the announce interval.
  254. //
  255. waitStatus = WAIT_TIMEOUT;
  256. do {
  257. //
  258. // Act according to whether the termination event, the announce
  259. // event, or the timeout caused us to wake up.
  260. //
  261. // !!! Or the configuration event indicating a configuration
  262. // change notification.
  263. if ( waitStatus == WAIT_FAILED ) {
  264. //
  265. // Don't consume all the CPU just because an error happened
  266. //
  267. Sleep(1000);
  268. } else if ( waitStatus == WAIT_OBJECT_0 + TERMINATION_SIGNALED ) {
  269. SS_PRINT(( "Scavenger: termination event signaled\n" ));
  270. //
  271. // The scavenger termination event was signaled, so we have
  272. // to gracefully kill this thread. If this is not a hidden
  273. // server, announce the fact that we're going down.
  274. //
  275. if ( !hidden ) {
  276. Announce( TRUE, NtInterval, TRUE, TRUE );
  277. }
  278. //
  279. // Close the announcement event.
  280. //
  281. NtClose( SsData.SsAnnouncementEvent );
  282. SsData.SsAnnouncementEvent = NULL;
  283. //
  284. // Close the Registry watch event.
  285. //
  286. if( events[ REGISTRY_CHANGED ] != INVALID_HANDLE_VALUE )
  287. NtClose( events[ REGISTRY_CHANGED ] );
  288. //
  289. // Close the Registry handle
  290. //
  291. if( hParameters != INVALID_HANDLE_VALUE )
  292. RegCloseKey( hParameters );
  293. //
  294. // Return to caller.
  295. //
  296. return NO_ERROR;
  297. } else if( waitStatus == WAIT_OBJECT_0 + REGISTRY_CHANGED ) {
  298. //
  299. // Somebody changed some server parameters. Tell the driver
  300. //
  301. SS_PRINT(( "SsScavengerThread: Server parameters changed\n" ));
  302. //
  303. // Tell the server FSD to look for a change in the registry
  304. //
  305. (void)SsServerFsControl( FSCTL_SRV_REGISTRY_CHANGE, NULL, NULL, 0 );
  306. //
  307. // Turn it back on so we get future changes, too
  308. //
  309. (void)RegNotifyChangeKeyValue( hParameters,
  310. TRUE,
  311. REG_NOTIFY_CHANGE_LAST_SET,
  312. events[ REGISTRY_CHANGED ],
  313. TRUE
  314. );
  315. } else if( waitStatus == WAIT_OBJECT_0 + DOMAIN_CHANGED ) {
  316. SsSetDomainName();
  317. } else {
  318. SS_ASSERT( waitStatus == WAIT_TIMEOUT ||
  319. waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED ||
  320. waitStatus == WAIT_OBJECT_0 + STATUS_CHANGED );
  321. //
  322. // If we've timed out,
  323. // we've already set the flags indicating whether to announce
  324. // on to lanman to NT browsers
  325. //
  326. if ( waitStatus != WAIT_TIMEOUT ) {
  327. DoLmAnnouncement = TRUE;
  328. DoNtAnnouncement = TRUE;
  329. }
  330. //
  331. // If we're not a hidden server, announce ourselves.
  332. //
  333. // Hold the database resource while we do the announcement so
  334. // that we get a consistent view of the database.
  335. //
  336. (VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
  337. if ( !SsData.ServerInfo102.sv102_hidden ) {
  338. hidden = FALSE;
  339. Announce( DoNtAnnouncement, NtInterval, DoLmAnnouncement, FALSE );
  340. //
  341. // If we were not hidden last time through the loop but
  342. // we're hidden now, we've changed to hidden, so announce
  343. // that we're going down. This causes clients in the domain
  344. // to take us out of their server enumerations.
  345. //
  346. } else if ( !hidden ) {
  347. hidden = TRUE;
  348. Announce( TRUE, NtInterval, TRUE, TRUE );
  349. }
  350. //
  351. // If the server is hidden, the wait timeout is infinite. We'll
  352. // be woken up by the announce event if the server becomes
  353. // unhidden.
  354. //
  355. if ( SsData.ServerInfo102.sv102_hidden ) {
  356. timeout = 0xffffffff;
  357. } else {
  358. //
  359. // Remember when the last announcement was.
  360. //
  361. if ( DoNtAnnouncement ) {
  362. NtLastAnnouncementTime = GetTickCount();
  363. }
  364. if ( DoLmAnnouncement ) {
  365. LmLastAnnouncementTime = GetTickCount();
  366. }
  367. //
  368. // Compute the time delta to the next announcement
  369. //
  370. // For NtInterval,
  371. // use a local copy of the interval since we compute the correct
  372. // value.
  373. //
  374. // For the Lanman interval,
  375. // use the global copy to allow the interval to be changed.
  376. //
  377. NtTimeout = ComputeAnnounceTime(
  378. NtLastAnnouncementTime,
  379. NtInterval );
  380. if (SsData.ServerInfo599.sv599_lmannounce) {
  381. LmTimeout = ComputeAnnounceTime(
  382. LmLastAnnouncementTime,
  383. SsData.ServerInfo102.sv102_announce );
  384. } else {
  385. // Don't awaken this thread to do nothing.
  386. LmTimeout = 0xffffffff;
  387. }
  388. //
  389. // If our NT announcement frequency is less than 12 minutes,
  390. // increase our announcement frequency by 4 minutes.
  391. //
  392. if ( NtInterval < 12 * 60 ) {
  393. NtInterval += 4 * 60;
  394. if ( NtInterval > 12 * 60) {
  395. NtInterval = 12 * 60;
  396. }
  397. }
  398. //
  399. // Determine which timeout we're actually going to use.
  400. //
  401. if ( NtTimeout == LmTimeout ) {
  402. timeout = NtTimeout;
  403. DoLmAnnouncement = TRUE;
  404. DoNtAnnouncement = TRUE;
  405. } else if ( NtTimeout < LmTimeout ) {
  406. timeout = NtTimeout;
  407. DoLmAnnouncement = FALSE;
  408. DoNtAnnouncement = TRUE;
  409. } else {
  410. timeout = LmTimeout;
  411. DoLmAnnouncement = TRUE;
  412. DoNtAnnouncement = FALSE;
  413. }
  414. }
  415. RtlReleaseResource( &SsData.SsServerInfoResource );
  416. }
  417. //
  418. // Wait for one of the events to be signaled or for the timeout
  419. // to elapse.
  420. //
  421. waitStatus = WaitForMultipleObjects( numEvents , events, FALSE, timeout );
  422. if ( waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED ) {
  423. //
  424. // We were awakened because an announce was signalled.
  425. // Unless we are a master browser on at least one transport,
  426. // delay for a random delta to stagger announcements a bit
  427. // to prevent lots of servers from announcing
  428. // simultaneously.
  429. //
  430. BOOL isMasterBrowser = FALSE;
  431. PNAME_LIST_ENTRY service;
  432. PTRANSPORT_LIST_ENTRY transport;
  433. RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
  434. for( service = SsData.SsServerNameList;
  435. isMasterBrowser == FALSE && service != NULL;
  436. service = service->Next ) {
  437. if( service->ServiceBits & SV_TYPE_MASTER_BROWSER ) {
  438. isMasterBrowser = TRUE;
  439. break;
  440. }
  441. for( transport=service->Transports; transport != NULL; transport=transport->Next ) {
  442. if( transport->ServiceBits & SV_TYPE_MASTER_BROWSER ) {
  443. isMasterBrowser = TRUE;
  444. break;
  445. }
  446. }
  447. }
  448. RtlReleaseResource( &SsData.SsServerInfoResource );
  449. if ( !isMasterBrowser ) {
  450. Sleep( ((rand( ) * (SERVER_REQUEST_ANNOUNCE_DELTA * 1000)) / RAND_MAX) );
  451. }
  452. }
  453. } while ( TRUE );
  454. return NO_ERROR;
  455. } // SsScavengerThread
  456. VOID
  457. SsAnnounce (
  458. IN POEM_STRING OemAnnounceName,
  459. IN LPWSTR EmulatedDomainName OPTIONAL,
  460. IN BOOL DoNtAnnouncement,
  461. IN DWORD NtInterval,
  462. IN BOOL DoLmAnnouncement,
  463. IN BOOL TerminationAnnouncement,
  464. IN BOOL IsPrimaryDomain,
  465. IN LPTSTR Transport,
  466. IN DWORD serviceType
  467. )
  468. /*++
  469. Routine Description:
  470. This routine sends a broadcast datagram as a second-class mailslot
  471. that announces the presence of this server on the network.
  472. Arguments:
  473. OemAnnounceName - The name we're announcing to the network
  474. EmulatedDomainName - The name of the domain this announcement is happening for.
  475. NULL is specified for the primary domain.
  476. DoNtAnnouncement - Do an Nt-style announcement.
  477. NtInterval - NT announcement interval (in seconds)
  478. DoLmAnnouncement - Do an Lm-style announcement.
  479. TerminationAnnouncement - if TRUE, send the announcement that
  480. indicates that this server is going away. Otherwise, send
  481. the normal message that tells clients that we're here.
  482. Transport - Ssupplies the transport to issue the announcement
  483. on.
  484. serviceType - Service bits being announced
  485. Return Value:
  486. None.
  487. --*/
  488. {
  489. DWORD messageSize;
  490. PHOST_ANNOUNCE_PACKET packet;
  491. PBROWSE_ANNOUNCE_PACKET browsePacket;
  492. LPSTR serverName;
  493. DWORD oemServerNameLength; // includes the null terminator
  494. LPSTR serverComment;
  495. DWORD serverCommentLength; // includes the null terminator
  496. OEM_STRING oemCommentString;
  497. UNICODE_STRING unicodeCommentString;
  498. NET_API_STATUS status;
  499. //
  500. // Fill in the necessary information.
  501. //
  502. if( TerminationAnnouncement ) {
  503. serviceType &= ~SV_TYPE_SERVER; // since the server is going away!
  504. }
  505. SS_PRINT(( "SsScavengerThread: Announcing for transport %ws, Bits: %lx\n",
  506. Transport, serviceType ));
  507. //
  508. // Get the length of the oem equivalent of the server name
  509. //
  510. oemServerNameLength = OemAnnounceName->Length + 1;
  511. //
  512. // Convert server comment to a unicode string
  513. //
  514. if ( *SsData.ServerCommentBuffer == '\0' ) {
  515. serverCommentLength = 1;
  516. } else {
  517. unicodeCommentString.Length =
  518. (USHORT)(STRLEN( SsData.ServerCommentBuffer ) * sizeof(WCHAR));
  519. unicodeCommentString.MaximumLength =
  520. (USHORT)(unicodeCommentString.Length + sizeof(WCHAR));
  521. unicodeCommentString.Buffer = SsData.ServerCommentBuffer;
  522. serverCommentLength =
  523. RtlUnicodeStringToOemSize( &unicodeCommentString );
  524. }
  525. oemCommentString.MaximumLength = (USHORT)serverCommentLength;
  526. messageSize = max(sizeof(HOST_ANNOUNCE_PACKET) + oemServerNameLength +
  527. serverCommentLength,
  528. sizeof(BROWSE_ANNOUNCE_PACKET) + serverCommentLength);
  529. //
  530. // Get memory to hold the message. If we can't allocate enough
  531. // memory, don't send an announcement.
  532. //
  533. packet = MIDL_user_allocate( messageSize );
  534. if ( packet == NULL ) {
  535. return;
  536. }
  537. //
  538. // If we are announcing as a Lan Manager server, broadcast the
  539. // announcement.
  540. //
  541. if (SsData.ServerInfo599.sv599_lmannounce && DoLmAnnouncement ) {
  542. packet->AnnounceType = HostAnnouncement ;
  543. SmbPutUlong( &packet->HostAnnouncement.Type, serviceType );
  544. packet->HostAnnouncement.CompatibilityPad = 0;
  545. packet->HostAnnouncement.VersionMajor =
  546. (BYTE)SsData.ServerInfo102.sv102_version_major;
  547. packet->HostAnnouncement.VersionMinor =
  548. (BYTE)SsData.ServerInfo102.sv102_version_minor;
  549. SmbPutUshort(
  550. &packet->HostAnnouncement.Periodicity,
  551. (WORD)SsData.ServerInfo102.sv102_announce
  552. );
  553. //
  554. // Convert the server name from unicode to oem
  555. //
  556. serverName = (LPSTR)( &packet->HostAnnouncement.NameComment );
  557. RtlCopyMemory( serverName, OemAnnounceName->Buffer, OemAnnounceName->Length );
  558. serverName[OemAnnounceName->Length] = '\0';
  559. serverComment = serverName + oemServerNameLength;
  560. if ( serverCommentLength == 1 ) {
  561. *serverComment = '\0';
  562. } else {
  563. oemCommentString.Buffer = serverComment;
  564. (VOID) RtlUnicodeStringToOemString(
  565. &oemCommentString,
  566. &unicodeCommentString,
  567. FALSE
  568. );
  569. }
  570. SendSecondClassMailslot(
  571. Transport,
  572. packet,
  573. FIELD_OFFSET(HOST_ANNOUNCE_PACKET, HostAnnouncement.NameComment) +
  574. oemServerNameLength + serverCommentLength,
  575. EmulatedDomainName,
  576. "\\MAILSLOT\\LANMAN",
  577. 0x00
  578. );
  579. }
  580. //
  581. // Now announce the server as a Winball server.
  582. //
  583. if ( DoNtAnnouncement ) {
  584. browsePacket = (PBROWSE_ANNOUNCE_PACKET)packet;
  585. browsePacket->BrowseType = ( serviceType & SV_TYPE_MASTER_BROWSER ?
  586. LocalMasterAnnouncement :
  587. HostAnnouncement );
  588. browsePacket->BrowseAnnouncement.UpdateCount = 0;
  589. SmbPutUlong( &browsePacket->BrowseAnnouncement.CommentPointer, (ULONG)((0xaa55 << 16) + (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR));
  590. SmbPutUlong( &browsePacket->BrowseAnnouncement.Periodicity, NtInterval * 1000 );
  591. SmbPutUlong( &browsePacket->BrowseAnnouncement.Type, serviceType );
  592. browsePacket->BrowseAnnouncement.VersionMajor =
  593. (BYTE)SsData.ServerInfo102.sv102_version_major;
  594. browsePacket->BrowseAnnouncement.VersionMinor =
  595. (BYTE)SsData.ServerInfo102.sv102_version_minor;
  596. RtlCopyMemory( &browsePacket->BrowseAnnouncement.ServerName,
  597. OemAnnounceName->Buffer,
  598. OemAnnounceName->Length );
  599. browsePacket->BrowseAnnouncement.ServerName[OemAnnounceName->Length] = '\0';
  600. serverComment = (LPSTR)&browsePacket->BrowseAnnouncement.Comment;
  601. if ( serverCommentLength == 1 ) {
  602. *serverComment = '\0';
  603. } else {
  604. oemCommentString.Buffer = serverComment;
  605. (VOID) RtlUnicodeStringToOemString(
  606. &oemCommentString,
  607. &unicodeCommentString,
  608. FALSE
  609. );
  610. }
  611. //
  612. // We need to determine the correct mechanism for sending the mailslot.
  613. //
  614. // 1) Wolfpack (the cluster folks) needs to send the mailslot using
  615. // the SMB server driver. It registers a "fake" transport with
  616. // the SMB server. They only register their cluster name on this
  617. // fake transport. Luckily, they only support the primary domain.
  618. // Wolfpack doesn't currently register its name to the browser
  619. // since the browser doesn't support name registration on a
  620. // subset of the transports.
  621. // 2) Announcements on other than the primary domain need to go via
  622. // the bowser. The bowser not only sends the mailslot to a name
  623. // that's a function of the emulated domain name, but also properly
  624. // crofts up the correct source name.
  625. // 3) Announcement to direct host IPX need to go via the bowser. The
  626. // SMB server driver returns an error if you try to go that way.
  627. //
  628. // If these requirements ever conflict, one of the mechanisms below
  629. // will need to be fixed to support the conflicting needs.
  630. //
  631. if ( IsPrimaryDomain ) {
  632. status = SendSecondClassMailslot(
  633. Transport,
  634. packet,
  635. FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) +
  636. serverCommentLength,
  637. EmulatedDomainName,
  638. "\\MAILSLOT\\BROWSE",
  639. (UCHAR)(serviceType & SV_TYPE_MASTER_BROWSER ?
  640. BROWSER_ELECTION_SIGNATURE :
  641. MASTER_BROWSER_SIGNATURE)
  642. );
  643. } else {
  644. status = ERROR_NOT_SUPPORTED;
  645. }
  646. if ( status != NERR_Success ) {
  647. UCHAR packetBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAX_PATH)*sizeof(WCHAR)];
  648. PLMDR_REQUEST_PACKET requestPacket = (PLMDR_REQUEST_PACKET)packetBuffer;
  649. UNICODE_STRING TransportString;
  650. RtlInitUnicodeString(&TransportString, Transport);
  651. requestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
  652. requestPacket->TransportName = TransportString;
  653. RtlInitUnicodeString( &requestPacket->EmulatedDomainName, EmulatedDomainName );
  654. requestPacket->Type = Datagram;
  655. requestPacket->Parameters.SendDatagram.DestinationNameType = (serviceType & SV_TYPE_MASTER_BROWSER ? BrowserElection : MasterBrowser);
  656. requestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
  657. //
  658. // The domain announcement name is special, so we don't have to specify
  659. // a destination name for it.
  660. //
  661. requestPacket->Parameters.SendDatagram.NameLength = STRLEN(EmulatedDomainName)*sizeof(TCHAR);
  662. STRCPY(requestPacket->Parameters.SendDatagram.Name, EmulatedDomainName);
  663. //
  664. // This is a simple IoControl - It just sends the datagram.
  665. //
  666. status = SsBrowserIoControl(IOCTL_LMDR_WRITE_MAILSLOT,
  667. packet,
  668. FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) +
  669. serverCommentLength,
  670. requestPacket,
  671. FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+
  672. requestPacket->Parameters.SendDatagram.NameLength);
  673. }
  674. }
  675. MIDL_user_free( packet );
  676. } // SsAnnounce
  677. ULONG
  678. ComputeTransportAddressClippedLength(
  679. IN PCHAR TransportAddress,
  680. IN ULONG TransportAddressLength
  681. )
  682. /*++
  683. Routine Description:
  684. This routine returns the length of the transport address with the trailing
  685. blanks removed.
  686. Arguments:
  687. TransportAddress - Transport address with potentially trailing blanks
  688. TransportAddressLength - Length of the Transport Address including trailing
  689. blanks
  690. Return Value:
  691. Length of the Transport Address excluding trailing blanks.
  692. --*/
  693. {
  694. PCHAR p;
  695. //
  696. // Cut off any trailing spaces
  697. //
  698. p = &TransportAddress[ TransportAddressLength ];
  699. for( ; p > TransportAddress && *(p-1) == ' '; p-- )
  700. ;
  701. return (ULONG)(p - TransportAddress);
  702. }
  703. VOID
  704. Announce (
  705. IN BOOL DoNtAnnouncement,
  706. IN DWORD NtInterval,
  707. IN BOOL DoLmAnnouncement,
  708. IN BOOL TerminationAnnouncement
  709. )
  710. /*++
  711. Routine Description:
  712. This routine sends a broadcast datagram as a second-class mailslot
  713. that announces the presence of this server using all
  714. of the configured server names and all networks.
  715. Arguments:
  716. DoNtAnnouncement - Do an Nt-style announcement.
  717. NtInterval - NT announcement interval (in seconds)
  718. DoLmAnnouncement - Do an Lm-style announcement.
  719. TerminationAnnouncement - if TRUE, send the announcement that
  720. indicates that this server is going away. Otherwise, send
  721. the normal message that tells clients that we're here.
  722. Return Value:
  723. None.
  724. --*/
  725. {
  726. PNAME_LIST_ENTRY Service;
  727. PTRANSPORT_LIST_ENTRY Transport;
  728. NTSTATUS status;
  729. OEM_STRING OemAnnounceName;
  730. (VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
  731. //
  732. // Loop through each emulated server name announcing on each.
  733. //
  734. for( Service = SsData.SsServerNameList; Service != NULL; Service = Service->Next ) {
  735. //
  736. // Save the AnnounceName without trailing blanks.
  737. //
  738. OemAnnounceName.Length = (USHORT) ComputeTransportAddressClippedLength(
  739. Service->TransportAddress,
  740. Service->TransportAddressLength );
  741. OemAnnounceName.MaximumLength = OemAnnounceName.Length;
  742. OemAnnounceName.Buffer = Service->TransportAddress;
  743. if( OemAnnounceName.Length == 0 ) {
  744. //
  745. // A blank name???
  746. //
  747. continue;
  748. }
  749. //
  750. // Loop through each transport announcing on each.
  751. //
  752. for( Transport = Service->Transports; Transport != NULL; Transport = Transport->Next ) {
  753. //
  754. // Do the actual announcement
  755. // NTBUG 125806 : We pass TRUE for isPrimaryDomain but for DCs w/ multiple hosted
  756. // domains (future plan-- NT6) we will have to figure out how to set this
  757. // flag appropriately. For Win2K (NT5) this is not an issue. See bug 286735
  758. // addressing a problem to the cluster service).
  759. //
  760. SsAnnounce( &OemAnnounceName,
  761. Service->DomainName,
  762. DoNtAnnouncement,
  763. NtInterval,
  764. DoLmAnnouncement,
  765. TerminationAnnouncement,
  766. TRUE,
  767. Transport->TransportName,
  768. Service->ServiceBits | Transport->ServiceBits );
  769. }
  770. }
  771. RtlReleaseResource( &SsData.SsServerInfoResource );
  772. }
  773. NET_API_STATUS
  774. SendSecondClassMailslot (
  775. IN LPTSTR Transport OPTIONAL,
  776. IN PVOID Message,
  777. IN DWORD MessageLength,
  778. IN LPTSTR Domain,
  779. IN LPSTR MailslotNameText,
  780. IN UCHAR SignatureByte
  781. )
  782. {
  783. NET_API_STATUS status;
  784. DWORD dataSize;
  785. DWORD smbSize;
  786. PSMB_HEADER header;
  787. PSMB_TRANSACT_MAILSLOT parameters;
  788. LPSTR mailslotName;
  789. DWORD mailslotNameLength;
  790. PVOID message;
  791. DWORD domainLength;
  792. CHAR domainName[NETBIOS_NAME_LEN];
  793. PCHAR domainNamePointer;
  794. PSERVER_REQUEST_PACKET srp;
  795. UNICODE_STRING domainString;
  796. OEM_STRING oemDomainString;
  797. srp = SsAllocateSrp();
  798. if ( srp == NULL ) {
  799. return ERROR_NOT_ENOUGH_MEMORY;
  800. }
  801. RtlInitUnicodeString(&domainString, Domain);
  802. oemDomainString.Buffer = domainName;
  803. oemDomainString.MaximumLength = sizeof(domainName);
  804. status = RtlUpcaseUnicodeStringToOemString(
  805. &oemDomainString,
  806. &domainString,
  807. FALSE
  808. );
  809. if (!NT_SUCCESS(status)) {
  810. return RtlNtStatusToDosError(status);
  811. }
  812. domainLength = oemDomainString.Length;
  813. domainNamePointer = &domainName[domainLength];
  814. for ( ; domainLength < NETBIOS_NAME_LEN - 1 ; domainLength++ ) {
  815. *domainNamePointer++ = ' ';
  816. }
  817. //
  818. // Append the signature byte to the end of the name.
  819. //
  820. *domainNamePointer = SignatureByte;
  821. domainLength += 1;
  822. srp->Name1.Buffer = (PWSTR)domainName;
  823. srp->Name1.Length = (USHORT)domainLength;
  824. srp->Name1.MaximumLength = (USHORT)domainLength;
  825. if ( ARGUMENT_PRESENT ( Transport ) ) {
  826. RtlInitUnicodeString( &srp->Name2, Transport );
  827. } else {
  828. srp->Name2.Buffer = NULL;
  829. srp->Name2.Length = 0;
  830. srp->Name2.MaximumLength = 0;
  831. }
  832. //
  833. // Determine the sizes of various fields that will go in the SMB
  834. // and the total size of the SMB.
  835. //
  836. mailslotNameLength = strlen( MailslotNameText );
  837. dataSize = mailslotNameLength + 1 + MessageLength;
  838. smbSize = sizeof(SMB_HEADER) + sizeof(SMB_TRANSACT_MAILSLOT) - 1 + dataSize;
  839. //
  840. // Allocate enough memory to hold the SMB. If we can't allocate the
  841. // memory, don't do an announcement.
  842. //
  843. header = MIDL_user_allocate( smbSize );
  844. if ( header == NULL ) {
  845. SsFreeSrp( srp );
  846. return ERROR_NOT_ENOUGH_MEMORY;
  847. }
  848. //
  849. // Fill in the header. Most of the fields don't matter and are
  850. // zeroed.
  851. //
  852. RtlZeroMemory( header, smbSize );
  853. header->Protocol[0] = 0xFF;
  854. header->Protocol[1] = 'S';
  855. header->Protocol[2] = 'M';
  856. header->Protocol[3] = 'B';
  857. header->Command = SMB_COM_TRANSACTION;
  858. //
  859. // Get the pointer to the params and fill them in.
  860. //
  861. parameters = (PSMB_TRANSACT_MAILSLOT)( header + 1 );
  862. mailslotName = (LPSTR)( parameters + 1 ) - 1;
  863. message = mailslotName + mailslotNameLength + 1;
  864. parameters->WordCount = 0x11;
  865. SmbPutUshort( &parameters->TotalDataCount, (WORD)MessageLength );
  866. SmbPutUlong( &parameters->Timeout, 0x3E8 ); // !!! fix
  867. SmbPutUshort( &parameters->DataCount, (WORD)MessageLength );
  868. SmbPutUshort(
  869. &parameters->DataOffset,
  870. (WORD)( (DWORD_PTR)message - (DWORD_PTR)header )
  871. );
  872. parameters->SetupWordCount = 3;
  873. SmbPutUshort( &parameters->Opcode, MS_WRITE_OPCODE );
  874. SmbPutUshort( &parameters->Class, 2 );
  875. SmbPutUshort( &parameters->ByteCount, (WORD)dataSize );
  876. RtlCopyMemory( mailslotName, MailslotNameText, mailslotNameLength + 1 );
  877. RtlCopyMemory( message, Message, MessageLength );
  878. status = SsServerFsControl(
  879. FSCTL_SRV_SEND_DATAGRAM,
  880. srp,
  881. header,
  882. smbSize
  883. );
  884. if ( status != NERR_Success ) {
  885. SS_PRINT(( "SendSecondClassMailslot: NtFsControlFile failed: %X\n",
  886. status ));
  887. }
  888. MIDL_user_free( header );
  889. SsFreeSrp( srp );
  890. return status;
  891. } // SendSecondClassMailslot
  892. NTSTATUS
  893. OpenBrowser(
  894. OUT PHANDLE BrowserHandle
  895. )
  896. /*++
  897. Routine Description:
  898. This function opens a handle to the bowser device driver.
  899. Arguments:
  900. OUT PHANDLE BrowserHandle - Returns the handle to the browser.
  901. Return Value:
  902. NET_API_STATUS - NERR_Success or reason for failure.
  903. --*/
  904. {
  905. NTSTATUS ntstatus;
  906. UNICODE_STRING deviceName;
  907. IO_STATUS_BLOCK ioStatusBlock;
  908. OBJECT_ATTRIBUTES objectAttributes;
  909. //
  910. // Open the redirector device.
  911. //
  912. RtlInitUnicodeString(&deviceName, DD_BROWSER_DEVICE_NAME_U);
  913. InitializeObjectAttributes(
  914. &objectAttributes,
  915. &deviceName,
  916. OBJ_CASE_INSENSITIVE,
  917. NULL,
  918. NULL
  919. );
  920. ntstatus = NtOpenFile(
  921. BrowserHandle,
  922. SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
  923. &objectAttributes,
  924. &ioStatusBlock,
  925. FILE_SHARE_READ | FILE_SHARE_WRITE,
  926. FILE_SYNCHRONOUS_IO_NONALERT
  927. );
  928. if (NT_SUCCESS(ntstatus)) {
  929. ntstatus = ioStatusBlock.Status;
  930. }
  931. return ntstatus;
  932. }
  933. NET_API_STATUS
  934. SsBrowserIoControl (
  935. IN DWORD IoControlCode,
  936. IN PVOID Buffer,
  937. IN DWORD BufferLength,
  938. IN PLMDR_REQUEST_PACKET Packet,
  939. IN DWORD PacketLength
  940. )
  941. {
  942. HANDLE browserHandle;
  943. NTSTATUS status;
  944. PLMDR_REQUEST_PACKET realPacket;
  945. DWORD RealPacketSize;
  946. DWORD bytesReturned;
  947. LPBYTE Where;
  948. //
  949. // Open the browser device driver.
  950. //
  951. if ( !NT_SUCCESS(status = OpenBrowser(&browserHandle)) ) {
  952. return RtlNtStatusToDosError(status);
  953. }
  954. //
  955. // Now copy the request packet to a new buffer to allow us to pack the
  956. // transport name to the end of the buffer we pass to the driver.
  957. //
  958. RealPacketSize = PacketLength+Packet->TransportName.MaximumLength;
  959. RealPacketSize += Packet->EmulatedDomainName.MaximumLength;
  960. realPacket = MIDL_user_allocate( RealPacketSize );
  961. if (realPacket == NULL) {
  962. return ERROR_NOT_ENOUGH_MEMORY;
  963. }
  964. RtlCopyMemory(realPacket, Packet, PacketLength);
  965. Where = ((LPBYTE)realPacket)+PacketLength;
  966. if (Packet->TransportName.Length != 0) {
  967. realPacket->TransportName.Buffer = (LPWSTR) Where;
  968. realPacket->TransportName.MaximumLength = Packet->TransportName.MaximumLength;
  969. RtlCopyUnicodeString(&realPacket->TransportName, &Packet->TransportName);
  970. Where += Packet->TransportName.MaximumLength;
  971. }
  972. if (Packet->EmulatedDomainName.Length != 0) {
  973. realPacket->EmulatedDomainName.Buffer = (LPWSTR) Where;
  974. realPacket->EmulatedDomainName.MaximumLength = Packet->EmulatedDomainName.MaximumLength;
  975. RtlCopyUnicodeString(&realPacket->EmulatedDomainName, &Packet->EmulatedDomainName);
  976. Where += Packet->EmulatedDomainName.MaximumLength;
  977. }
  978. //
  979. // Send the request to the Datagram Receiver DD.
  980. //
  981. if (!DeviceIoControl(
  982. browserHandle,
  983. IoControlCode,
  984. realPacket,
  985. RealPacketSize,
  986. Buffer,
  987. BufferLength,
  988. &bytesReturned,
  989. NULL
  990. )) {
  991. status = GetLastError();
  992. }
  993. MIDL_user_free(realPacket);
  994. CloseHandle(browserHandle);
  995. return status;
  996. }