mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1357 lines
33 KiB
1357 lines
33 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|