1356 lines
37 KiB
C
1356 lines
37 KiB
C
/*++
|
||
|
||
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;
|
||
|
||
}
|