|
|
/*++
Copyright (c) 1991-1992 Microsoft Corporation
Module Name:
Scavengr.c
Abstract:
This module contains the code for the server service scavenger thread. This thread handles announcements and configuration changes. (Although originally written to run in a separate thread, this code now runs in the initial thread of the server service.
Author:
David Treadwell (davidtr) 17-Apr-1991
Revision History:
--*/
#include "srvsvcp.h"
#include "ssreg.h"
#include <netlibnt.h>
#include <tstr.h>
#define INCLUDE_SMB_TRANSACTION
#undef NT_PIPE_PREFIX
#include <smbtypes.h>
#include <smb.h>
#include <smbtrans.h>
#include <smbgtpt.h>
#include <hostannc.h>
#include <ntddbrow.h>
#include <lmerr.h>
#define TERMINATION_SIGNALED 0
#define ANNOUNCE_SIGNALED 1
#define STATUS_CHANGED 2
#define DOMAIN_CHANGED 3
#define REGISTRY_CHANGED 4 // Must be the last one
#define NUMBER_OF_EVENTS 5
//
// Bias request announcements by SERVER_REQUEST_ANNOUNCE_DELTA SECONDS
//
#define SERVER_REQUEST_ANNOUNCE_DELTA 30
//
// Forward declarations.
//
VOID Announce ( IN BOOL DoNtAnnouncement, IN DWORD NtInterval, IN BOOL DoLmAnnouncement, IN BOOL TerminationAnnouncement );
NET_API_STATUS SendSecondClassMailslot ( IN LPTSTR Transport OPTIONAL, IN PVOID Message, IN DWORD MessageLength, IN LPTSTR Domain, IN LPSTR MailslotNameText, IN UCHAR SignatureByte );
NET_API_STATUS SsBrowserIoControl ( IN DWORD IoControlCode, IN PVOID Buffer, IN DWORD BufferLength, IN PLMDR_REQUEST_PACKET Packet, IN DWORD PacketLength );
DWORD ComputeAnnounceTime ( IN DWORD LastAnnouncementTime, IN DWORD Interval )
/*++
Routine Description:
Compute the time to wait (in milliseconds) until the next announcement should be made.
Arguments:
LastAnnouncementTime - Time (in milliseconds since reboot) when the last announcement was made.
Interval - Interval (in seconds) between announcements
Return Value:
Timeout period (in milliseconds)
--*/
{ DWORD AnnounceDelta; DWORD Timeout; DWORD CurrentTime;
//
// Get the current time.
//
CurrentTime = GetTickCount();
//
// If the clock has gone backward,
// send an announcement now.
//
if ( LastAnnouncementTime > CurrentTime ) { return 0; }
//
// Convert the announcement period from seconds to milliseconds.
//
Timeout = Interval * 1000;
//
// Add in the random announce delta which helps prevent lots of
// servers from announcing at the same time.
//
AnnounceDelta = SsData.ServerInfo102.sv102_anndelta;
Timeout += ((rand( ) * AnnounceDelta * 2) / RAND_MAX) - AnnounceDelta;
//
// If our time has expired,
// send an announcement now.
//
if ( (CurrentTime - LastAnnouncementTime) >= Timeout ) { return 0; }
//
// Adjust our timeout period for time already elapsed.
//
return Timeout - (CurrentTime - LastAnnouncementTime);
}
DWORD SsScavengerThread ( IN LPVOID lpThreadParameter )
/*++
Routine Description:
This routine implements the server service scavenger thread.
Arguments:
lpThreadParameter - ignored.
Return Value:
NET_API_STATUS - thread termination result.
--*/
{ HANDLE events[ NUMBER_OF_EVENTS ]; ULONG numEvents = NUMBER_OF_EVENTS-1; UNICODE_STRING unicodeEventName; OBJECT_ATTRIBUTES obja; DWORD waitStatus; DWORD timeout;
DWORD LmTimeout; BOOL DoLmAnnouncement; DWORD LmLastAnnouncementTime;
DWORD NtTimeout; BOOL DoNtAnnouncement; DWORD NtLastAnnouncementTime; DWORD NtInterval;
NTSTATUS status; BOOL hidden = TRUE; HKEY hParameters = INVALID_HANDLE_VALUE;
lpThreadParameter;
//
// Use the scavenger termination event to know when we're supposed
// to wake up and kill ourselves.
//
events[TERMINATION_SIGNALED] = SsData.SsTerminationEvent;
//
// Initialize the NT announcement interval to the LM announcement interval
//
NtInterval = SsData.ServerInfo102.sv102_announce; DoLmAnnouncement = TRUE; DoNtAnnouncement = TRUE;
//
// Create the announce event. When this gets signaled, we wake up
// and do an announcement. We use a synchronization event rather
// than a notification event so that we don't have to worry about
// resetting the event after we wake up.
//
//
// Please note that we create this event with OBJ_OPENIF. We do this
// to allow the browser to signal the server to force an announcement.
//
// The bowser will create this event as a part of the bowser
// initialization, and will set it to the signalled state when it needs
// to have the server announce.
//
RtlInitUnicodeString( &unicodeEventName, SERVER_ANNOUNCE_EVENT_W ); InitializeObjectAttributes( &obja, &unicodeEventName, OBJ_OPENIF, NULL, NULL );
status = NtCreateEvent( &SsData.SsAnnouncementEvent, SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE, &obja, SynchronizationEvent, FALSE );
if ( !NT_SUCCESS(status) ) { SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n", status )); return NetpNtStatusToApiStatus( status ); }
events[ANNOUNCE_SIGNALED] = SsData.SsAnnouncementEvent;
//
// Create an unnamed event to be set to the signalled state when the
// service status changes (or a local application requests an
// announcement)
//
InitializeObjectAttributes( &obja, NULL, OBJ_OPENIF, NULL, NULL );
status = NtCreateEvent( &SsData.SsStatusChangedEvent, SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE, &obja, SynchronizationEvent, FALSE );
if ( !NT_SUCCESS(status) ) { SS_PRINT(( "SsScavengerThread: NtCreateEvent failed: %X\n", status ));
NtClose( SsData.SsAnnouncementEvent ); SsData.SsAnnouncementEvent = NULL;
return NetpNtStatusToApiStatus( status ); }
events[STATUS_CHANGED] = SsData.SsStatusChangedEvent;
events[ DOMAIN_CHANGED ] = SsData.SsDomainNameChangeEvent ? SsData.SsDomainNameChangeEvent : INVALID_HANDLE_VALUE;
//
// Put a watch on the registry for any changes that happen in the
// null session share or pipe list. Don't bail out if this fails,
// because we've done this as a convenience in adding new null
// session-reliant servers. It doesn't really affect the normal
// operation of the server if this doesn't work.
//
events[ REGISTRY_CHANGED ] = INVALID_HANDLE_VALUE; status = NtCreateEvent( &events[ REGISTRY_CHANGED ], SYNCHRONIZE | EVENT_QUERY_STATE | EVENT_MODIFY_STATE, NULL, SynchronizationEvent, FALSE );
if ( NT_SUCCESS(status) ) { status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, FULL_PARAMETERS_REGISTRY_PATH, 0, KEY_NOTIFY, &hParameters );
if( status == ERROR_SUCCESS ) { (void)RegNotifyChangeKeyValue( hParameters, TRUE, REG_NOTIFY_CHANGE_LAST_SET, events[ REGISTRY_CHANGED ], TRUE ); //
// Add this event to the list of events we're waiting for
//
++numEvents; } }
//
// Seed the random number generator. We use it to generate random
// announce deltas.
//
srand( PtrToUlong(SsData.SsAnnouncementEvent) );
//
// Do an announcement immediately for startup, then loop announcing
// based on the announce interval.
//
waitStatus = WAIT_TIMEOUT;
do {
//
// Act according to whether the termination event, the announce
// event, or the timeout caused us to wake up.
//
// !!! Or the configuration event indicating a configuration
// change notification.
if ( waitStatus == WAIT_FAILED ) {
//
// Don't consume all the CPU just because an error happened
//
Sleep(1000);
} else if ( waitStatus == WAIT_OBJECT_0 + TERMINATION_SIGNALED ) {
SS_PRINT(( "Scavenger: termination event signaled\n" ));
//
// The scavenger termination event was signaled, so we have
// to gracefully kill this thread. If this is not a hidden
// server, announce the fact that we're going down.
//
if ( !hidden ) { Announce( TRUE, NtInterval, TRUE, TRUE ); }
//
// Close the announcement event.
//
NtClose( SsData.SsAnnouncementEvent );
SsData.SsAnnouncementEvent = NULL;
//
// Close the Registry watch event.
//
if( events[ REGISTRY_CHANGED ] != INVALID_HANDLE_VALUE ) NtClose( events[ REGISTRY_CHANGED ] );
//
// Close the Registry handle
//
if( hParameters != INVALID_HANDLE_VALUE ) RegCloseKey( hParameters );
//
// Return to caller.
//
return NO_ERROR;
} else if( waitStatus == WAIT_OBJECT_0 + REGISTRY_CHANGED ) { //
// Somebody changed some server parameters. Tell the driver
//
SS_PRINT(( "SsScavengerThread: Server parameters changed\n" ));
//
// Tell the server FSD to look for a change in the registry
//
(void)SsServerFsControl( FSCTL_SRV_REGISTRY_CHANGE, NULL, NULL, 0 );
//
// Turn it back on so we get future changes, too
//
(void)RegNotifyChangeKeyValue( hParameters, TRUE, REG_NOTIFY_CHANGE_LAST_SET, events[ REGISTRY_CHANGED ], TRUE );
} else if( waitStatus == WAIT_OBJECT_0 + DOMAIN_CHANGED ) {
SsSetDomainName();
} else {
SS_ASSERT( waitStatus == WAIT_TIMEOUT || waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED || waitStatus == WAIT_OBJECT_0 + STATUS_CHANGED );
//
// If we've timed out,
// we've already set the flags indicating whether to announce
// on to lanman to NT browsers
//
if ( waitStatus != WAIT_TIMEOUT ) { DoLmAnnouncement = TRUE; DoNtAnnouncement = TRUE; }
//
// If we're not a hidden server, announce ourselves.
//
// Hold the database resource while we do the announcement so
// that we get a consistent view of the database.
//
(VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
if ( !SsData.ServerInfo102.sv102_hidden ) {
hidden = FALSE;
Announce( DoNtAnnouncement, NtInterval, DoLmAnnouncement, FALSE );
//
// If we were not hidden last time through the loop but
// we're hidden now, we've changed to hidden, so announce
// that we're going down. This causes clients in the domain
// to take us out of their server enumerations.
//
} else if ( !hidden ) {
hidden = TRUE; Announce( TRUE, NtInterval, TRUE, TRUE );
}
//
// If the server is hidden, the wait timeout is infinite. We'll
// be woken up by the announce event if the server becomes
// unhidden.
//
if ( SsData.ServerInfo102.sv102_hidden ) {
timeout = 0xffffffff;
} else {
//
// Remember when the last announcement was.
//
if ( DoNtAnnouncement ) { NtLastAnnouncementTime = GetTickCount(); }
if ( DoLmAnnouncement ) { LmLastAnnouncementTime = GetTickCount(); }
//
// Compute the time delta to the next announcement
//
// For NtInterval,
// use a local copy of the interval since we compute the correct
// value.
//
// For the Lanman interval,
// use the global copy to allow the interval to be changed.
//
NtTimeout = ComputeAnnounceTime( NtLastAnnouncementTime, NtInterval );
if (SsData.ServerInfo599.sv599_lmannounce) { LmTimeout = ComputeAnnounceTime( LmLastAnnouncementTime, SsData.ServerInfo102.sv102_announce ); } else { // Don't awaken this thread to do nothing.
LmTimeout = 0xffffffff; }
//
// If our NT announcement frequency is less than 12 minutes,
// increase our announcement frequency by 4 minutes.
//
if ( NtInterval < 12 * 60 ) {
NtInterval += 4 * 60;
if ( NtInterval > 12 * 60) { NtInterval = 12 * 60; } }
//
// Determine which timeout we're actually going to use.
//
if ( NtTimeout == LmTimeout ) { timeout = NtTimeout; DoLmAnnouncement = TRUE; DoNtAnnouncement = TRUE; } else if ( NtTimeout < LmTimeout ) { timeout = NtTimeout; DoLmAnnouncement = FALSE; DoNtAnnouncement = TRUE; } else { timeout = LmTimeout; DoLmAnnouncement = TRUE; DoNtAnnouncement = FALSE; }
}
RtlReleaseResource( &SsData.SsServerInfoResource ); }
//
// Wait for one of the events to be signaled or for the timeout
// to elapse.
//
waitStatus = WaitForMultipleObjects( numEvents , events, FALSE, timeout );
if ( waitStatus == WAIT_OBJECT_0 + ANNOUNCE_SIGNALED ) {
//
// We were awakened because an announce was signalled.
// Unless we are a master browser on at least one transport,
// delay for a random delta to stagger announcements a bit
// to prevent lots of servers from announcing
// simultaneously.
//
BOOL isMasterBrowser = FALSE; PNAME_LIST_ENTRY service; PTRANSPORT_LIST_ENTRY transport;
RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
for( service = SsData.SsServerNameList; isMasterBrowser == FALSE && service != NULL; service = service->Next ) {
if( service->ServiceBits & SV_TYPE_MASTER_BROWSER ) { isMasterBrowser = TRUE; break; }
for( transport=service->Transports; transport != NULL; transport=transport->Next ) { if( transport->ServiceBits & SV_TYPE_MASTER_BROWSER ) { isMasterBrowser = TRUE; break; } } }
RtlReleaseResource( &SsData.SsServerInfoResource );
if ( !isMasterBrowser ) { Sleep( ((rand( ) * (SERVER_REQUEST_ANNOUNCE_DELTA * 1000)) / RAND_MAX) ); }
}
} while ( TRUE );
return NO_ERROR;
} // SsScavengerThread
VOID SsAnnounce ( IN POEM_STRING OemAnnounceName, IN LPWSTR EmulatedDomainName OPTIONAL, IN BOOL DoNtAnnouncement, IN DWORD NtInterval, IN BOOL DoLmAnnouncement, IN BOOL TerminationAnnouncement, IN BOOL IsPrimaryDomain, IN LPTSTR Transport, IN DWORD serviceType )
/*++
Routine Description:
This routine sends a broadcast datagram as a second-class mailslot that announces the presence of this server on the network.
Arguments:
OemAnnounceName - The name we're announcing to the network
EmulatedDomainName - The name of the domain this announcement is happening for. NULL is specified for the primary domain.
DoNtAnnouncement - Do an Nt-style announcement.
NtInterval - NT announcement interval (in seconds)
DoLmAnnouncement - Do an Lm-style announcement.
TerminationAnnouncement - if TRUE, send the announcement that indicates that this server is going away. Otherwise, send the normal message that tells clients that we're here.
Transport - Ssupplies the transport to issue the announcement on.
serviceType - Service bits being announced
Return Value:
None.
--*/
{ DWORD messageSize; PHOST_ANNOUNCE_PACKET packet; PBROWSE_ANNOUNCE_PACKET browsePacket;
LPSTR serverName; DWORD oemServerNameLength; // includes the null terminator
LPSTR serverComment; DWORD serverCommentLength; // includes the null terminator
OEM_STRING oemCommentString;
UNICODE_STRING unicodeCommentString;
NET_API_STATUS status;
//
// Fill in the necessary information.
//
if( TerminationAnnouncement ) { serviceType &= ~SV_TYPE_SERVER; // since the server is going away!
}
SS_PRINT(( "SsScavengerThread: Announcing for transport %ws, Bits: %lx\n", Transport, serviceType ));
//
// Get the length of the oem equivalent of the server name
//
oemServerNameLength = OemAnnounceName->Length + 1;
//
// Convert server comment to a unicode string
//
if ( *SsData.ServerCommentBuffer == '\0' ) { serverCommentLength = 1; } else { unicodeCommentString.Length = (USHORT)(STRLEN( SsData.ServerCommentBuffer ) * sizeof(WCHAR)); unicodeCommentString.MaximumLength = (USHORT)(unicodeCommentString.Length + sizeof(WCHAR)); unicodeCommentString.Buffer = SsData.ServerCommentBuffer; serverCommentLength = RtlUnicodeStringToOemSize( &unicodeCommentString ); }
oemCommentString.MaximumLength = (USHORT)serverCommentLength;
messageSize = max(sizeof(HOST_ANNOUNCE_PACKET) + oemServerNameLength + serverCommentLength, sizeof(BROWSE_ANNOUNCE_PACKET) + serverCommentLength);
//
// Get memory to hold the message. If we can't allocate enough
// memory, don't send an announcement.
//
packet = MIDL_user_allocate( messageSize ); if ( packet == NULL ) { return; }
//
// If we are announcing as a Lan Manager server, broadcast the
// announcement.
//
if (SsData.ServerInfo599.sv599_lmannounce && DoLmAnnouncement ) {
packet->AnnounceType = HostAnnouncement ;
SmbPutUlong( &packet->HostAnnouncement.Type, serviceType );
packet->HostAnnouncement.CompatibilityPad = 0;
packet->HostAnnouncement.VersionMajor = (BYTE)SsData.ServerInfo102.sv102_version_major; packet->HostAnnouncement.VersionMinor = (BYTE)SsData.ServerInfo102.sv102_version_minor;
SmbPutUshort( &packet->HostAnnouncement.Periodicity, (WORD)SsData.ServerInfo102.sv102_announce );
//
// Convert the server name from unicode to oem
//
serverName = (LPSTR)( &packet->HostAnnouncement.NameComment );
RtlCopyMemory( serverName, OemAnnounceName->Buffer, OemAnnounceName->Length ); serverName[OemAnnounceName->Length] = '\0';
serverComment = serverName + oemServerNameLength;
if ( serverCommentLength == 1 ) { *serverComment = '\0'; } else {
oemCommentString.Buffer = serverComment; (VOID) RtlUnicodeStringToOemString( &oemCommentString, &unicodeCommentString, FALSE ); }
SendSecondClassMailslot( Transport, packet, FIELD_OFFSET(HOST_ANNOUNCE_PACKET, HostAnnouncement.NameComment) + oemServerNameLength + serverCommentLength, EmulatedDomainName, "\\MAILSLOT\\LANMAN", 0x00 ); }
//
// Now announce the server as a Winball server.
//
if ( DoNtAnnouncement ) { browsePacket = (PBROWSE_ANNOUNCE_PACKET)packet;
browsePacket->BrowseType = ( serviceType & SV_TYPE_MASTER_BROWSER ? LocalMasterAnnouncement : HostAnnouncement );
browsePacket->BrowseAnnouncement.UpdateCount = 0;
SmbPutUlong( &browsePacket->BrowseAnnouncement.CommentPointer, (ULONG)((0xaa55 << 16) + (BROWSER_VERSION_MAJOR << 8) + BROWSER_VERSION_MINOR));
SmbPutUlong( &browsePacket->BrowseAnnouncement.Periodicity, NtInterval * 1000 );
SmbPutUlong( &browsePacket->BrowseAnnouncement.Type, serviceType );
browsePacket->BrowseAnnouncement.VersionMajor = (BYTE)SsData.ServerInfo102.sv102_version_major; browsePacket->BrowseAnnouncement.VersionMinor = (BYTE)SsData.ServerInfo102.sv102_version_minor;
RtlCopyMemory( &browsePacket->BrowseAnnouncement.ServerName, OemAnnounceName->Buffer, OemAnnounceName->Length ); browsePacket->BrowseAnnouncement.ServerName[OemAnnounceName->Length] = '\0';
serverComment = (LPSTR)&browsePacket->BrowseAnnouncement.Comment;
if ( serverCommentLength == 1 ) { *serverComment = '\0'; } else {
oemCommentString.Buffer = serverComment; (VOID) RtlUnicodeStringToOemString( &oemCommentString, &unicodeCommentString, FALSE ); }
//
// We need to determine the correct mechanism for sending the mailslot.
//
// 1) Wolfpack (the cluster folks) needs to send the mailslot using
// the SMB server driver. It registers a "fake" transport with
// the SMB server. They only register their cluster name on this
// fake transport. Luckily, they only support the primary domain.
// Wolfpack doesn't currently register its name to the browser
// since the browser doesn't support name registration on a
// subset of the transports.
// 2) Announcements on other than the primary domain need to go via
// the bowser. The bowser not only sends the mailslot to a name
// that's a function of the emulated domain name, but also properly
// crofts up the correct source name.
// 3) Announcement to direct host IPX need to go via the bowser. The
// SMB server driver returns an error if you try to go that way.
//
// If these requirements ever conflict, one of the mechanisms below
// will need to be fixed to support the conflicting needs.
//
if ( IsPrimaryDomain ) { status = SendSecondClassMailslot( Transport, packet, FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) + serverCommentLength, EmulatedDomainName, "\\MAILSLOT\\BROWSE", (UCHAR)(serviceType & SV_TYPE_MASTER_BROWSER ? BROWSER_ELECTION_SIGNATURE : MASTER_BROWSER_SIGNATURE) ); } else { status = ERROR_NOT_SUPPORTED; }
if ( status != NERR_Success ) { UCHAR packetBuffer[sizeof(LMDR_REQUEST_PACKET)+(MAX_PATH)*sizeof(WCHAR)]; PLMDR_REQUEST_PACKET requestPacket = (PLMDR_REQUEST_PACKET)packetBuffer; UNICODE_STRING TransportString;
RtlInitUnicodeString(&TransportString, Transport);
requestPacket->Version = LMDR_REQUEST_PACKET_VERSION;
requestPacket->TransportName = TransportString; RtlInitUnicodeString( &requestPacket->EmulatedDomainName, EmulatedDomainName );
requestPacket->Type = Datagram;
requestPacket->Parameters.SendDatagram.DestinationNameType = (serviceType & SV_TYPE_MASTER_BROWSER ? BrowserElection : MasterBrowser);
requestPacket->Parameters.SendDatagram.MailslotNameLength = 0;
//
// The domain announcement name is special, so we don't have to specify
// a destination name for it.
//
requestPacket->Parameters.SendDatagram.NameLength = STRLEN(EmulatedDomainName)*sizeof(TCHAR);
STRCPY(requestPacket->Parameters.SendDatagram.Name, EmulatedDomainName);
//
// This is a simple IoControl - It just sends the datagram.
//
status = SsBrowserIoControl(IOCTL_LMDR_WRITE_MAILSLOT, packet, FIELD_OFFSET(BROWSE_ANNOUNCE_PACKET, BrowseAnnouncement.Comment) + serverCommentLength, requestPacket, FIELD_OFFSET(LMDR_REQUEST_PACKET, Parameters.SendDatagram.Name)+ requestPacket->Parameters.SendDatagram.NameLength); } }
MIDL_user_free( packet );
} // SsAnnounce
ULONG ComputeTransportAddressClippedLength( IN PCHAR TransportAddress, IN ULONG TransportAddressLength )
/*++
Routine Description:
This routine returns the length of the transport address with the trailing blanks removed.
Arguments:
TransportAddress - Transport address with potentially trailing blanks
TransportAddressLength - Length of the Transport Address including trailing blanks
Return Value:
Length of the Transport Address excluding trailing blanks.
--*/
{ PCHAR p;
//
// Cut off any trailing spaces
//
p = &TransportAddress[ TransportAddressLength ]; for( ; p > TransportAddress && *(p-1) == ' '; p-- ) ;
return (ULONG)(p - TransportAddress); }
VOID Announce ( IN BOOL DoNtAnnouncement, IN DWORD NtInterval, IN BOOL DoLmAnnouncement, IN BOOL TerminationAnnouncement )
/*++
Routine Description:
This routine sends a broadcast datagram as a second-class mailslot that announces the presence of this server using all of the configured server names and all networks.
Arguments:
DoNtAnnouncement - Do an Nt-style announcement.
NtInterval - NT announcement interval (in seconds)
DoLmAnnouncement - Do an Lm-style announcement.
TerminationAnnouncement - if TRUE, send the announcement that indicates that this server is going away. Otherwise, send the normal message that tells clients that we're here.
Return Value:
None.
--*/
{ PNAME_LIST_ENTRY Service; PTRANSPORT_LIST_ENTRY Transport; NTSTATUS status; OEM_STRING OemAnnounceName;
(VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
//
// Loop through each emulated server name announcing on each.
//
for( Service = SsData.SsServerNameList; Service != NULL; Service = Service->Next ) {
//
// Save the AnnounceName without trailing blanks.
//
OemAnnounceName.Length = (USHORT) ComputeTransportAddressClippedLength( Service->TransportAddress, Service->TransportAddressLength );
OemAnnounceName.MaximumLength = OemAnnounceName.Length; OemAnnounceName.Buffer = Service->TransportAddress;
if( OemAnnounceName.Length == 0 ) { //
// A blank name???
//
continue; }
//
// Loop through each transport announcing on each.
//
for( Transport = Service->Transports; Transport != NULL; Transport = Transport->Next ) {
//
// Do the actual announcement
// NTBUG 125806 : We pass TRUE for isPrimaryDomain but for DCs w/ multiple hosted
// domains (future plan-- NT6) we will have to figure out how to set this
// flag appropriately. For Win2K (NT5) this is not an issue. See bug 286735
// addressing a problem to the cluster service).
//
SsAnnounce( &OemAnnounceName, Service->DomainName, DoNtAnnouncement, NtInterval, DoLmAnnouncement, TerminationAnnouncement, TRUE, Transport->TransportName, Service->ServiceBits | Transport->ServiceBits );
} }
RtlReleaseResource( &SsData.SsServerInfoResource ); }
NET_API_STATUS SendSecondClassMailslot ( IN LPTSTR Transport OPTIONAL, IN PVOID Message, IN DWORD MessageLength, IN LPTSTR Domain, IN LPSTR MailslotNameText, IN UCHAR SignatureByte ) { NET_API_STATUS status; DWORD dataSize; DWORD smbSize; PSMB_HEADER header; PSMB_TRANSACT_MAILSLOT parameters; LPSTR mailslotName; DWORD mailslotNameLength; PVOID message; DWORD domainLength; CHAR domainName[NETBIOS_NAME_LEN]; PCHAR domainNamePointer; PSERVER_REQUEST_PACKET srp;
UNICODE_STRING domainString; OEM_STRING oemDomainString;
srp = SsAllocateSrp();
if ( srp == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; }
RtlInitUnicodeString(&domainString, Domain);
oemDomainString.Buffer = domainName; oemDomainString.MaximumLength = sizeof(domainName);
status = RtlUpcaseUnicodeStringToOemString( &oemDomainString, &domainString, FALSE );
if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
domainLength = oemDomainString.Length;
domainNamePointer = &domainName[domainLength];
for ( ; domainLength < NETBIOS_NAME_LEN - 1 ; domainLength++ ) { *domainNamePointer++ = ' '; }
//
// Append the signature byte to the end of the name.
//
*domainNamePointer = SignatureByte;
domainLength += 1;
srp->Name1.Buffer = (PWSTR)domainName; srp->Name1.Length = (USHORT)domainLength; srp->Name1.MaximumLength = (USHORT)domainLength;
if ( ARGUMENT_PRESENT ( Transport ) ) { RtlInitUnicodeString( &srp->Name2, Transport );
} else {
srp->Name2.Buffer = NULL; srp->Name2.Length = 0; srp->Name2.MaximumLength = 0; }
//
// Determine the sizes of various fields that will go in the SMB
// and the total size of the SMB.
//
mailslotNameLength = strlen( MailslotNameText );
dataSize = mailslotNameLength + 1 + MessageLength; smbSize = sizeof(SMB_HEADER) + sizeof(SMB_TRANSACT_MAILSLOT) - 1 + dataSize;
//
// Allocate enough memory to hold the SMB. If we can't allocate the
// memory, don't do an announcement.
//
header = MIDL_user_allocate( smbSize ); if ( header == NULL ) {
SsFreeSrp( srp );
return ERROR_NOT_ENOUGH_MEMORY; }
//
// Fill in the header. Most of the fields don't matter and are
// zeroed.
//
RtlZeroMemory( header, smbSize );
header->Protocol[0] = 0xFF; header->Protocol[1] = 'S'; header->Protocol[2] = 'M'; header->Protocol[3] = 'B'; header->Command = SMB_COM_TRANSACTION;
//
// Get the pointer to the params and fill them in.
//
parameters = (PSMB_TRANSACT_MAILSLOT)( header + 1 ); mailslotName = (LPSTR)( parameters + 1 ) - 1; message = mailslotName + mailslotNameLength + 1;
parameters->WordCount = 0x11; SmbPutUshort( ¶meters->TotalDataCount, (WORD)MessageLength ); SmbPutUlong( ¶meters->Timeout, 0x3E8 ); // !!! fix
SmbPutUshort( ¶meters->DataCount, (WORD)MessageLength ); SmbPutUshort( ¶meters->DataOffset, (WORD)( (DWORD_PTR)message - (DWORD_PTR)header ) ); parameters->SetupWordCount = 3; SmbPutUshort( ¶meters->Opcode, MS_WRITE_OPCODE ); SmbPutUshort( ¶meters->Class, 2 ); SmbPutUshort( ¶meters->ByteCount, (WORD)dataSize );
RtlCopyMemory( mailslotName, MailslotNameText, mailslotNameLength + 1 );
RtlCopyMemory( message, Message, MessageLength );
status = SsServerFsControl( FSCTL_SRV_SEND_DATAGRAM, srp, header, smbSize );
if ( status != NERR_Success ) { SS_PRINT(( "SendSecondClassMailslot: NtFsControlFile failed: %X\n", status )); }
MIDL_user_free( header );
SsFreeSrp( srp );
return status;
} // SendSecondClassMailslot
NTSTATUS OpenBrowser( OUT PHANDLE BrowserHandle ) /*++
Routine Description:
This function opens a handle to the bowser device driver.
Arguments:
OUT PHANDLE BrowserHandle - Returns the handle to the browser.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/ { NTSTATUS ntstatus;
UNICODE_STRING deviceName;
IO_STATUS_BLOCK ioStatusBlock; OBJECT_ATTRIBUTES objectAttributes;
//
// Open the redirector device.
//
RtlInitUnicodeString(&deviceName, DD_BROWSER_DEVICE_NAME_U);
InitializeObjectAttributes( &objectAttributes, &deviceName, OBJ_CASE_INSENSITIVE, NULL, NULL );
ntstatus = NtOpenFile( BrowserHandle, SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, &objectAttributes, &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT );
if (NT_SUCCESS(ntstatus)) { ntstatus = ioStatusBlock.Status; }
return ntstatus;
}
NET_API_STATUS SsBrowserIoControl ( IN DWORD IoControlCode, IN PVOID Buffer, IN DWORD BufferLength, IN PLMDR_REQUEST_PACKET Packet, IN DWORD PacketLength ) { HANDLE browserHandle; NTSTATUS status; PLMDR_REQUEST_PACKET realPacket; DWORD RealPacketSize; DWORD bytesReturned; LPBYTE Where;
//
// Open the browser device driver.
//
if ( !NT_SUCCESS(status = OpenBrowser(&browserHandle)) ) { return RtlNtStatusToDosError(status); }
//
// Now copy the request packet to a new buffer to allow us to pack the
// transport name to the end of the buffer we pass to the driver.
//
RealPacketSize = PacketLength+Packet->TransportName.MaximumLength; RealPacketSize += Packet->EmulatedDomainName.MaximumLength; realPacket = MIDL_user_allocate( RealPacketSize ); if (realPacket == NULL) { return ERROR_NOT_ENOUGH_MEMORY; }
RtlCopyMemory(realPacket, Packet, PacketLength); Where = ((LPBYTE)realPacket)+PacketLength;
if (Packet->TransportName.Length != 0) {
realPacket->TransportName.Buffer = (LPWSTR) Where;
realPacket->TransportName.MaximumLength = Packet->TransportName.MaximumLength;
RtlCopyUnicodeString(&realPacket->TransportName, &Packet->TransportName);
Where += Packet->TransportName.MaximumLength; }
if (Packet->EmulatedDomainName.Length != 0) {
realPacket->EmulatedDomainName.Buffer = (LPWSTR) Where;
realPacket->EmulatedDomainName.MaximumLength = Packet->EmulatedDomainName.MaximumLength;
RtlCopyUnicodeString(&realPacket->EmulatedDomainName, &Packet->EmulatedDomainName);
Where += Packet->EmulatedDomainName.MaximumLength; }
//
// Send the request to the Datagram Receiver DD.
//
if (!DeviceIoControl( browserHandle, IoControlCode, realPacket, RealPacketSize, Buffer, BufferLength, &bytesReturned, NULL )) { status = GetLastError(); }
MIDL_user_free(realPacket);
CloseHandle(browserHandle);
return status;
}
|