1357 lines
33 KiB
C
1357 lines
33 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
svcshare.c
|
||
|
||
Abstract:
|
||
|
||
This module contains support routines for the server service.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 13-Feb-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "svcsupp.tmh"
|
||
#pragma hdrstop
|
||
|
||
|
||
BOOLEAN
|
||
FilterTransportName (
|
||
IN PVOID Context,
|
||
IN PVOID Block
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvCopyUnicodeStringToBuffer )
|
||
#pragma alloc_text( PAGE, SrvDeleteOrderedList )
|
||
#pragma alloc_text( PAGE, SrvEnumApiHandler )
|
||
#pragma alloc_text( PAGE, SrvFindEntryInOrderedList )
|
||
#pragma alloc_text( PAGE, SrvFindNextEntryInOrderedList )
|
||
#pragma alloc_text( PAGE, SrvFindUserOnConnection )
|
||
#pragma alloc_text( PAGE, SrvGetResumeHandle )
|
||
#pragma alloc_text( PAGE, SrvInitializeOrderedList )
|
||
#pragma alloc_text( PAGE, SrvInsertEntryOrderedList )
|
||
#pragma alloc_text( PAGE, SrvRemoveEntryOrderedList )
|
||
#pragma alloc_text( PAGE, SrvSendDatagram )
|
||
#pragma alloc_text( PAGE, FilterTransportName )
|
||
#ifdef SLMDBG
|
||
#pragma alloc_text( PAGE, SrvSendSecondClassMailslot )
|
||
#endif
|
||
#pragma alloc_text( PAGE, SrvLengthOfStringInApiBuffer )
|
||
#pragma alloc_text( PAGE, SrvInhibitIdlePowerDown )
|
||
#pragma alloc_text( PAGE, SrvAllowIdlePowerDown )
|
||
#endif
|
||
|
||
|
||
VOID
|
||
SrvCopyUnicodeStringToBuffer (
|
||
IN PUNICODE_STRING String,
|
||
IN PCHAR FixedStructure,
|
||
IN OUT LPWSTR *EndOfVariableData,
|
||
OUT LPWSTR *VariableDataPointer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine puts a single variable-length Unicode string into a
|
||
buffer. The string data is converted to ANSI as it is copied. The
|
||
string is not written if it would overwrite the last fixed structure
|
||
in the buffer.
|
||
|
||
Arguments:
|
||
|
||
String - a pointer to the string to copy into the buffer. If String
|
||
is null (Length == 0 || Buffer == NULL) then a pointer to a
|
||
zero terminator is inserted.
|
||
|
||
FixedStructure - a pointer to the end of the last fixed
|
||
structure in the buffer.
|
||
|
||
EndOfVariableData - the last position on the buffer that variable
|
||
data for this structure can occupy.
|
||
|
||
VariableDataPointer - a pointer to the place in the buffer where
|
||
a pointer to the variable data should be written.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG length;
|
||
ULONG i;
|
||
PWCH src;
|
||
LPWSTR dest;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Determine where in the buffer the string will go, allowing for a
|
||
// zero-terminator.
|
||
//
|
||
|
||
if ( String->Buffer != NULL ) {
|
||
length = String->Length >> 1;
|
||
*EndOfVariableData -= (length + 1);
|
||
} else {
|
||
length = 0;
|
||
*EndOfVariableData -= 1;
|
||
}
|
||
|
||
//
|
||
// Will the string fit? If no, just set the pointer to NULL.
|
||
//
|
||
|
||
if ( (ULONG_PTR)*EndOfVariableData >= (ULONG_PTR)FixedStructure ) {
|
||
|
||
//
|
||
// It fits. Set up the pointer to the place in the buffer where
|
||
// the string will go.
|
||
//
|
||
|
||
*VariableDataPointer = *EndOfVariableData;
|
||
|
||
//
|
||
// Copy the string to the buffer if it is not null.
|
||
//
|
||
|
||
dest = *EndOfVariableData;
|
||
|
||
for ( i = 0, src = String->Buffer; i < length; i++ ) {
|
||
*dest++ = (TCHAR)*src++;
|
||
}
|
||
|
||
//
|
||
// Set the zero terminator.
|
||
//
|
||
|
||
*dest = (TCHAR)(L'\0');
|
||
|
||
} else {
|
||
|
||
//
|
||
// It doesn't fit. Set the offset to NULL.
|
||
//
|
||
|
||
*VariableDataPointer = NULL;
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvCopyUnicodeStringToBuffer
|
||
|
||
|
||
VOID
|
||
SrvDeleteOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
"Deinitializes" or deletes an ordered list head.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head to delete.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
if ( ListHead->Initialized ) {
|
||
|
||
ASSERT( IsListEmpty( &ListHead->ListHead ) );
|
||
|
||
//
|
||
// Indicate that the ordered list has been "deleted".
|
||
//
|
||
|
||
ListHead->Initialized = FALSE;
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvDeleteOrderedList
|
||
|
||
|
||
NTSTATUS
|
||
SrvEnumApiHandler (
|
||
IN PSERVER_REQUEST_PACKET Srp,
|
||
IN PVOID OutputBuffer,
|
||
IN ULONG BufferLength,
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PENUM_FILTER_ROUTINE FilterRoutine,
|
||
IN PENUM_SIZE_ROUTINE SizeRoutine,
|
||
IN PENUM_FILL_ROUTINE FillRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
All Enum and GetInfo APIs are handled by this routine in the server
|
||
FSD. It takes the ResumeHandle in the SRP to find the first
|
||
appropriate block, then calls the passed-in filter routine to check
|
||
if the block should be filled in. If it should, we call the filter
|
||
routine, then try to get another block. This continues until tyhe
|
||
entire list has been walked.
|
||
|
||
Arguments:
|
||
|
||
Srp - a pointer to the SRP for the operation.
|
||
|
||
OutputBuffer - the buffer in which to fill output information.
|
||
|
||
BufferLength - the length of the buffer.
|
||
|
||
ListHead - the head of the ordered list to walk.
|
||
|
||
FilterRoutine - a pointer to a function that will check a block
|
||
against information in the SRP to determine whether the
|
||
information in the block should be placed in the output
|
||
buffer.
|
||
|
||
SizeRoutine - a pointer to a function that will find the total size
|
||
a single block will take up in the output buffer. This routine
|
||
is used to check whether we should bother to call the fill
|
||
routine.
|
||
|
||
FillRoutine - a pointer to a function that will fill in the output
|
||
buffer with information from a block.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - results of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID block;
|
||
PVOID lastBlockRead;
|
||
ULONG totalEntries;
|
||
ULONG entriesRead;
|
||
ULONG bytesRequired;
|
||
ULONG newResumeHandle;
|
||
|
||
PCHAR fixedStructurePointer;
|
||
PCHAR variableData;
|
||
ULONG level;
|
||
ULONG maxResumeHandle;
|
||
|
||
BOOLEAN bufferOverflow = FALSE;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Set up local variables.
|
||
//
|
||
|
||
fixedStructurePointer = OutputBuffer;
|
||
variableData = fixedStructurePointer + BufferLength;
|
||
variableData = (PCHAR)((ULONG_PTR)variableData & ~1);
|
||
level = Srp->Level;
|
||
|
||
lastBlockRead = NULL;
|
||
entriesRead = 0;
|
||
totalEntries = 0;
|
||
bytesRequired = 0;
|
||
newResumeHandle = 0;
|
||
|
||
//
|
||
// Grab the current resume handle in the list we're
|
||
// enumerating. This allows us to return only blocks that existed
|
||
// when the enumeration started, thereby avoiding problems with
|
||
// blocks created after the enumeration distorting the data.
|
||
//
|
||
|
||
maxResumeHandle = ListHead->CurrentResumeHandle;
|
||
|
||
//
|
||
// Get blocks from the global list by using the ordered list
|
||
// routines. We pass resume handle +1 to get the next block after
|
||
// the last one returned. If the passed-in resume handle is 0, this
|
||
// will return the first valid block in the list.
|
||
//
|
||
|
||
block = SrvFindEntryInOrderedList(
|
||
ListHead,
|
||
NULL,
|
||
NULL,
|
||
Srp->Parameters.Get.ResumeHandle + 1,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
while ( block != NULL &&
|
||
SrvGetResumeHandle( ListHead, block ) < maxResumeHandle ) {
|
||
|
||
ULONG blockSize;
|
||
|
||
//
|
||
// Call the filter routine to determine whether we should
|
||
// return this block.
|
||
//
|
||
|
||
if ( FilterRoutine( Srp, block ) ) {
|
||
|
||
blockSize = SizeRoutine( Srp, block );
|
||
|
||
totalEntries++;
|
||
bytesRequired += blockSize;
|
||
|
||
//
|
||
// If all the information in the block will fit in the
|
||
// output buffer, write it. Otherwise, indicate that there
|
||
// was an overflow. As soon as an entry doesn't fit, stop
|
||
// putting them in the buffer. This ensures that the resume
|
||
// mechanism will work--retuning partial entries would make
|
||
// it nearly impossible to use the resumability of the APIs,
|
||
// since the caller would have to resume from an imcomplete
|
||
// entry.
|
||
//
|
||
|
||
if ( (ULONG_PTR)fixedStructurePointer + blockSize <=
|
||
(ULONG_PTR)variableData && !bufferOverflow ) {
|
||
|
||
FillRoutine(
|
||
Srp,
|
||
block,
|
||
(PVOID *)&fixedStructurePointer,
|
||
(LPWSTR *)&variableData
|
||
);
|
||
|
||
entriesRead++;
|
||
lastBlockRead = block;
|
||
newResumeHandle = SrvGetResumeHandle( ListHead, lastBlockRead );
|
||
} else {
|
||
|
||
bufferOverflow = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the next block in the list. This routine will dereference
|
||
// the block we have been looking at and get a new block if a valid
|
||
// one exists.
|
||
//
|
||
|
||
block = SrvFindNextEntryInOrderedList( ListHead, block );
|
||
}
|
||
|
||
//
|
||
// Dereference this last one.
|
||
//
|
||
|
||
if ( block != NULL ) {
|
||
|
||
ListHead->DereferenceRoutine( block );
|
||
|
||
}
|
||
|
||
//
|
||
// Set the information to pass back to the server service.
|
||
//
|
||
|
||
Srp->Parameters.Get.EntriesRead = entriesRead;
|
||
Srp->Parameters.Get.TotalEntries = totalEntries;
|
||
Srp->Parameters.Get.TotalBytesNeeded = bytesRequired;
|
||
|
||
//
|
||
// If we found at least one block, return the resume handle for it.
|
||
// If we didn't find any blocks, don't modify the resume handle.
|
||
//
|
||
|
||
if ( lastBlockRead != NULL ) {
|
||
Srp->Parameters.Get.ResumeHandle = newResumeHandle;
|
||
}
|
||
|
||
//
|
||
// Return appropriate status.
|
||
//
|
||
|
||
if ( entriesRead == 0 && totalEntries > 0 ) {
|
||
|
||
//
|
||
// Not even a single entry fit.
|
||
//
|
||
|
||
Srp->ErrorCode = NERR_BufTooSmall;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else if ( bufferOverflow ) {
|
||
|
||
//
|
||
// At least one entry fit, but not all of them.
|
||
//
|
||
|
||
Srp->ErrorCode = ERROR_MORE_DATA;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// All entries fit.
|
||
//
|
||
|
||
Srp->ErrorCode = NO_ERROR;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
} // SrvEnumApiHandler
|
||
|
||
|
||
PVOID
|
||
SrvFindEntryInOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PFILTER_ROUTINE FilterRoutine OPTIONAL,
|
||
IN PVOID Context OPTIONAL,
|
||
IN ULONG ResumeHandle,
|
||
IN BOOLEAN ExactHandleMatch,
|
||
IN PLIST_ENTRY StartLocation OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uses a filter routine or resume handle to find an entry
|
||
in an ordered list. It walks the list, looking for a block with a
|
||
resume handle less than or equal to the specified resume handle, or
|
||
a block that passes the filter routine's tests. If a matching
|
||
handle or passing block is found, the block is referenced and a
|
||
pointer to it is returned. If ExactHandleMatch is FALSE and there
|
||
is no exact match of the handle, then the first block with a resume
|
||
handle greater than the one specified is referenced and returned.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head to search.
|
||
|
||
FilterRoutine - a routine that will check whether a block is valid
|
||
for the purposes of the calling routine.
|
||
|
||
Context - a pointer to pass to the filter routine.
|
||
|
||
ResumeHandle - the resume handle to look for. If a filter routine
|
||
is specified, this parameter should be -1.
|
||
|
||
ExactHandleMatch - if TRUE, only an exact match is returned. If there
|
||
is no exact match, return NULL. If a filter routine is specified
|
||
this should be FALSE.
|
||
|
||
StartLocation - if specified, start looking at this location in
|
||
the list. This is used by SrvFindNextEntryInOrderedList to
|
||
speed up finding a valid block.
|
||
|
||
Return Value:
|
||
|
||
PVOID - NULL if no block matched or if the handle is beyond the end of
|
||
the list. A pointer to a block if a valid block is found. The
|
||
block is referenced.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY listEntry;
|
||
PVOID block;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Acquire the lock that protects the ordered list.
|
||
//
|
||
|
||
ACQUIRE_LOCK( ListHead->Lock );
|
||
|
||
//
|
||
// Find the starting location for the search. If a start was
|
||
// specified, start there; otherwise, start at the beginning of the
|
||
// list.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( StartLocation ) ) {
|
||
listEntry = StartLocation;
|
||
} else {
|
||
listEntry = ListHead->ListHead.Flink;
|
||
}
|
||
|
||
//
|
||
// Walk the list of blocks until we find one with a resume handle
|
||
// greater than or equal to the specified resume handle.
|
||
//
|
||
|
||
for ( ; listEntry != &ListHead->ListHead; listEntry = listEntry->Flink ) {
|
||
|
||
ULONG currentResumeHandle;
|
||
|
||
currentResumeHandle = ((PORDERED_LIST_ENTRY)listEntry)->ResumeHandle;
|
||
|
||
//
|
||
// Get a pointer to the actual block.
|
||
//
|
||
|
||
block = (PCHAR)listEntry - ListHead->ListEntryOffset;
|
||
|
||
//
|
||
// Determine whether we've reached the specified handle, or
|
||
// whether the block passes the filter routine's tests.
|
||
//
|
||
|
||
if ( currentResumeHandle >= ResumeHandle ||
|
||
( ARGUMENT_PRESENT( FilterRoutine ) &&
|
||
FilterRoutine( Context, block ) ) ) {
|
||
|
||
if ( ExactHandleMatch && currentResumeHandle != ResumeHandle ) {
|
||
|
||
//
|
||
// We have passed the specified resume handle without
|
||
// finding an exact match. Return NULL, indicating that
|
||
// no exact match exists.
|
||
//
|
||
|
||
RELEASE_LOCK( ListHead->Lock );
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Check the state of the block and if it is active,
|
||
// reference it. This must be done as an atomic operation
|
||
// order to prevent the block from being deleted.
|
||
//
|
||
|
||
if ( ListHead->ReferenceRoutine( block ) ) {
|
||
|
||
//
|
||
// Release the list lock and return a pointer to the
|
||
// block to the caller.
|
||
//
|
||
|
||
RELEASE_LOCK( ListHead->Lock );
|
||
|
||
return block;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} // walk list
|
||
|
||
//
|
||
// If we are here, it means that we walked the entire list without
|
||
// finding a valid match. Release the list lock and return NULL.
|
||
//
|
||
|
||
RELEASE_LOCK( ListHead->Lock );
|
||
|
||
return NULL;
|
||
|
||
} // SrvFindEntryInOrderedList
|
||
|
||
|
||
PVOID
|
||
SrvFindNextEntryInOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PVOID Block
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds the next valid block after the one passed in.
|
||
It calls SrvFindEntryInOrderedList to do most of the work. It
|
||
also handles dereferencing the passed-in block and referencing the
|
||
returned block. The passed-in block is dereferenced regardless
|
||
of whether a block is returned, so calling routines must be careful
|
||
to obtain all the information they need from the block before
|
||
calling this routine.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head to search.
|
||
|
||
Block - a pointer to the block after which we should look for
|
||
the next block.
|
||
|
||
Return Value:
|
||
|
||
PVOID - NULL if no block matched or if the handle is beyond the end of
|
||
the list. A pointer to a block if a valid block is found.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID returnBlock;
|
||
PORDERED_LIST_ENTRY listEntry;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Find the ordered list entry in the block. We need this to pass
|
||
// the start location and resume handle to
|
||
// SrvFindEntryInOrderedList.
|
||
//
|
||
|
||
listEntry =
|
||
(PORDERED_LIST_ENTRY)( (PCHAR)Block + ListHead->ListEntryOffset );
|
||
|
||
//
|
||
// Call SrvFindEntryInOrderedList with a start location. This will
|
||
// find the block to return, if any.
|
||
//
|
||
// This adds one to the resume handle because we want the *next*
|
||
// block, not this one, to be returned.
|
||
//
|
||
|
||
returnBlock = SrvFindEntryInOrderedList(
|
||
ListHead,
|
||
NULL,
|
||
NULL,
|
||
listEntry->ResumeHandle + 1,
|
||
FALSE,
|
||
&listEntry->ListEntry
|
||
);
|
||
|
||
//
|
||
// Dereference the passed-in block.
|
||
//
|
||
|
||
ListHead->DereferenceRoutine( Block );
|
||
|
||
//
|
||
// Return what we got from SrvFindEntryInOrderedList.
|
||
//
|
||
|
||
return returnBlock;
|
||
|
||
} // SrvFindNextEntryInOrderedList
|
||
|
||
|
||
PSESSION
|
||
SrvFindUserOnConnection (
|
||
IN PCONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Finds a "legitimate" user on a virtual circuit. This routine is
|
||
an attempt to find a good username to return even though there
|
||
may be multiple users on a VC. Some of the APIs assume that there
|
||
will be one user per VC, and this is an attempt to support that
|
||
bahavior.
|
||
|
||
The following rules are used:
|
||
|
||
0 users--return NULL.
|
||
|
||
1 user--return a pointer to that session block.
|
||
|
||
2 users--if one matches the computer name, return the other. This
|
||
is because RIPL sessions have a session name matching the
|
||
client name, and this is probably not a useful user. If both
|
||
usernames differ from the computer name, return NULL.
|
||
|
||
3 or more users--return NULL.
|
||
|
||
*** THIS ROUTINE MUST BE CALLED WITH THE CONNECTION LOCK HELD. It
|
||
remains held on exit.
|
||
|
||
Arguments:
|
||
|
||
Connection - a pointer to the connection block to search for a user.
|
||
|
||
Return Value:
|
||
|
||
NULL or a pointer to a session.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSESSION matchingSession = NULL;
|
||
PSESSION nonMatchingSession = NULL;
|
||
USHORT i;
|
||
PPAGED_CONNECTION pagedConnection = Connection->PagedConnection;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Walk the connection's session table looking for valid sessions.
|
||
//
|
||
|
||
for ( i = 0; i < pagedConnection->SessionTable.TableSize; i++ ) {
|
||
|
||
PSESSION session;
|
||
|
||
session = pagedConnection->SessionTable.Table[i].Owner;
|
||
|
||
//
|
||
// Determine whether this is a valid session.
|
||
//
|
||
|
||
if ( session != NULL && GET_BLOCK_STATE(session) == BlockStateActive ) {
|
||
|
||
//
|
||
// It is a valid session. Determine whether the name matches
|
||
// the connection's client name.
|
||
//
|
||
|
||
UNICODE_STRING computerName, userName;
|
||
|
||
computerName.Buffer = pagedConnection->ClientMachineName;
|
||
computerName.Length =
|
||
(USHORT)( Connection->PagedConnection->ClientMachineNameString.Length -
|
||
sizeof(WCHAR) * 2 );
|
||
|
||
SrvGetUserAndDomainName( session, &userName, NULL );
|
||
|
||
if( userName.Buffer && userName.Length != 0 ) {
|
||
|
||
if ( RtlCompareUnicodeString(
|
||
&computerName,
|
||
&userName,
|
||
TRUE ) == 0 ) {
|
||
|
||
//
|
||
// The user name and machine name are the same.
|
||
//
|
||
|
||
matchingSession = session;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we already found another user name that doesn't match
|
||
// the client computer name, we're hosed. Return NULL.
|
||
//
|
||
|
||
if ( nonMatchingSession != NULL ) {
|
||
SrvReleaseUserAndDomainName( session, &userName, NULL );
|
||
return NULL;
|
||
}
|
||
|
||
nonMatchingSession = session;
|
||
|
||
} // does session user name match computer name?
|
||
|
||
SrvReleaseUserAndDomainName( session, &userName, NULL );
|
||
}
|
||
|
||
} // valid session?
|
||
|
||
} // walk session table
|
||
|
||
//
|
||
// If only one non-matching name was found, we got here, so return
|
||
// that session.
|
||
//
|
||
|
||
if ( nonMatchingSession != NULL ) {
|
||
return nonMatchingSession;
|
||
}
|
||
|
||
//
|
||
// If a matching session was found return it, or return NULL if
|
||
// no sessions matched.
|
||
//
|
||
|
||
return matchingSession;
|
||
|
||
} // SrvFindUserOnConnection
|
||
|
||
|
||
ULONG
|
||
SrvGetResumeHandle (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PVOID Block
|
||
)
|
||
{
|
||
PORDERED_LIST_ENTRY listEntry;
|
||
|
||
PAGED_CODE( );
|
||
|
||
// !!! make this a macro?
|
||
|
||
listEntry =
|
||
(PORDERED_LIST_ENTRY)( (PCHAR)Block + ListHead->ListEntryOffset );
|
||
|
||
return listEntry->ResumeHandle;
|
||
|
||
} // SrvGetResumeHandle
|
||
|
||
|
||
VOID
|
||
SrvInitializeOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN ULONG ListEntryOffset,
|
||
IN PREFERENCE_ROUTINE ReferenceRoutine,
|
||
IN PDEREFERENCE_ROUTINE DereferenceRoutine,
|
||
IN PSRV_LOCK Lock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes an ordered list. It initializes the list
|
||
head and lock and sets up other header fields from the information
|
||
passed in.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head to initialize.
|
||
|
||
ListEntryOffset - the offset into a data block in the list to the
|
||
ORDERED_LIST_ENTRY field. This is used to find the start of
|
||
the block from the list entry field.
|
||
|
||
ReferenceRoutine - a pointer to the routine to call to reference
|
||
a data block stored in the list. This is done to prevent the
|
||
data block from going away between when we find it and when
|
||
higher-level routines start using it.
|
||
|
||
DereferenceRoutine - a pointer to the routine to call to dereference
|
||
a data block stored in the list.
|
||
|
||
Lock - a pointer to a lock to use for synchronization.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
ASSERT( !ListHead->Initialized );
|
||
|
||
//
|
||
// Initialize the head of the doubly linked list.
|
||
//
|
||
|
||
InitializeListHead( &ListHead->ListHead );
|
||
|
||
//
|
||
// Save the address of the list lock.
|
||
//
|
||
|
||
ASSERT( ARGUMENT_PRESENT(Lock) );
|
||
ListHead->Lock = Lock;
|
||
|
||
//
|
||
// Initialize other fields in the header.
|
||
//
|
||
|
||
ListHead->CurrentResumeHandle = 1;
|
||
ListHead->ListEntryOffset = ListEntryOffset;
|
||
ListHead->ReferenceRoutine = ReferenceRoutine,
|
||
ListHead->DereferenceRoutine = DereferenceRoutine;
|
||
|
||
ListHead->Initialized = TRUE;
|
||
|
||
return;
|
||
|
||
} // SrvInitializeOrderedList
|
||
|
||
|
||
VOID
|
||
SrvInsertEntryOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PVOID Block
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine inserts an entry in an ordered list. The entry is
|
||
placed on the doubly linked list and the resume handle is set.
|
||
|
||
*** It is the responsibility of that calling routine to ensure that
|
||
the block does not go away while this routine executes.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head on which to put the block.
|
||
|
||
Block - a pointer to the data block to place on the list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PORDERED_LIST_ENTRY listEntry;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Determine where the list entry field is.
|
||
//
|
||
|
||
listEntry = (PORDERED_LIST_ENTRY)
|
||
( (PCHAR)Block + ListHead->ListEntryOffset );
|
||
|
||
//
|
||
// Acquire the lock that protects the ordered list.
|
||
//
|
||
|
||
ACQUIRE_LOCK( ListHead->Lock );
|
||
|
||
//
|
||
// Insert the entry in the doubly linked list.
|
||
//
|
||
|
||
SrvInsertTailList( &ListHead->ListHead, &listEntry->ListEntry );
|
||
|
||
//
|
||
// Set up the resume handle in the block and update the current
|
||
// handle in the header.
|
||
//
|
||
|
||
listEntry->ResumeHandle = ListHead->CurrentResumeHandle;
|
||
ListHead->CurrentResumeHandle++;
|
||
|
||
//
|
||
// Release the lock and return.
|
||
//
|
||
|
||
RELEASE_LOCK( ListHead->Lock );
|
||
|
||
return;
|
||
|
||
} // SrvInsertEntryOrderedList
|
||
|
||
|
||
VOID
|
||
SrvRemoveEntryOrderedList (
|
||
IN PORDERED_LIST_HEAD ListHead,
|
||
IN PVOID Block
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes an entry from an ordered list.
|
||
|
||
*** It is the responsibility of that calling routine to ensure that
|
||
the block does not go away while this routine executes.
|
||
|
||
Arguments:
|
||
|
||
ListHead - a pointer to the list head on which to put the block.
|
||
|
||
Block - a pointer to the data block to place on the list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PORDERED_LIST_ENTRY listEntry;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Determine where the list entry field is.
|
||
//
|
||
|
||
listEntry = (PORDERED_LIST_ENTRY)
|
||
( (PCHAR)Block + ListHead->ListEntryOffset );
|
||
|
||
//
|
||
// Acquire the lock that protects the ordered list.
|
||
//
|
||
|
||
ACQUIRE_LOCK( ListHead->Lock );
|
||
|
||
//
|
||
// Remove the entry from the doubly linked list.
|
||
//
|
||
|
||
SrvRemoveEntryList( &ListHead->ListHead, &listEntry->ListEntry );
|
||
|
||
//
|
||
// Release the lock and return.
|
||
//
|
||
|
||
RELEASE_LOCK( ListHead->Lock );
|
||
|
||
return;
|
||
|
||
} // SrvRemoveEntryOrderedList
|
||
|
||
|
||
NTSTATUS
|
||
SrvSendDatagram (
|
||
IN PANSI_STRING Domain,
|
||
IN PUNICODE_STRING Transport OPTIONAL,
|
||
IN PVOID Buffer,
|
||
IN ULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a datagram to the specified domain.
|
||
|
||
!!! Temporary--should go away when we have real 2nd-class mailslot
|
||
support.
|
||
|
||
Arguments:
|
||
|
||
Domain - the name of the domain to send to. Note that the domain
|
||
name must be padded with spaces and terminated with the
|
||
appropriate signature byte (00 or 07) by the caller.
|
||
|
||
Transport - the name of the transport to send to. If not present, then
|
||
the datagram is sent on all transports.
|
||
|
||
Buffer - the message to send.
|
||
|
||
BufferLength - the length of the buffer,
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - results of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG connectionInformationSize;
|
||
PTDI_CONNECTION_INFORMATION connectionInformation;
|
||
PTA_NETBIOS_ADDRESS taNetbiosAddress;
|
||
PENDPOINT endpoint;
|
||
|
||
PAGED_CODE( );
|
||
|
||
connectionInformationSize = sizeof(TDI_CONNECTION_INFORMATION) +
|
||
sizeof(TA_NETBIOS_ADDRESS);
|
||
connectionInformation = ALLOCATE_NONPAGED_POOL(
|
||
connectionInformationSize,
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
if ( connectionInformation == NULL ) {
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
|
||
connectionInformation->UserDataLength = 0;
|
||
connectionInformation->UserData = NULL;
|
||
connectionInformation->OptionsLength = 0;
|
||
connectionInformation->Options = NULL;
|
||
connectionInformation->RemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS);
|
||
|
||
taNetbiosAddress = (PTA_NETBIOS_ADDRESS)(connectionInformation + 1);
|
||
connectionInformation->RemoteAddress = taNetbiosAddress;
|
||
taNetbiosAddress->TAAddressCount = 1;
|
||
taNetbiosAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS;
|
||
taNetbiosAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_NETBIOS);
|
||
taNetbiosAddress->Address[0].Address[0].NetbiosNameType = 0;
|
||
|
||
RtlCopyMemory(
|
||
taNetbiosAddress->Address[0].Address[0].NetbiosName,
|
||
Domain->Buffer,
|
||
MIN( Domain->Length, COMPUTER_NAME_LENGTH + 1 )
|
||
);
|
||
|
||
endpoint = SrvFindEntryInOrderedList(
|
||
&SrvEndpointList,
|
||
FilterTransportName,
|
||
Transport,
|
||
(ULONG)-1,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
while ( endpoint != NULL ) {
|
||
|
||
if ( !endpoint->IsConnectionless ) {
|
||
|
||
if( endpoint->IsNoNetBios ) {
|
||
//
|
||
// Make mailslot sends over this transport "always work"
|
||
//
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
status = SrvIssueSendDatagramRequest(
|
||
endpoint->FileObject,
|
||
&endpoint->DeviceObject,
|
||
connectionInformation,
|
||
Buffer,
|
||
BufferLength
|
||
);
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Dereference the endpoint if this was targetted to a specific
|
||
// transport, and return an error.
|
||
//
|
||
|
||
if (Transport != NULL) {
|
||
|
||
DEALLOCATE_NONPAGED_POOL( connectionInformation );
|
||
SrvDereferenceEndpoint( endpoint );
|
||
|
||
return STATUS_REQUEST_NOT_ACCEPTED;
|
||
}
|
||
}
|
||
|
||
|
||
if (Transport == NULL) {
|
||
|
||
//
|
||
// Find the next endpoint. This will dereference the current
|
||
// endpoint.
|
||
//
|
||
|
||
endpoint = SrvFindNextEntryInOrderedList( &SrvEndpointList, endpoint );
|
||
|
||
} else {
|
||
|
||
//
|
||
// This datagram was destined to a specific endpoint. Do not
|
||
// look for the next endpoint.
|
||
//
|
||
|
||
SrvDereferenceEndpoint( endpoint );
|
||
endpoint = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
DEALLOCATE_NONPAGED_POOL( connectionInformation );
|
||
|
||
return status;
|
||
|
||
} // SrvSendDatagram
|
||
|
||
|
||
BOOLEAN
|
||
FilterTransportName (
|
||
IN PVOID Context,
|
||
IN PVOID Block
|
||
)
|
||
{
|
||
PENDPOINT endpoint = Block;
|
||
|
||
PAGED_CODE( );
|
||
|
||
if ( Context == NULL ) {
|
||
return( TRUE );
|
||
}
|
||
|
||
return ( RtlEqualUnicodeString ( &endpoint->TransportName, (PUNICODE_STRING)Context, TRUE ) );
|
||
}
|
||
|
||
#ifdef SLMDBG
|
||
#define toupper(a) ( (a <= 'a' || a >= 'z') ? (ULONG)a : (a - ('a' - 'A')) )
|
||
|
||
VOID
|
||
SrvSendSecondClassMailslot (
|
||
IN PVOID Message,
|
||
IN ULONG MessageLength,
|
||
IN PCHAR Domain,
|
||
IN PSZ UserName
|
||
)
|
||
{
|
||
ULONG dataSize;
|
||
ULONG transactionDataSize;
|
||
ULONG smbSize;
|
||
PSMB_HEADER header;
|
||
PSMB_TRANSACT_MAILSLOT parameters;
|
||
PSZ mailslotNameData = "\\MAILSLOT\\MESSNGR";
|
||
PSZ mailslotName;
|
||
ULONG mailslotNameLength;
|
||
PSZ userName;
|
||
PSZ domainInData;
|
||
ULONG userNameLength;
|
||
PVOID message;
|
||
STRING domain;
|
||
ULONG domainLength;
|
||
CHAR domainName[COMPUTER_NAME_LENGTH+1];
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Upcase the domain and find the domain length.
|
||
//
|
||
|
||
for ( domainLength = 0; Domain[domainLength] != 0; domainLength++ ) {
|
||
domainName[domainLength] = (CHAR)toupper( Domain[domainLength] );
|
||
}
|
||
|
||
//
|
||
// Now fill the remaining domain anme with spaces.
|
||
//
|
||
|
||
for ( ; domainLength < COMPUTER_NAME_LENGTH ; domainLength++ ) {
|
||
domainName[domainLength] = ' ';
|
||
}
|
||
|
||
domainName[++domainLength] = '\0';
|
||
|
||
ASSERT( domainLength == COMPUTER_NAME_LENGTH + 1);
|
||
|
||
domain.Buffer = domainName;
|
||
domain.Length = domain.MaximumLength = (USHORT)domainLength;
|
||
|
||
//
|
||
// Determine the sizes of various fields that will go in the SMB
|
||
// and the total size of the SMB.
|
||
//
|
||
|
||
mailslotNameLength = strlen( mailslotNameData );
|
||
userNameLength = strlen( UserName );
|
||
|
||
transactionDataSize = userNameLength + 1 + domainLength + 1 + MessageLength;
|
||
dataSize = mailslotNameLength + 1 + transactionDataSize;
|
||
smbSize = sizeof(SMB_HEADER) + sizeof(SMB_TRANSACT_MAILSLOT) - 1 + dataSize;
|
||
|
||
header = ALLOCATE_HEAP_COLD( smbSize, BlockTypeDataBuffer );
|
||
if ( header == NULL ) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// 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 = (PSZ)( parameters + 1 ) - 1;
|
||
userName = mailslotName + mailslotNameLength + 1;
|
||
domainInData = userName + userNameLength + 1;
|
||
message = domainInData + domainLength + 1;
|
||
|
||
parameters->WordCount = 0x11;
|
||
SmbPutUshort( ¶meters->TotalDataCount, (USHORT)transactionDataSize );
|
||
SmbPutUlong( ¶meters->Timeout, 0x3E8 ); // !!! fix
|
||
SmbPutUshort( ¶meters->DataCount, (USHORT)transactionDataSize );
|
||
SmbPutUshort(
|
||
¶meters->DataOffset,
|
||
(USHORT)( (ULONG)userName - (ULONG)header )
|
||
);
|
||
parameters->SetupWordCount = 3;
|
||
SmbPutUshort( ¶meters->Opcode, MS_WRITE_OPCODE );
|
||
SmbPutUshort( ¶meters->Class, 2 );
|
||
SmbPutUshort( ¶meters->ByteCount, (USHORT)dataSize );
|
||
|
||
RtlCopyMemory( mailslotName, mailslotNameData, mailslotNameLength + 1 );
|
||
RtlCopyMemory( userName, UserName, userNameLength + 1 );
|
||
RtlCopyMemory( domainInData, Domain, domainLength + 1 );
|
||
RtlCopyMemory( message, Message, MessageLength );
|
||
|
||
//
|
||
// Send the actual mailslot message.
|
||
//
|
||
|
||
SrvSendDatagram( &domain, NULL, header, smbSize );
|
||
|
||
FREE_HEAP( header );
|
||
|
||
return;
|
||
|
||
} // SrvSendSecondClassMailslot
|
||
#endif
|
||
|
||
|
||
ULONG
|
||
SrvLengthOfStringInApiBuffer (
|
||
IN PUNICODE_STRING UnicodeString
|
||
)
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
if ( UnicodeString == NULL ) {
|
||
return 0;
|
||
}
|
||
|
||
return UnicodeString->Length + sizeof(UNICODE_NULL);
|
||
|
||
} // SrvLengthOfStringInApiBuffer
|
||
|
||
//
|
||
// Ensure that the system will not go into a power-down idle standby mode
|
||
//
|
||
VOID
|
||
SrvInhibitIdlePowerDown()
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if( SrvPoRegistrationState != NULL &&
|
||
InterlockedIncrement( &SrvIdleCount ) == 1 ) {
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(( "SRV: Calling PoRegisterSystemState to inhibit idle standby\n" ));
|
||
}
|
||
|
||
PoRegisterSystemState( SrvPoRegistrationState, ES_SYSTEM_REQUIRED | ES_CONTINUOUS );
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allow the system to go into a power-down idle standby mode
|
||
//
|
||
VOID
|
||
SrvAllowIdlePowerDown()
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if( SrvPoRegistrationState != NULL &&
|
||
InterlockedDecrement( &SrvIdleCount ) == 0 ) {
|
||
|
||
IF_DEBUG( PNP ) {
|
||
KdPrint(( "SRV: Calling PoRegisterSystemState to allow idle standby\n" ));
|
||
}
|
||
|
||
PoRegisterSystemState( SrvPoRegistrationState, ES_CONTINUOUS );
|
||
}
|
||
}
|
||
|