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.
5654 lines
153 KiB
5654 lines
153 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Attach.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines for the NetWare
|
|
redirector to connect and disconnect from a server.
|
|
|
|
Author:
|
|
|
|
Colin Watson [ColinW] 10-Jan-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "Procs.h"
|
|
#include <stdlib.h> // rand
|
|
|
|
//
|
|
// The number of bytes in the ipx host address, not
|
|
// including the socket.
|
|
//
|
|
|
|
#define IPX_HOST_ADDR_LEN 10
|
|
|
|
//
|
|
// The debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_CREATE)
|
|
|
|
VOID
|
|
ExtractNextComponentName (
|
|
OUT PUNICODE_STRING Name,
|
|
IN PUNICODE_STRING Path,
|
|
IN BOOLEAN ColonSeparator
|
|
);
|
|
|
|
NTSTATUS
|
|
ExtractPathAndFileName(
|
|
IN PUNICODE_STRING EntryPath,
|
|
OUT PUNICODE_STRING PathString,
|
|
OUT PUNICODE_STRING FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
DoBinderyLogon(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password
|
|
);
|
|
|
|
NTSTATUS
|
|
ConnectToServer(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
OUT PSCB *pScbCollision
|
|
);
|
|
|
|
BOOLEAN
|
|
ProcessFindNearestEntry(
|
|
PIRP_CONTEXT IrpContext,
|
|
PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
|
|
);
|
|
|
|
NTSTATUS
|
|
GetMaxPacketSize(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb
|
|
);
|
|
|
|
PNONPAGED_SCB
|
|
FindServer(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb,
|
|
PUNICODE_STRING ServerName
|
|
);
|
|
|
|
NTSTATUS
|
|
NwAllocateAndInitScb(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING UidServerName OPTIONAL,
|
|
IN PUNICODE_STRING ServerName OPTIONAL,
|
|
OUT PSCB *ppScb
|
|
);
|
|
|
|
NTSTATUS
|
|
IndirectToSeedServer(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PUNICODE_STRING pServerName,
|
|
PUNICODE_STRING pNewServer
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, ExtractNextComponentName )
|
|
#pragma alloc_text( PAGE, ExtractPathAndFileName )
|
|
#pragma alloc_text( PAGE, CrackPath )
|
|
#pragma alloc_text( PAGE, CreateScb )
|
|
#pragma alloc_text( PAGE, FindServer )
|
|
#pragma alloc_text( PAGE, ProcessFindNearestEntry )
|
|
#pragma alloc_text( PAGE, NegotiateBurstMode )
|
|
#pragma alloc_text( PAGE, GetMaxPacketSize )
|
|
#pragma alloc_text( PAGE, NwDeleteScb )
|
|
#pragma alloc_text( PAGE, NwLogoffAndDisconnect )
|
|
#pragma alloc_text( PAGE, InitializeAttach )
|
|
#pragma alloc_text( PAGE, OpenScbSockets )
|
|
#pragma alloc_text( PAGE, DoBinderyLogon )
|
|
#pragma alloc_text( PAGE, QueryServersAddress )
|
|
#pragma alloc_text( PAGE, TreeConnectScb )
|
|
#pragma alloc_text( PAGE, TreeDisconnectScb )
|
|
|
|
#ifndef QFE_BUILD
|
|
#pragma alloc_text( PAGE1, ProcessFindNearest )
|
|
#pragma alloc_text( PAGE1, NwLogoffAllServers )
|
|
#pragma alloc_text( PAGE1, DestroyAllScb )
|
|
#pragma alloc_text( PAGE1, SelectConnection )
|
|
#pragma alloc_text( PAGE1, NwFindScb )
|
|
#pragma alloc_text( PAGE1, ConnectToServer )
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if 0 // Not pageable
|
|
|
|
// see ifndef QFE_BUILD above
|
|
|
|
#endif
|
|
|
|
|
|
VOID
|
|
ExtractNextComponentName (
|
|
OUT PUNICODE_STRING Name,
|
|
IN PUNICODE_STRING Path,
|
|
IN BOOLEAN ColonSeparator
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extracts a the "next" component from a path string.
|
|
|
|
It assumes that
|
|
|
|
Arguments:
|
|
|
|
Name - Returns a pointer to the component.
|
|
|
|
Path - Supplies a pointer to the backslash seperated pathname.
|
|
|
|
ColonSeparator - A colon can be used to terminate this component
|
|
name.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
register USHORT i; // Index into Name string.
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Path->Length == 0) {
|
|
RtlInitUnicodeString(Name, NULL);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the extracted name to the name passed in skipping the
|
|
// leading backslash.
|
|
//
|
|
|
|
// DebugTrace(+0, Dbg, "NwExtractNextComponentName = %wZ\n", Path );
|
|
|
|
Name->Buffer = Path->Buffer + 1;
|
|
Name->Length = Path->Length - sizeof(WCHAR);
|
|
Name->MaximumLength = Path->MaximumLength - sizeof(WCHAR);
|
|
|
|
//
|
|
// Scan forward finding the terminal "\" in the server name.
|
|
//
|
|
|
|
for (i=0;i<(USHORT)(Name->Length/sizeof(WCHAR));i++) {
|
|
|
|
if ( Name->Buffer[i] == OBJ_NAME_PATH_SEPARATOR ||
|
|
( ColonSeparator && Name->Buffer[i] == L':' ) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the length and maximum length of the structure
|
|
// to match the new length.
|
|
//
|
|
|
|
Name->Length = Name->MaximumLength = (USHORT)(i*sizeof(WCHAR));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ExtractPathAndFileName (
|
|
IN PUNICODE_STRING EntryPath,
|
|
OUT PUNICODE_STRING PathString,
|
|
OUT PUNICODE_STRING FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cracks the entry path into two pieces, the path and the file
|
|
name component at the start of the name.
|
|
|
|
|
|
Arguments:
|
|
|
|
IN PUNICODE_STRING EntryPath - Supplies the path to disect.
|
|
OUT PUNICODE_STRING PathString - Returns the directory containing the file.
|
|
OUT PUNICODE_STRING FileName - Returns the file name specified.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - SUCCESS
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING Component;
|
|
UNICODE_STRING FilePath = *EntryPath;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Strip trailing separators
|
|
while ( (FilePath.Length != 0) &&
|
|
FilePath.Buffer[(FilePath.Length-1)/sizeof(WCHAR)] ==
|
|
OBJ_NAME_PATH_SEPARATOR ) {
|
|
|
|
FilePath.Length -= sizeof(WCHAR);
|
|
FilePath.MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
// PathString will become EntryPath minus FileName and trailing separators
|
|
*PathString = FilePath;
|
|
|
|
// Initialize FileName just incase there are no components at all.
|
|
RtlInitUnicodeString( FileName, NULL );
|
|
|
|
//
|
|
// Scan through the current file name to find the entire path
|
|
// up to (but not including) the last component in the path.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// Extract the next component from the name.
|
|
//
|
|
|
|
ExtractNextComponentName(&Component, &FilePath, FALSE);
|
|
|
|
//
|
|
// Bump the "remaining name" pointer by the length of this
|
|
// component
|
|
//
|
|
|
|
if (Component.Length != 0) {
|
|
|
|
FilePath.Length -= Component.Length+sizeof(WCHAR);
|
|
FilePath.MaximumLength -= Component.MaximumLength+sizeof(WCHAR);
|
|
FilePath.Buffer += (Component.Length/sizeof(WCHAR))+1;
|
|
|
|
*FileName = Component;
|
|
}
|
|
|
|
|
|
} while (Component.Length != 0);
|
|
|
|
//
|
|
// Take the name, subtract the last component of the name
|
|
// and concatenate the current path with the new path.
|
|
//
|
|
|
|
if ( FileName->Length != 0 ) {
|
|
|
|
//
|
|
// Set the path's name based on the original name, subtracting
|
|
// the length of the name portion (including the "\")
|
|
//
|
|
|
|
PathString->Length -= (FileName->Length + sizeof(WCHAR));
|
|
if ( PathString->Length != 0 ) {
|
|
PathString->MaximumLength -= (FileName->MaximumLength + sizeof(WCHAR));
|
|
} else{
|
|
RtlInitUnicodeString( PathString, NULL );
|
|
}
|
|
} else {
|
|
|
|
// There was no path or filename
|
|
|
|
RtlInitUnicodeString( PathString, NULL );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CrackPath (
|
|
IN PUNICODE_STRING BaseName,
|
|
OUT PUNICODE_STRING DriveName,
|
|
OUT PWCHAR DriveLetter,
|
|
OUT PUNICODE_STRING ServerName,
|
|
OUT PUNICODE_STRING VolumeName,
|
|
OUT PUNICODE_STRING PathName,
|
|
OUT PUNICODE_STRING FileName,
|
|
OUT PUNICODE_STRING FullName OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extracts the relevant portions from BaseName to extract
|
|
the components of the user's string.
|
|
|
|
|
|
Arguments:
|
|
|
|
BaseName - Supplies the base user's path.
|
|
|
|
DriveName - Supplies a string to hold the drive specifier.
|
|
|
|
DriveLetter - Returns the drive letter. 0 for none, 'A'-'Z' for
|
|
disk drives, '1'-'9' for LPT connections.
|
|
|
|
ServerName - Supplies a string to hold the remote server name.
|
|
|
|
VolumeName - Supplies a string to hold the volume name.
|
|
|
|
PathName - Supplies a string to hold the remaining part of the path.
|
|
|
|
FileName - Supplies a string to hold the final component of the path.
|
|
|
|
FullName - Supplies a string to put the Path followed by FileName
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of operation
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
UNICODE_STRING BaseCopy = *BaseName;
|
|
UNICODE_STRING ShareName;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString( DriveName, NULL);
|
|
RtlInitUnicodeString( ServerName, NULL);
|
|
RtlInitUnicodeString( VolumeName, NULL);
|
|
RtlInitUnicodeString( PathName, NULL);
|
|
RtlInitUnicodeString( FileName, NULL);
|
|
*DriveLetter = 0;
|
|
|
|
if (ARGUMENT_PRESENT(FullName)) {
|
|
RtlInitUnicodeString( FullName, NULL);
|
|
}
|
|
|
|
//
|
|
// If the name is "\", or empty, there is nothing to do.
|
|
//
|
|
|
|
if ( BaseName->Length <= sizeof( WCHAR ) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
|
|
|
|
//
|
|
// Skip over the server name.
|
|
//
|
|
|
|
BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
|
|
BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
|
|
BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
|
|
|
|
if ((ServerName->Length == sizeof(L"X:") - sizeof(WCHAR) ) &&
|
|
(ServerName->Buffer[(ServerName->Length / sizeof(WCHAR)) - 1] == L':'))
|
|
{
|
|
|
|
//
|
|
// The file name is of the form x:\server\volume\foo\bar
|
|
//
|
|
|
|
*DriveName = *ServerName;
|
|
*DriveLetter = DriveName->Buffer[0];
|
|
|
|
RtlInitUnicodeString( ServerName, NULL );
|
|
ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
|
|
|
|
if ( ServerName->Length != 0 ) {
|
|
|
|
//
|
|
// Skip over the server name.
|
|
//
|
|
|
|
BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
|
|
BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
|
|
BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
|
|
}
|
|
}
|
|
else if ( ( ServerName->Length == sizeof(L"LPTx") - sizeof(WCHAR) ) &&
|
|
( _wcsnicmp( ServerName->Buffer, L"LPT", 3 ) == 0) &&
|
|
( ServerName->Buffer[3] >= '0' && ServerName->Buffer[3] <= '9' ) )
|
|
{
|
|
|
|
//
|
|
// The file name is of the form LPTx\server\printq
|
|
//
|
|
|
|
*DriveName = *ServerName;
|
|
*DriveLetter = DriveName->Buffer[3];
|
|
|
|
RtlInitUnicodeString( ServerName, NULL );
|
|
ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
|
|
|
|
if ( ServerName->Length != 0 ) {
|
|
|
|
//
|
|
// Skip over the server name.
|
|
//
|
|
|
|
BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
|
|
BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
|
|
BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
if ( ServerName->Length != 0 ) {
|
|
|
|
//
|
|
// The file name is of the form \\server\volume\foo\bar
|
|
// Set volume name to server\volume.
|
|
//
|
|
|
|
ExtractNextComponentName( &ShareName, &BaseCopy, TRUE );
|
|
|
|
//
|
|
// Set volume name = \drive:\server\share or \server\share if the
|
|
// path is UNC.
|
|
//
|
|
|
|
VolumeName->Buffer = ServerName->Buffer - 1;
|
|
|
|
if ( ShareName.Length != 0 ) {
|
|
|
|
VolumeName->Length = ServerName->Length + ShareName.Length + 2 * sizeof( WCHAR );
|
|
|
|
if ( DriveName->Buffer != NULL ) {
|
|
VolumeName->Buffer = DriveName->Buffer - 1;
|
|
VolumeName->Length += DriveName->Length + sizeof(WCHAR);
|
|
}
|
|
|
|
BaseCopy.Buffer += ShareName.Length / sizeof(WCHAR) + 1;
|
|
BaseCopy.Length -= ShareName.Length + sizeof(WCHAR);
|
|
BaseCopy.MaximumLength -= ShareName.MaximumLength + sizeof(WCHAR);
|
|
|
|
} else {
|
|
|
|
VolumeName->Length = ServerName->Length + sizeof( WCHAR );
|
|
return( STATUS_SUCCESS );
|
|
|
|
}
|
|
|
|
VolumeName->MaximumLength = VolumeName->Length;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// server name is empty. this should only happen if we are
|
|
// opening the redirector itself. if there is volume or other
|
|
// components left, fail it.
|
|
//
|
|
|
|
if (BaseCopy.Length > sizeof(WCHAR))
|
|
{
|
|
return STATUS_BAD_NETWORK_PATH ;
|
|
}
|
|
}
|
|
|
|
Status = ExtractPathAndFileName ( &BaseCopy, PathName, FileName );
|
|
|
|
if (NT_SUCCESS(Status) &&
|
|
ARGUMENT_PRESENT(FullName)) {
|
|
|
|
//
|
|
// Use the feature that PathName and FileName are in the same buffer
|
|
// to return <pathname>\<filename>
|
|
//
|
|
|
|
if ( PathName->Buffer == NULL ) {
|
|
|
|
// return just <filename> or NULL
|
|
|
|
*FullName = *FileName;
|
|
|
|
} else {
|
|
// Set FullFileName to <PathName>'\'<FileName>
|
|
|
|
FullName->Buffer = PathName->Buffer;
|
|
|
|
FullName->Length = PathName->Length +
|
|
FileName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
FullName->MaximumLength = PathName->MaximumLength +
|
|
FileName->MaximumLength +
|
|
sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS
|
|
GetServerByAddress(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
OUT PSCB *Scb,
|
|
IN IPXaddress *pServerAddress
|
|
)
|
|
/*+++
|
|
|
|
Description:
|
|
|
|
This routine looks up a server by address. If it finds a server that
|
|
has been connected, it returns it referenced. Otherwise, it returns no
|
|
server.
|
|
|
|
---*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
KIRQL OldIrql;
|
|
PNONPAGED_SCB pFirstNpScb, pNextNpScb;
|
|
PNONPAGED_SCB pFoundNpScb = NULL;
|
|
UNICODE_STRING CredentialName;
|
|
|
|
//
|
|
// Start at the head of the SCB list.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
if ( ScbQueue.Flink == &ScbQueue ) {
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ScbQueueEntry = ScbQueue.Flink;
|
|
pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
|
|
NONPAGED_SCB,
|
|
ScbLinks );
|
|
pNextNpScb = pFirstNpScb;
|
|
|
|
//
|
|
// Leave the first SCB referenced since we need it to
|
|
// be there for when we walk all the way around the list.
|
|
//
|
|
|
|
NwReferenceScb( pFirstNpScb );
|
|
NwReferenceScb( pNextNpScb );
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql);
|
|
|
|
while ( TRUE ) {
|
|
|
|
//
|
|
// Check to see if the SCB address matches the address we have
|
|
// and if the user uid matches the uid for this request. Skip
|
|
// matches that are abandoned anonymous creates.
|
|
//
|
|
|
|
if ( pNextNpScb->pScb ) {
|
|
|
|
if ( ( RtlCompareMemory( (BYTE *) pServerAddress,
|
|
(BYTE *) &pNextNpScb->ServerAddress,
|
|
IPX_HOST_ADDR_LEN ) == IPX_HOST_ADDR_LEN ) &&
|
|
( pIrpContext->Specific.Create.UserUid.QuadPart ==
|
|
pNextNpScb->pScb->UserUid.QuadPart ) &&
|
|
( pNextNpScb->State != SCB_STATE_FLAG_SHUTDOWN ) &&
|
|
( !IS_ANONYMOUS_SCB( pNextNpScb->pScb ) ) ) {
|
|
|
|
if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
|
|
|
|
//
|
|
// If this isn't an ex-create server, you can't use
|
|
// it for this operation.
|
|
//
|
|
|
|
if ( !IsCredentialName( &(pNextNpScb->ServerName) ) ) {
|
|
goto ContinueLoop;
|
|
}
|
|
|
|
//
|
|
// On a credential create, the credential supplied has
|
|
// to match the extended credential for the server.
|
|
//
|
|
|
|
Status = GetCredentialFromServerName( &pNextNpScb->ServerName,
|
|
&CredentialName );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ContinueLoop;
|
|
}
|
|
|
|
if ( RtlCompareUnicodeString( &CredentialName,
|
|
pIrpContext->Specific.Create.puCredentialName,
|
|
TRUE ) ) {
|
|
goto ContinueLoop;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this is an ex-create server, you can't use it for
|
|
// this operation.
|
|
//
|
|
|
|
if ( IsCredentialName( &(pNextNpScb->ServerName) ) ) {
|
|
goto ContinueLoop;
|
|
}
|
|
}
|
|
|
|
pFoundNpScb = pNextNpScb;
|
|
DebugTrace( 0, Dbg, "GetServerByAddress: %wZ\n", &pFoundNpScb->ServerName );
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
ContinueLoop:
|
|
|
|
//
|
|
// Otherwise, get the next one in the list. Don't
|
|
// forget to skip the list head.
|
|
//
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
|
|
|
|
if ( ScbQueueEntry == &ScbQueue ) {
|
|
ScbQueueEntry = ScbQueue.Flink;
|
|
}
|
|
|
|
NwDereferenceScb( pNextNpScb );
|
|
pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
|
|
if ( pNextNpScb == pFirstNpScb ) {
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Otherwise, reference this SCB and continue.
|
|
//
|
|
|
|
NwReferenceScb( pNextNpScb );
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
}
|
|
|
|
NwDereferenceScb( pFirstNpScb );
|
|
|
|
if ( pFoundNpScb ) {
|
|
*Scb = pFoundNpScb->pScb;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CheckScbSecurity(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PSCB pScb,
|
|
IN PUNICODE_STRING puUserName,
|
|
IN PUNICODE_STRING puPassword,
|
|
IN BOOLEAN fDeferLogon
|
|
)
|
|
/*+++
|
|
|
|
You must be at the head of the queue to call this function.
|
|
This function makes sure that the Scb is valid for the user
|
|
that requested it.
|
|
|
|
---*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
BOOLEAN SecurityConflict = FALSE;
|
|
|
|
ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
|
|
|
|
//
|
|
// If there's no user name or password, there's no conflict.
|
|
//
|
|
|
|
if ( ( puUserName == NULL ) &&
|
|
( puPassword == NULL ) ) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ( pScb->UserName.Length &&
|
|
pScb->UserName.Buffer ) {
|
|
|
|
//
|
|
// Do a bindery security check if we were bindery
|
|
// authenticated to this server.
|
|
//
|
|
|
|
if ( !fDeferLogon &&
|
|
puUserName != NULL &&
|
|
puUserName->Buffer != NULL ) {
|
|
|
|
ASSERT( pScb->Password.Buffer != NULL );
|
|
|
|
if ( !RtlEqualUnicodeString( &pScb->UserName, puUserName, TRUE ) ||
|
|
( puPassword &&
|
|
puPassword->Buffer &&
|
|
puPassword->Length &&
|
|
!RtlEqualUnicodeString( &pScb->Password, puPassword, TRUE ) )) {
|
|
|
|
SecurityConflict = TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Do an nds security check.
|
|
//
|
|
|
|
Status = NdsCheckCredentials( pIrpContext,
|
|
puUserName,
|
|
puPassword );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
SecurityConflict = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there was a security conflict, see if we can just
|
|
// take this connection over (i.e. there are no open
|
|
// files or open handles to the server).
|
|
//
|
|
|
|
if ( SecurityConflict ) {
|
|
|
|
if ( ( pScb->OpenFileCount == 0 ) &&
|
|
( pScb->IcbCount == 0 ) ) {
|
|
|
|
if ( pScb->UserName.Buffer ) {
|
|
FREE_POOL( pScb->UserName.Buffer );
|
|
}
|
|
|
|
RtlInitUnicodeString( &pScb->UserName, NULL );
|
|
RtlInitUnicodeString( &pScb->Password, NULL );
|
|
pScb->pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
|
|
|
|
} else {
|
|
|
|
DebugTrace( 0, Dbg, "SCB security conflict.\n", 0 );
|
|
return STATUS_NETWORK_CREDENTIAL_CONFLICT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "SCB security check succeeded.\n", 0 );
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
GetScb(
|
|
OUT PSCB *Scb,
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING Server,
|
|
IN IPXaddress *pServerAddress,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password,
|
|
IN BOOLEAN DeferLogon,
|
|
OUT PBOOLEAN Existing
|
|
)
|
|
/*+++
|
|
|
|
Description:
|
|
|
|
This routine locates an existing SCB or creates a new SCB.
|
|
This is the first half of the original CreateScb routine.
|
|
|
|
Locks:
|
|
|
|
See the anonymous create information in CreateScb().
|
|
|
|
---*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PSCB pScb = NULL;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
BOOLEAN ExistingScb = TRUE;
|
|
UNICODE_STRING UidServer;
|
|
UNICODE_STRING ExCredName;
|
|
PUNICODE_STRING puConnectName;
|
|
KIRQL OldIrql;
|
|
|
|
DebugTrace( 0, Dbg, "GetScb... %wZ\n", Server );
|
|
|
|
if ( pServerAddress != NULL ) {
|
|
DebugTrace( 0, Dbg, " ->Server Address = (provided)\n", 0 );
|
|
} else {
|
|
DebugTrace( 0, Dbg, " ->Server Address = NULL\n", 0 );
|
|
}
|
|
|
|
RtlInitUnicodeString( &UidServer, NULL );
|
|
|
|
if ( ( Server == NULL ) ||
|
|
( Server->Length == 0 ) ) {
|
|
|
|
//
|
|
// No server name was provided. Either this is a connect by address,
|
|
// or a connect to a nearby bindery server (defaulting to the preferred
|
|
// server).
|
|
//
|
|
|
|
if ( pServerAddress == NULL ) {
|
|
|
|
//
|
|
// No server address was provided, so this is an attempt to open
|
|
// a nearby bindery server.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// The loop checks that after we get to the front, the SCB
|
|
// is still in the state we wanted. If not, we need to
|
|
// reselect another.
|
|
//
|
|
|
|
pNpScb = SelectConnection( NULL );
|
|
|
|
//
|
|
// Note: We'd like to call SelectConnection with the pNpScb
|
|
// that we last tried, but if the scavenger runs before
|
|
// this loop gets back to the select connection, we could
|
|
// pass a bum pointer to SelectConnection, which is bad.
|
|
//
|
|
|
|
if ( pNpScb != NULL) {
|
|
|
|
pScb = pNpScb->pScb;
|
|
|
|
//
|
|
// Queue ourselves to the SCB, wait to get to the front to
|
|
// protect access to server State.
|
|
//
|
|
|
|
pIrpContext->pNpScb = pNpScb;
|
|
pIrpContext->pScb = pScb;
|
|
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
//
|
|
// These states have to match the conditions of the
|
|
// SelectConnection to prevent an infinite loop.
|
|
//
|
|
|
|
if (!((pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
|
|
(pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
|
|
(pNpScb->State == SCB_STATE_IN_USE ))) {
|
|
|
|
//
|
|
// No good any more as default server, select another.
|
|
//
|
|
|
|
pScb = NULL ;
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
NwDereferenceScb( pNpScb );
|
|
continue ;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// otherwise, we're done
|
|
//
|
|
|
|
break ;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// An address was provided, so we are attempting to do a lookup
|
|
// based on address. The server that we are looking for might
|
|
// exist but not yet have its address recorded, so if we do an
|
|
// anonymous create, we have to check at the end whether or not
|
|
// someone else came in and successfully created while we were
|
|
// looking up the name.
|
|
//
|
|
// We don't have to hold the RCB anymore since colliding creates
|
|
// have to be handled gracefully anyway.
|
|
//
|
|
|
|
Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
PLIST_ENTRY pTemp;
|
|
|
|
//
|
|
// No anonymous creates are allowed if we are not allowed
|
|
// to send packets to the net (because it's not possible for
|
|
// us to resolve the address to a name).
|
|
//
|
|
|
|
if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
//
|
|
// There's no connection to this server, so we'll
|
|
// have to create one. Let's start with an anonymous
|
|
// Scb.
|
|
//
|
|
|
|
Status = NwAllocateAndInitScb( pIrpContext,
|
|
NULL,
|
|
NULL,
|
|
&pScb );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// We've made the anonymous create, so put it on the scb
|
|
// list and get to the head of the queue.
|
|
//
|
|
|
|
SetFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
|
|
|
|
pIrpContext->pScb = pScb;
|
|
pIrpContext->pNpScb = pScb->pNpScb;
|
|
|
|
ExInterlockedInsertHeadList( &pScb->pNpScb->Requests,
|
|
&pIrpContext->NextRequest,
|
|
&pScb->pNpScb->NpScbSpinLock );
|
|
|
|
pTemp = &pScb->pNpScb->ScbLinks;
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
InsertTailList(&ScbQueue, pTemp);
|
|
KeReleaseSpinLock(&ScbSpinLock, OldIrql);
|
|
|
|
DebugTrace( 0, Dbg, "GetScb started an anonymous create.\n", 0 );
|
|
ExistingScb = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get to the head of the queue and see if this was
|
|
// an abandoned anonymous create. If so, get the
|
|
// right server and continue.
|
|
//
|
|
|
|
pIrpContext->pScb = pScb;
|
|
pIrpContext->pNpScb = pScb->pNpScb;
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
if ( pScb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
|
|
|
|
//
|
|
// The create abandoned this scb, redoing a
|
|
// GetServerByAddress() is guaranteed to get
|
|
// us a good server if there is a server out
|
|
// there.
|
|
//
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
NwDereferenceScb( pScb->pNpScb );
|
|
|
|
Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
ASSERT( pScb != NULL );
|
|
ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
|
|
}
|
|
}
|
|
|
|
ASSERT( pScb != NULL );
|
|
pNpScb = pScb->pNpScb;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// A server name was provided, so we are doing a straight
|
|
// lookup or create by name. Do we need to munge the name
|
|
// for a supplemental credential connect?
|
|
//
|
|
|
|
RtlInitUnicodeString( &ExCredName, NULL );
|
|
|
|
if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
|
|
( !IsCredentialName( Server ) ) ) {
|
|
|
|
Status = BuildExCredentialServerName( Server,
|
|
pIrpContext->Specific.Create.puCredentialName,
|
|
&ExCredName );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
puConnectName = &ExCredName;
|
|
|
|
} else {
|
|
|
|
puConnectName = Server;
|
|
}
|
|
|
|
Status = MakeUidServer( &UidServer,
|
|
&pIrpContext->Specific.Create.UserUid,
|
|
puConnectName );
|
|
|
|
|
|
if ( ExCredName.Buffer ) {
|
|
FREE_POOL( ExCredName.Buffer );
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
|
|
|
|
ExistingScb = NwFindScb( &pScb, pIrpContext, &UidServer, Server );
|
|
|
|
ASSERT( pScb != NULL );
|
|
pNpScb = pScb->pNpScb;
|
|
|
|
pIrpContext->pNpScb = pNpScb;
|
|
pIrpContext->pScb = pScb;
|
|
NwAppendToQueueAndWait(pIrpContext);
|
|
|
|
}
|
|
|
|
//
|
|
// 1) We may or may not have a server (evidenced by pScb).
|
|
//
|
|
// 2) If we have a server and ExistingScb is TRUE, we have
|
|
// an existing server, possibly already connected.
|
|
// Otherwise, we have a newly created server that
|
|
// may or may not be anonymous.
|
|
//
|
|
// 3) If we are logged into this server make sure the supplied
|
|
// username and password, match the username and password
|
|
// that we logged in with.
|
|
//
|
|
|
|
if ( ( pScb ) && ( ExistingScb ) ) {
|
|
|
|
if ( pNpScb->State == SCB_STATE_IN_USE ) {
|
|
|
|
Status = CheckScbSecurity( pIrpContext,
|
|
pScb,
|
|
UserName,
|
|
Password,
|
|
DeferLogon );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
if ( UidServer.Buffer != NULL ) {
|
|
FREE_POOL( UidServer.Buffer );
|
|
}
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
NwDereferenceScb( pNpScb );
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
*Scb = pScb;
|
|
*Existing = ExistingScb;
|
|
|
|
#ifdef NWDBG
|
|
|
|
if ( pScb != NULL ) {
|
|
|
|
//
|
|
// If we have a server, the SCB is referenced and we will
|
|
// be at the head of the queue.
|
|
//
|
|
|
|
ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if ( UidServer.Buffer != NULL ) {
|
|
FREE_POOL( UidServer.Buffer );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "GetScb returned %08lx\n", pScb );
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConnectScb(
|
|
IN PSCB *Scb,
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING Server,
|
|
IN IPXaddress *pServerAddress,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password,
|
|
IN BOOLEAN DeferLogon,
|
|
IN BOOLEAN DeleteConnection,
|
|
IN BOOLEAN ExistingScb
|
|
)
|
|
/*+++
|
|
|
|
Description:
|
|
|
|
This routine puts the provided scb in the connected state.
|
|
This is the second half of the original CreateScb routine.
|
|
|
|
Arguments:
|
|
|
|
Scb - The scb for the server we want to connect.
|
|
pIrpContext - The context for this request.
|
|
Server - The name of the server, or NULL.
|
|
pServerAddress - The address of the server, or NULL,
|
|
UserName - The name of the user to connect as, or NULL.
|
|
Password - The password for the user, or NULL.
|
|
DeferLogon - Should we defer the logon?
|
|
DeleteConnection - Should we succeed even without the net so that
|
|
the delete request will succeed?
|
|
ExistingScb - Is this an existing SCB?
|
|
|
|
If the SCB is anonymous, we need to safely check for colliding
|
|
creates when we find out who the server is.
|
|
|
|
If this is a reconnect attempt, this routine will not dequeue the
|
|
irp context, which could cause a deadlock in the reconnect logic.
|
|
|
|
---*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PSCB pScb = *Scb;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
|
|
BOOLEAN AnonymousScb = FALSE;
|
|
PSCB pCollisionScb = NULL;
|
|
|
|
NTSTATUS LoginStatus;
|
|
BOOLEAN TriedNdsLogin;
|
|
|
|
PLOGON pLogon;
|
|
BOOLEAN DeferredLogon = DeferLogon;
|
|
PNDS_SECURITY_CONTEXT pNdsContext;
|
|
NTSTATUS CredStatus;
|
|
|
|
DebugTrace( 0, Dbg, "ConnectScb... %08lx\n", pScb );
|
|
|
|
//
|
|
// If we already have an SCB, find out where in the
|
|
// connect chain we need to start off.
|
|
//
|
|
|
|
if ( pScb ) {
|
|
|
|
pNpScb = pScb->pNpScb;
|
|
AnonymousScb = IS_ANONYMOUS_SCB( pScb );
|
|
|
|
if ( ExistingScb ) {
|
|
|
|
ASSERT( !AnonymousScb );
|
|
|
|
//
|
|
// If this SCB is in STATE_ATTACHING, we need to check
|
|
// the address in the SCB to make sure that it was at one
|
|
// point a valid server. If it wasn't, then we shouldn't
|
|
// honor this create because it's probably a tree create.
|
|
//
|
|
|
|
if ( DeleteConnection ) {
|
|
|
|
ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) );
|
|
|
|
if ( ( pNpScb->State == SCB_STATE_ATTACHING ) &&
|
|
( (pNpScb->ServerAddress).Socket == 0 ) ) {
|
|
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
goto CleanupAndExit;
|
|
|
|
} else {
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
RedoConnect:
|
|
|
|
if ( pNpScb->State == SCB_STATE_ATTACHING ) {
|
|
goto GetAddress;
|
|
} else if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
|
|
goto Connect;
|
|
} else if ( pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
|
|
goto Login;
|
|
} else if ( pNpScb->State == SCB_STATE_IN_USE ) {
|
|
goto InUse;
|
|
} else {
|
|
|
|
DebugTrace( 0, Dbg, "ConnectScb: Unknown Scb State %08lx\n", pNpScb->State );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a new SCB, we have to run through the whole routine.
|
|
//
|
|
|
|
pNpScb->State = SCB_STATE_ATTACHING;
|
|
}
|
|
|
|
}
|
|
|
|
GetAddress:
|
|
|
|
//
|
|
// Set the reroute attempted bit so that we don't try
|
|
// to reconnect during the connect.
|
|
//
|
|
|
|
SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
|
|
|
|
if ( !pServerAddress ) {
|
|
|
|
//
|
|
// If we don't have an address, this SCB cannot be anonymous!!
|
|
//
|
|
|
|
ASSERT( !AnonymousScb );
|
|
|
|
//
|
|
// We have to cast an exception frame for this legacy routine
|
|
// that still uses structured exceptions.
|
|
//
|
|
|
|
try {
|
|
|
|
pNpScb = FindServer( pIrpContext, pNpScb, Server );
|
|
|
|
ASSERT( pNpScb != NULL );
|
|
|
|
//
|
|
// This is redundant unless the starting server was NULL.
|
|
// FindServer returns the same SCB we provided to it
|
|
// unless we called it with NULL.
|
|
//
|
|
|
|
pScb = pNpScb->pScb;
|
|
pIrpContext->pNpScb = pNpScb;
|
|
pIrpContext->pScb = pScb;
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
} except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Status = GetExceptionCode();
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Build the address into the NpScb since we already know it.
|
|
//
|
|
|
|
RtlCopyMemory( &pNpScb->ServerAddress,
|
|
pServerAddress,
|
|
sizeof( TDI_ADDRESS_IPX ) );
|
|
|
|
if ( pNpScb->ServerAddress.Socket != NCP_SOCKET ) {
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "CreateScb supplied server socket is deviant.\n", 0 );
|
|
}
|
|
|
|
BuildIpxAddress( pNpScb->ServerAddress.Net,
|
|
pNpScb->ServerAddress.Node,
|
|
pNpScb->ServerAddress.Socket,
|
|
&pNpScb->RemoteAddress );
|
|
|
|
pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
|
|
}
|
|
|
|
Connect:
|
|
|
|
//
|
|
// FindServer may have connected us to the server already,
|
|
// so we may be able to skip the reconnect here.
|
|
//
|
|
|
|
if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
|
|
|
|
//
|
|
// If this is an anonymous scb, we have to be prepared
|
|
// for ConnectToServer() to find that we've already connected
|
|
// this server by name. In this case, we cancel the
|
|
// anonymous create and use the server that was created
|
|
// while we were looking up the name.
|
|
//
|
|
|
|
Status = ConnectToServer( pIrpContext, &pCollisionScb );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
//
|
|
// We succeeded. If there's a collision scb, then we need to
|
|
// abandon the anonymous scb and use the scb that we collided
|
|
// with. Otherwise, we successfully completed an anonymous
|
|
// connect and can go on with the create normally.
|
|
//
|
|
|
|
if ( pCollisionScb ) {
|
|
|
|
ASSERT( AnonymousScb );
|
|
|
|
//
|
|
// Deref and dequeue from the abandoned server.
|
|
//
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
NwDereferenceScb( pIrpContext->pNpScb );
|
|
|
|
//
|
|
// Queue to the appropriate server.
|
|
//
|
|
|
|
pIrpContext->pScb = pCollisionScb;
|
|
pIrpContext->pNpScb = pCollisionScb->pNpScb;
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
pScb = pCollisionScb;
|
|
pNpScb = pCollisionScb->pNpScb;
|
|
*Scb = pCollisionScb;
|
|
|
|
//
|
|
// Re-start connecting the scb.
|
|
//
|
|
|
|
AnonymousScb = FALSE;
|
|
ExistingScb = TRUE;
|
|
|
|
pCollisionScb = NULL;
|
|
|
|
DebugTrace( 0, Dbg, "Re-doing connect on anonymous collision.\n", 0 );
|
|
goto RedoConnect;
|
|
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " Logout from server - just in case\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"F",
|
|
NCP_LOGOUT );
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " Connect to real server = %X\n", Status);
|
|
|
|
pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
|
|
}
|
|
|
|
Login:
|
|
|
|
//
|
|
// If we have credentials for the tree and this server was named
|
|
// explicitly, we shouldn't defer the login or else the browse
|
|
// view of the tree may be wrong. For this reason, NdsServerAuthenticate
|
|
// has to be a straight shot call and can't remove us from the head
|
|
// of the queue.
|
|
//
|
|
|
|
if ( ( ( Server != NULL ) || ( pServerAddress != NULL ) ) &&
|
|
( DeferredLogon ) &&
|
|
( pScb->MajorVersion > 3 ) &&
|
|
( pScb->UserName.Length == 0 ) ) {
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &pScb->UserUid, FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
if ( pLogon ) {
|
|
|
|
CredStatus = NdsLookupCredentials( pIrpContext,
|
|
&pScb->NdsTreeName,
|
|
pLogon,
|
|
&pNdsContext,
|
|
CREDENTIAL_READ,
|
|
FALSE );
|
|
|
|
if ( NT_SUCCESS( CredStatus ) ) {
|
|
|
|
if ( ( pNdsContext->Credential != NULL ) &&
|
|
( pNdsContext->CredentialLocked == FALSE ) ) {
|
|
|
|
DebugTrace( 0, Dbg, "Forcing authentication to %wZ.\n",
|
|
&pScb->UidServerName );
|
|
DeferredLogon = FALSE;
|
|
}
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pNpScb->State == SCB_STATE_LOGIN_REQUIRED && !DeferredLogon ) {
|
|
|
|
//
|
|
// NOTE: DoBinderyLogon() and DoNdsLogon() may return a
|
|
// warning status. If they do, we must return the
|
|
// warning status to the caller.
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
TriedNdsLogin = FALSE;
|
|
|
|
//
|
|
// We force a bindery login for a non 4.x server. Otherwise, we
|
|
// allow a fall-back from NDS style authentication to bindery style
|
|
// authentication.
|
|
//
|
|
|
|
if ( pScb->MajorVersion >= 4 ) {
|
|
|
|
ASSERT( pScb->NdsTreeName.Length != 0 );
|
|
|
|
Status = DoNdsLogon( pIrpContext, UserName, Password );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Do we need to re-license the connection?
|
|
//
|
|
|
|
if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
|
|
|
|
Status = NdsLicenseConnection( pIrpContext );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
Status = STATUS_REMOTE_SESSION_LIMIT;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
TriedNdsLogin = TRUE;
|
|
LoginStatus = Status;
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
Status = DoBinderyLogon( pIrpContext, UserName, Password );
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
if ( TriedNdsLogin ) {
|
|
|
|
//
|
|
// Both login attempts have failed. We usually prefer
|
|
// the NDS status, but not always.
|
|
//
|
|
|
|
if ( ( Status != STATUS_WRONG_PASSWORD ) &&
|
|
( Status != STATUS_ACCOUNT_DISABLED ) ) {
|
|
Status = LoginStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Couldn't log on, be good boys and disconnect.
|
|
//
|
|
|
|
ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"D-" ); // Disconnect
|
|
|
|
Stats.Sessions--;
|
|
|
|
if ( pScb->MajorVersion == 2 ) {
|
|
Stats.NW2xConnects--;
|
|
} else if ( pScb->MajorVersion == 3 ) {
|
|
Stats.NW3xConnects--;
|
|
} else if ( pScb->MajorVersion >= 4 ) {
|
|
Stats.NW4xConnects--;
|
|
}
|
|
|
|
//
|
|
// Demote this scb to reconnect required and exit.
|
|
//
|
|
|
|
pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
goto CleanupAndExit;
|
|
}
|
|
|
|
pNpScb->State = SCB_STATE_IN_USE;
|
|
}
|
|
|
|
//
|
|
// We have to be at the head of the queue to do the reconnect.
|
|
//
|
|
|
|
if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
|
|
ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
|
|
} else {
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
}
|
|
|
|
ReconnectScb( pIrpContext, pScb );
|
|
|
|
InUse:
|
|
|
|
//
|
|
// Ok, we've completed the connect routine. Return this good server.
|
|
//
|
|
|
|
*Scb = pScb;
|
|
|
|
CleanupAndExit:
|
|
|
|
//
|
|
// The reconnect path must not do anything to remove the irp context from
|
|
// the head of the queue since it also owns the irp context in the second
|
|
// position on the queue and that irp context is running.
|
|
//
|
|
|
|
if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
}
|
|
|
|
DebugTrace( 0, Dbg, "ConnectScb: Connected %08lx\n", pScb );
|
|
DebugTrace( 0, Dbg, "ConnectScb: Status was %08lx\n", Status );
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CreateScb(
|
|
OUT PSCB *Scb,
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING Server,
|
|
IN IPXaddress *pServerAddress,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password,
|
|
IN BOOLEAN DeferLogon,
|
|
IN BOOLEAN DeleteConnection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine connects to the requested server.
|
|
|
|
The following mix of parameters are valid:
|
|
|
|
Server Name, No Net Address - The routine will look up
|
|
up the SCB or create a new one if necessary, getting
|
|
the server address from a nearby bindery.
|
|
|
|
No Server Name, Valid Net Address - The routine will
|
|
look up the SCB by address or create a new one if
|
|
necessary. The name of the server will be set in
|
|
the SCB upon return.
|
|
|
|
Server Name, Valid Net Address - The routine will look
|
|
up the SCB by name or will create a new one if
|
|
necessary. The supplied server address will be used,
|
|
sparing a bindery query.
|
|
|
|
No Server Name, No Net Address - A connection to the
|
|
preferred server or a nearby server will be returned.
|
|
|
|
Arguments:
|
|
|
|
Scb - The pointer to the scb in question.
|
|
pIrpContext - The information for this request.
|
|
Server - The name of the server, or NULL.
|
|
pServerAddress - The address of the server, or NULL.
|
|
UserName - The username for the connect, or NULL.
|
|
Password - The password for the connect, or NULL.
|
|
DeferLogon - Should we defer the logon until later?
|
|
DeleteConnection - Should we allow this even when there's no
|
|
net response so that the connection can
|
|
be deleted?
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of operation. If the return status is STATUS_SUCCESS,
|
|
then Scb must point to a valid Scb. The irp context pointers will also
|
|
be set, but the irp context will not be on the scb queue.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
PSCB pScb = NULL;
|
|
PNONPAGED_SCB pOriginalNpScb = pIrpContext->pNpScb;
|
|
PSCB pOriginalScb = pIrpContext->pScb;
|
|
BOOLEAN ExistingScb = FALSE;
|
|
BOOLEAN AnonymousScb = FALSE;
|
|
PLOGON pLogon;
|
|
PNDS_SECURITY_CONTEXT pNdsContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "CreateScb....\n", 0);
|
|
|
|
//
|
|
// Do not allow any SCB opens unless the redirector is running
|
|
// unless they are no connect creates and we are waiting to bind.
|
|
//
|
|
|
|
if ( NwRcb.State != RCB_STATE_RUNNING ) {
|
|
|
|
if ( ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ||
|
|
( NwRcb.State != RCB_STATE_NEED_BIND ) ) ) {
|
|
|
|
*Scb = NULL;
|
|
DebugTrace( -1, Dbg, "CreateScb -> %08lx\n", STATUS_REDIRECTOR_NOT_STARTED );
|
|
return STATUS_REDIRECTOR_NOT_STARTED;
|
|
}
|
|
}
|
|
|
|
if ( UserName != NULL ) {
|
|
DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName );
|
|
} else {
|
|
DebugTrace( 0, Dbg, " ->UserName = NULL\n", 0 );
|
|
}
|
|
|
|
if ( Password != NULL ) {
|
|
DebugTrace( 0, Dbg, " ->Password = %wZ\n", Password );
|
|
} else {
|
|
DebugTrace( 0, Dbg, " ->Password = NULL\n", 0 );
|
|
}
|
|
|
|
//
|
|
// Get the SCB for this server.
|
|
//
|
|
|
|
Status = GetScb( &pScb,
|
|
pIrpContext,
|
|
Server,
|
|
pServerAddress,
|
|
UserName,
|
|
Password,
|
|
DeferLogon,
|
|
&ExistingScb );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
*Scb = NULL;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// At this point, we may or may not have an SCB.
|
|
//
|
|
// If we have an SCB, we know:
|
|
//
|
|
// 1. The scb is referenced.
|
|
// 2. We are at the head of the queue.
|
|
//
|
|
// IMPORTANT POINT: The SCB may be anonymous. If it is,
|
|
// we do not hold the RCB, but rather we have to re-check
|
|
// whether or not the server has shown up via a different
|
|
// create when we find out who the anonymous server is.
|
|
// We do this because there is a window where we have a
|
|
// servers name but not its address and so our lookup by
|
|
// address might be inaccurate.
|
|
//
|
|
|
|
if ( ( pScb ) && IS_ANONYMOUS_SCB( pScb ) ) {
|
|
AnonymousScb = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we have a fully connected SCB, we need to go no further.
|
|
//
|
|
|
|
if ( ( pScb ) && ( pScb->pNpScb->State == SCB_STATE_IN_USE ) ) {
|
|
|
|
ASSERT( !AnonymousScb );
|
|
|
|
if ( ( pScb->MajorVersion >= 4 ) &&
|
|
( pScb->UserName.Buffer == NULL ) ) {
|
|
|
|
//
|
|
// This is an NDS authenticated server and we have
|
|
// to make sure the credentials aren't locked for
|
|
// logout.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &pScb->UserUid, FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
if ( pLogon ) {
|
|
|
|
Status = NdsLookupCredentials( pIrpContext,
|
|
&pScb->NdsTreeName,
|
|
pLogon,
|
|
&pNdsContext,
|
|
CREDENTIAL_READ,
|
|
FALSE );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
if ( ( pNdsContext->Credential != NULL ) &&
|
|
( pNdsContext->CredentialLocked == TRUE ) ) {
|
|
|
|
DebugTrace( 0, Dbg, "Denying create... we're logging out.\n", 0 );
|
|
Status = STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
}
|
|
}
|
|
}
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
|
|
//
|
|
// We must not change the irp context pointers if we're going
|
|
// to fail this call or we may mess up ref counts and what not.
|
|
//
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
*Scb = pScb;
|
|
|
|
} else {
|
|
|
|
*Scb = NULL;
|
|
NwDereferenceScb( pScb->pNpScb );
|
|
|
|
pIrpContext->pNpScb = pOriginalNpScb;
|
|
pIrpContext->pScb = pOriginalScb;
|
|
|
|
}
|
|
|
|
|
|
DebugTrace( -1, Dbg, "CreateScb: pScb = %08lx\n", pScb );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Run through the connect routines for this scb. The scb may
|
|
// be NULL if we're still looking for a nearby server.
|
|
//
|
|
|
|
Status = ConnectScb( &pScb,
|
|
pIrpContext,
|
|
Server,
|
|
pServerAddress,
|
|
UserName,
|
|
Password,
|
|
DeferLogon,
|
|
DeleteConnection,
|
|
ExistingScb );
|
|
|
|
//
|
|
// If ConnectScb fails, remove the extra ref count so
|
|
// the scavenger will clean it up. Anonymous failures
|
|
// are also cleaned up by the scavenger.
|
|
//
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
if ( pScb ) {
|
|
NwDereferenceScb( pScb->pNpScb );
|
|
}
|
|
|
|
//
|
|
// We must not change the irp context pointers if we're going
|
|
// to fail this call or we may mess up ref counts and what not.
|
|
//
|
|
|
|
pIrpContext->pNpScb = pOriginalNpScb;
|
|
pIrpContext->pScb = pOriginalScb;
|
|
*Scb = NULL;
|
|
|
|
DebugTrace( -1, Dbg, "CreateScb: Status = %08lx\n", Status );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If ConnectScb succeeds, then we must have an scb, the scb must
|
|
// be in the IN_USE state (or LOGIN_REQUIRED if DeferLogon was
|
|
// specified), it must be referenced, and we should not be on the
|
|
// queue.
|
|
//
|
|
|
|
ASSERT( pScb );
|
|
ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
|
|
ASSERT( pIrpContext->pNpScb == pScb->pNpScb );
|
|
ASSERT( pIrpContext->pScb == pScb );
|
|
ASSERT( pScb->pNpScb->Reference > 0 );
|
|
|
|
*Scb = pScb;
|
|
DebugTrace(-1, Dbg, "CreateScb -> pScb = %08lx\n", pScb );
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
return Status;
|
|
}
|
|
#define CTX_Retries 10
|
|
|
|
PNONPAGED_SCB
|
|
FindServer(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb,
|
|
PUNICODE_STRING ServerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to get the network address of a server. If no
|
|
servers are known, it first sends a find nearest SAP.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - A pointer to the request parameters.
|
|
|
|
pNpScb - A pointer to the non paged SCB for the server to get the
|
|
address of.
|
|
|
|
Return Value:
|
|
|
|
NONPAGED_SCB - A pointer the nonpaged SCB. This is the same as the
|
|
input value, unless the input SCB was NULL. Then this is a
|
|
pointer to the nearest server SCB.
|
|
|
|
This routine raises status if it fails to get the server's address.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Attempts;
|
|
BOOLEAN FoundServer = FALSE;
|
|
PNONPAGED_SCB pNearestNpScb = NULL;
|
|
PNONPAGED_SCB pLastNpScb = NULL;
|
|
|
|
BOOLEAN SentFindNearest = FALSE;
|
|
BOOLEAN SentGeneral = FALSE;
|
|
PMDL ReceiveMdl = NULL;
|
|
PUCHAR ReceiveBuffer = NULL;
|
|
IPXaddress ServerAddress;
|
|
|
|
BOOLEAN ConnectedToNearest = FALSE;
|
|
BOOLEAN AllocatedIrpContext = FALSE;
|
|
BOOLEAN LastScbWasValid;
|
|
PIRP_CONTEXT pNewIrpContext;
|
|
int ResponseCount;
|
|
int NewServers;
|
|
ULONG RetryCount = MAX_SAP_RETRIES;
|
|
|
|
static LARGE_INTEGER TimeoutWait = {0,0};
|
|
LARGE_INTEGER Now;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If we had a SAP timeout less than 10 seconds ago, just fail this
|
|
// request immediately. This allows dumb apps to exit a lot faster.
|
|
//
|
|
|
|
KeQuerySystemTime( &Now );
|
|
if ( Now.QuadPart < TimeoutWait.QuadPart ) {
|
|
ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
|
|
}
|
|
|
|
try {
|
|
|
|
if (IsTerminalServer()) {
|
|
//
|
|
// 1/31/97 cjc (Citrix code merge)
|
|
// Fix for Mellon Bank who restricted access based on the
|
|
// preferred server the user logs into. This caused a
|
|
// problem for a user who tries to logon to a server that
|
|
// the previous user doesn't have accesss to. The previous
|
|
// user's server is used to get the address of the current
|
|
// logon user's preferred server but he can't see the new
|
|
// user's server. Modifed this so it loops thru 10 servers
|
|
// rather than just the 1st 2 in the list.
|
|
//
|
|
RetryCount =CTX_Retries;
|
|
}
|
|
for ( Attempts = 0; Attempts < RetryCount && !FoundServer ; Attempts++ ) {
|
|
|
|
//
|
|
// If this SCB is now marked RECONNECT_REQUIRED, then
|
|
// it responded to the find nearest and we can immediately
|
|
// try to connect to it.
|
|
//
|
|
|
|
if ( pNpScb != NULL &&
|
|
pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
|
|
|
|
return pNpScb;
|
|
}
|
|
|
|
//
|
|
// Pick a server to use to find the address of the server that
|
|
// we are really interested in.
|
|
//
|
|
|
|
if (pLastNpScb) {
|
|
|
|
//
|
|
// For some reason we couldn't use pNearestScb. Scan from this
|
|
// server onwards.
|
|
//
|
|
|
|
pNearestNpScb = SelectConnection( pLastNpScb );
|
|
|
|
// Allow pLastNpScb to be deleted.
|
|
|
|
NwDereferenceScb( pLastNpScb );
|
|
|
|
pLastNpScb = NULL;
|
|
|
|
LastScbWasValid = TRUE;
|
|
|
|
} else {
|
|
|
|
pNearestNpScb = SelectConnection( NULL );
|
|
LastScbWasValid = FALSE;
|
|
|
|
}
|
|
|
|
if ( pNearestNpScb == NULL ) {
|
|
|
|
int i;
|
|
|
|
if (LastScbWasValid) {
|
|
ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
|
|
return NULL;
|
|
}
|
|
//
|
|
// If we sent a find nearest, and still don't have a single
|
|
// entry in the server list, it's time to give up.
|
|
//
|
|
|
|
if (( SentFindNearest) &&
|
|
( SentGeneral )) {
|
|
|
|
Error(
|
|
EVENT_NWRDR_NO_SERVER_ON_NETWORK,
|
|
STATUS_OBJECT_NAME_NOT_FOUND,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// We don't have any active servers in the list. Queue our
|
|
// IrpContext to the NwPermanentNpScb. This insures that
|
|
// only one thread in the system in doing a find nearest at
|
|
// any one time.
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, " Nearest Server\n", 0);
|
|
|
|
if ( !AllocatedIrpContext ) {
|
|
AllocatedIrpContext = NwAllocateExtraIrpContext(
|
|
&pNewIrpContext,
|
|
&NwPermanentNpScb );
|
|
|
|
if ( !AllocatedIrpContext ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
}
|
|
|
|
pNewIrpContext->pNpScb = &NwPermanentNpScb;
|
|
|
|
//
|
|
// Allocate an extra buffer large enough for 4
|
|
// find nearest responses, or a general SAP response.
|
|
//
|
|
|
|
pNewIrpContext->Specific.Create.FindNearestResponseCount = 0;
|
|
NewServers = 0;
|
|
|
|
|
|
ReceiveBuffer = ALLOCATE_POOL_EX(
|
|
NonPagedPool,
|
|
MAX_SAP_RESPONSE_SIZE );
|
|
|
|
pNewIrpContext->Specific.Create.FindNearestResponse[0] = ReceiveBuffer;
|
|
|
|
for ( i = 1; i < MAX_SAP_RESPONSES ; i++ ) {
|
|
pNewIrpContext->Specific.Create.FindNearestResponse[i] =
|
|
ReceiveBuffer + i * SAP_RECORD_SIZE;
|
|
}
|
|
|
|
//
|
|
// Get the tick count for this net, so that we know how
|
|
// long to wait for SAP responses.
|
|
//
|
|
|
|
(VOID)GetTickCount( pNewIrpContext, &NwPermanentNpScb.TickCount );
|
|
NwPermanentNpScb.SendTimeout = NwPermanentNpScb.TickCount + 10;
|
|
|
|
if (!SentFindNearest) {
|
|
|
|
//
|
|
// Send a find nearest SAP, and wait for up to several
|
|
// responses. This allows us to handle a busy server
|
|
// that responds quickly to SAPs but will not accept
|
|
// connections.
|
|
//
|
|
|
|
Status = ExchangeWithWait (
|
|
pNewIrpContext,
|
|
ProcessFindNearest,
|
|
"Aww",
|
|
SAP_FIND_NEAREST,
|
|
SAP_SERVICE_TYPE_SERVER );
|
|
|
|
if ( Status == STATUS_NETWORK_UNREACHABLE ) {
|
|
|
|
//
|
|
// IPX is not bound to anything that is currently
|
|
// up (which means it's probably bound only to the
|
|
// RAS WAN wrapper). Don't waste 20 seconds trying
|
|
// to find a server.
|
|
//
|
|
|
|
DebugTrace( 0, Dbg, "Aborting FindNearest. No Net.\n", 0 );
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
ExRaiseStatus( STATUS_NETWORK_UNREACHABLE );
|
|
}
|
|
|
|
//
|
|
// Process the set of find nearest responses.
|
|
//
|
|
|
|
for (i = 0; i < (int)pNewIrpContext->Specific.Create.FindNearestResponseCount; i++ ) {
|
|
if (ProcessFindNearestEntry(
|
|
pNewIrpContext,
|
|
(PSAP_FIND_NEAREST_RESPONSE)pNewIrpContext->Specific.Create.FindNearestResponse[i] )
|
|
) {
|
|
|
|
//
|
|
// We found a server that was previously unknown.
|
|
//
|
|
|
|
NewServers++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (( !NewServers ) &&
|
|
( !SentGeneral)){
|
|
|
|
SentGeneral = TRUE;
|
|
|
|
//
|
|
// Either no SAP responses or can't connect to nearest servers.
|
|
// Try a general SAP.
|
|
//
|
|
|
|
ReceiveMdl = ALLOCATE_MDL(
|
|
ReceiveBuffer,
|
|
MAX_SAP_RESPONSE_SIZE,
|
|
TRUE,
|
|
FALSE,
|
|
NULL );
|
|
|
|
if ( ReceiveMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool( ReceiveMdl );
|
|
pNewIrpContext->RxMdl->Next = ReceiveMdl;
|
|
|
|
Status = ExchangeWithWait (
|
|
pNewIrpContext,
|
|
SynchronousResponseCallback,
|
|
"Aww",
|
|
SAP_GENERAL_REQUEST,
|
|
SAP_SERVICE_TYPE_SERVER );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
DebugTrace( 0, Dbg, "Received %d bytes\n", pNewIrpContext->ResponseLength );
|
|
ResponseCount = ( pNewIrpContext->ResponseLength - 2 ) / SAP_RECORD_SIZE;
|
|
|
|
//
|
|
// Process at most MAX_SAP_RESPONSES servers.
|
|
//
|
|
|
|
if ( ResponseCount > MAX_SAP_RESPONSES ) {
|
|
ResponseCount = MAX_SAP_RESPONSES;
|
|
}
|
|
|
|
for ( i = 0; i < ResponseCount; i++ ) {
|
|
ProcessFindNearestEntry(
|
|
pNewIrpContext,
|
|
(PSAP_FIND_NEAREST_RESPONSE)(pNewIrpContext->rsp + SAP_RECORD_SIZE * i) );
|
|
}
|
|
}
|
|
|
|
pNewIrpContext->RxMdl->Next = NULL;
|
|
FREE_MDL( ReceiveMdl );
|
|
ReceiveMdl = NULL;
|
|
}
|
|
|
|
//
|
|
// We're done with the find nearest. Free the buffer and
|
|
// dequeue from the permanent SCB.
|
|
//
|
|
|
|
FREE_POOL( ReceiveBuffer );
|
|
ReceiveBuffer = NULL;
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
|
|
if ( !NT_SUCCESS( Status ) &&
|
|
pNewIrpContext->Specific.Create.FindNearestResponseCount == 0 ) {
|
|
|
|
//
|
|
// If the SAP timed out, map the error for MPR.
|
|
//
|
|
|
|
if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
//
|
|
// Setup the WaitTimeout, and fail this request.
|
|
//
|
|
|
|
KeQuerySystemTime( &TimeoutWait );
|
|
TimeoutWait.QuadPart += NwOneSecond * 10;
|
|
|
|
ExRaiseStatus( Status );
|
|
return NULL;
|
|
}
|
|
|
|
SentFindNearest = TRUE;
|
|
|
|
} else {
|
|
|
|
if ( !AllocatedIrpContext ) {
|
|
AllocatedIrpContext = NwAllocateExtraIrpContext(
|
|
&pNewIrpContext,
|
|
pNearestNpScb );
|
|
|
|
if ( !AllocatedIrpContext ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Point the IRP context at the nearest server.
|
|
//
|
|
|
|
pNewIrpContext->pNpScb = pNearestNpScb;
|
|
NwAppendToQueueAndWait( pNewIrpContext );
|
|
|
|
if ( pNearestNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
|
|
|
|
//
|
|
// We have no connection to this server, try to
|
|
// connect now. This is not a valid path for an
|
|
// anonymous create, so there's no chance that
|
|
// there will be a name collision.
|
|
//
|
|
|
|
Status = ConnectToServer( pNewIrpContext, NULL );
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Failed to connect to the server. Give up.
|
|
// We'll try another server.
|
|
//
|
|
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
|
|
// Keep pNearestScb referenced
|
|
// so it doesn't disappear.
|
|
|
|
pLastNpScb = pNearestNpScb;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
pNearestNpScb->State = SCB_STATE_LOGIN_REQUIRED;
|
|
ConnectedToNearest = TRUE;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// update the last used time for this SCB.
|
|
//
|
|
|
|
KeQuerySystemTime( &pNearestNpScb->LastUsedTime );
|
|
|
|
if (( pNpScb == NULL ) ||
|
|
( ServerName == NULL )) {
|
|
|
|
//
|
|
// We're looking for any server so use this one.
|
|
//
|
|
// We'll exit the for loop on the SCB queue,
|
|
// and with this SCB referenced.
|
|
//
|
|
|
|
pNpScb = pNearestNpScb;
|
|
Status = STATUS_SUCCESS;
|
|
FoundServer = TRUE;
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
|
|
} else {
|
|
|
|
Status = QueryServersAddress(
|
|
pNewIrpContext,
|
|
pNearestNpScb,
|
|
ServerName,
|
|
&ServerAddress );
|
|
|
|
//
|
|
// If we connect to this server just to query it's
|
|
// bindery, disconnect now.
|
|
//
|
|
|
|
if (IsTerminalServer()) {
|
|
if (ConnectedToNearest) {
|
|
|
|
ExchangeWithWait (
|
|
pNewIrpContext,
|
|
SynchronousResponseCallback,
|
|
"D-" ); // Disconnect
|
|
ConnectedToNearest = FALSE;
|
|
Stats.Sessions--;
|
|
|
|
if ( pNearestNpScb->MajorVersion == 2 ) {
|
|
Stats.NW2xConnects--;
|
|
} else if ( pNearestNpScb->MajorVersion == 3 ) {
|
|
Stats.NW3xConnects--;
|
|
} else if ( pNearestNpScb->MajorVersion == 4 ) {
|
|
Stats.NW4xConnects--;
|
|
}
|
|
pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
|
|
}
|
|
} else {
|
|
|
|
if ( ConnectedToNearest && NT_SUCCESS(Status) ) {
|
|
|
|
ExchangeWithWait (
|
|
pNewIrpContext,
|
|
SynchronousResponseCallback,
|
|
"D-" ); // Disconnect
|
|
|
|
pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
// Point the SCB at the real server address and connect to it,
|
|
// then logout. (We logout for no apparent reason except
|
|
// because this is what a netware redir does.)
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&pNpScb->ServerAddress,
|
|
&ServerAddress,
|
|
sizeof( TDI_ADDRESS_IPX ) );
|
|
|
|
if ( ServerAddress.Socket != NCP_SOCKET ) {
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "FindServer server socket is deviant.\n", 0 );
|
|
}
|
|
|
|
BuildIpxAddress(
|
|
ServerAddress.Net,
|
|
ServerAddress.Node,
|
|
ServerAddress.Socket,
|
|
&pNpScb->RemoteAddress );
|
|
|
|
FoundServer = TRUE;
|
|
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
NwDereferenceScb( pNearestNpScb );
|
|
|
|
pNewIrpContext->pNpScb = pNpScb;
|
|
pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
|
|
} else {
|
|
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
|
|
if ( (Status == STATUS_REMOTE_NOT_LISTENING ) ||
|
|
(Status == STATUS_BAD_NETWORK_PATH)) {
|
|
|
|
//
|
|
// This server is no longer talking to us.
|
|
// Try again. Keep pNearestScb referenced
|
|
// so it doesn't disappear.
|
|
//
|
|
|
|
pLastNpScb = pNearestNpScb;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
NwDereferenceScb( pNearestNpScb );
|
|
|
|
//
|
|
// This nearest server doesn't know about
|
|
// the server we are looking for. Give up
|
|
// and let another rdr try.
|
|
//
|
|
|
|
ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // else
|
|
} // for
|
|
|
|
} finally {
|
|
|
|
if ( ReceiveBuffer != NULL ) {
|
|
FREE_POOL( ReceiveBuffer );
|
|
}
|
|
|
|
if ( ReceiveMdl != NULL ) {
|
|
FREE_MDL( ReceiveMdl );
|
|
}
|
|
|
|
if ( AllocatedIrpContext ) {
|
|
NwDequeueIrpContext( pNewIrpContext, FALSE );
|
|
NwFreeExtraIrpContext( pNewIrpContext );
|
|
}
|
|
|
|
if (IsTerminalServer()) {
|
|
if ( (Attempts == CTX_Retries) && pLastNpScb) {
|
|
NwDereferenceScb( pLastNpScb );
|
|
}
|
|
} else {
|
|
if (pLastNpScb) {
|
|
NwDereferenceScb( pLastNpScb );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( !FoundServer ) {
|
|
ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
|
|
}
|
|
|
|
return pNpScb;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ProcessFindNearest(
|
|
IN struct _IRP_CONTEXT* pIrpContext,
|
|
IN ULONG BytesAvailable,
|
|
IN PUCHAR Response
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the full address of the remote server and builds
|
|
the corresponding TA_IPX_ADDRESS.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ResponseCount;
|
|
KIRQL OldIrql;
|
|
|
|
DebugTrace(+1, Dbg, "ProcessFindNearest...\n", 0);
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
if ( BytesAvailable == 0) {
|
|
|
|
//
|
|
// Timeout.
|
|
//
|
|
|
|
pIrpContext->ResponseParameters.Error = 0;
|
|
pIrpContext->pNpScb->OkToReceive = FALSE;
|
|
|
|
ASSERT( pIrpContext->Event.Header.SignalState == 0 );
|
|
#if NWDBG
|
|
pIrpContext->DebugValue = 0x101;
|
|
#endif
|
|
NwSetIrpContextEvent( pIrpContext );
|
|
DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
return STATUS_REMOTE_NOT_LISTENING;
|
|
}
|
|
|
|
if ( BytesAvailable >= FIND_NEAREST_RESP_SIZE &&
|
|
Response[0] == 0 &&
|
|
Response[1] == SAP_SERVICE_TYPE_SERVER ) {
|
|
|
|
//
|
|
// This is a valid find nearest response. Process the packet.
|
|
//
|
|
|
|
ResponseCount = pIrpContext->Specific.Create.FindNearestResponseCount++;
|
|
ASSERT( ResponseCount < MAX_SAP_RESPONSES );
|
|
|
|
//
|
|
// Copy the Find Nearest server response to the receive buffer.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
pIrpContext->Specific.Create.FindNearestResponse[ResponseCount],
|
|
Response,
|
|
FIND_NEAREST_RESP_SIZE );
|
|
|
|
//
|
|
// If we have reached critical mass on the number of find
|
|
// nearest responses, set the event to indicate that we
|
|
// are done.
|
|
//
|
|
|
|
if ( ResponseCount == MAX_SAP_RESPONSES - 1 ) {
|
|
|
|
ASSERT( pIrpContext->Event.Header.SignalState == 0 );
|
|
#ifdef NWDBG
|
|
pIrpContext->DebugValue = 0x102;
|
|
#endif
|
|
pIrpContext->ResponseParameters.Error = 0;
|
|
NwSetIrpContextEvent( pIrpContext );
|
|
|
|
} else {
|
|
pIrpContext->pNpScb->OkToReceive = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Discard the invalid find nearest response.
|
|
//
|
|
|
|
pIrpContext->pNpScb->OkToReceive = TRUE;
|
|
}
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_SUCCESS );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
BOOLEAN
|
|
ProcessFindNearestEntry(
|
|
PIRP_CONTEXT IrpContext,
|
|
PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
|
|
)
|
|
{
|
|
OEM_STRING OemServerName;
|
|
UNICODE_STRING UidServerName;
|
|
UNICODE_STRING ServerName;
|
|
NTSTATUS Status;
|
|
PSCB pScb;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
BOOLEAN ExistingScb = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "ProcessFindNearestEntry\n", 0);
|
|
|
|
ServerName.Buffer = NULL;
|
|
UidServerName.Buffer = NULL;
|
|
|
|
try {
|
|
|
|
RtlInitString( &OemServerName, FindNearestResponse->ServerName );
|
|
ASSERT( OemServerName.Length < MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) );
|
|
|
|
Status = RtlOemStringToCountedUnicodeString(
|
|
&ServerName,
|
|
&OemServerName,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Lookup of the SCB by name. If it is not found, an SCB
|
|
// will be created.
|
|
//
|
|
|
|
Status = MakeUidServer(
|
|
&UidServerName,
|
|
&IrpContext->Specific.Create.UserUid,
|
|
&ServerName );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
ExistingScb = NwFindScb( &pScb, IrpContext, &UidServerName, &ServerName );
|
|
ASSERT( pScb != NULL );
|
|
pNpScb = pScb->pNpScb;
|
|
|
|
//
|
|
// Copy the network address to the SCB, and calculate the
|
|
// IPX address.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&pNpScb->ServerAddress,
|
|
&FindNearestResponse->Network,
|
|
sizeof( TDI_ADDRESS_IPX ) );
|
|
|
|
if ( pNpScb->ServerAddress.Socket != NCP_SOCKET ) {
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "FindNearest server socket is deviant.\n", 0 );
|
|
}
|
|
|
|
BuildIpxAddress(
|
|
pNpScb->ServerAddress.Net,
|
|
pNpScb->ServerAddress.Node,
|
|
pNpScb->ServerAddress.Socket,
|
|
&pNpScb->RemoteAddress );
|
|
|
|
if ( pNpScb->State == SCB_STATE_ATTACHING ) {
|
|
|
|
//
|
|
// We are in the process of trying to connect to this
|
|
// server so mark it reconnect required so that
|
|
// CreateScb will know that we've found it address.
|
|
//
|
|
|
|
pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} finally {
|
|
|
|
if ( pNpScb != NULL ) {
|
|
NwDereferenceScb( pNpScb );
|
|
}
|
|
|
|
if (UidServerName.Buffer != NULL) {
|
|
FREE_POOL(UidServerName.Buffer);
|
|
}
|
|
|
|
RtlFreeUnicodeString( &ServerName );
|
|
}
|
|
|
|
//
|
|
// Tell the caller if we created a new Scb
|
|
//
|
|
|
|
|
|
if (ExistingScb) {
|
|
DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", FALSE );
|
|
return FALSE;
|
|
} else {
|
|
DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", TRUE );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ConnectToServer(
|
|
IN struct _IRP_CONTEXT* pIrpContext,
|
|
OUT PSCB *pScbCollision
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transfers connect and negotiate buffer NCPs to the server.
|
|
|
|
This routine may be called upon to connect an anonymous scb. Upon
|
|
learning the name of the anonymous scb, it will determine if another
|
|
create has completed while the name lookup was in progress. If it has,
|
|
then the routine will refer the called to that new scb. Otherwise, the
|
|
scb will be entered onto the scb list and used normally. The RCB
|
|
protects the scb list by name only. For more info, see the comment
|
|
in CreateScb().
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - supplies context and server information
|
|
|
|
Return Value:
|
|
|
|
Status of operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status, BurstStatus;
|
|
PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
|
|
PSCB pScb = pNpScb->pScb;
|
|
BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
|
|
ULONG MaxSafeSize ;
|
|
BOOLEAN LIPNegotiated ;
|
|
PLOGON Logon;
|
|
|
|
OEM_STRING OemServerName;
|
|
UNICODE_STRING ServerName;
|
|
UNICODE_STRING CredentialName;
|
|
PUNICODE_STRING puConnectName;
|
|
BYTE OemName[MAX_SERVER_NAME_LENGTH];
|
|
WCHAR Server[MAX_SERVER_NAME_LENGTH];
|
|
KIRQL OldIrql;
|
|
UNICODE_STRING UidServerName;
|
|
BOOLEAN Success;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +0, Dbg, " Connect\n", 0);
|
|
|
|
RtlInitUnicodeString( &CredentialName, NULL );
|
|
|
|
//
|
|
// Get the tick count for our connection to this server
|
|
//
|
|
|
|
Status = GetTickCount( pIrpContext, &pNpScb->TickCount );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
pNpScb->TickCount = DEFAULT_TICK_COUNT;
|
|
}
|
|
|
|
pNpScb->SendTimeout = pNpScb->TickCount + 10;
|
|
|
|
//
|
|
// Initialize timers for a server that supports burst but not LIP
|
|
//
|
|
|
|
pNpScb->NwLoopTime = pNpScb->NwSingleBurstPacketTime = pNpScb->SendTimeout;
|
|
pNpScb->NwReceiveDelay = pNpScb->NwSendDelay = 0;
|
|
|
|
pNpScb->NtSendDelay.QuadPart = 0;
|
|
|
|
//
|
|
// Request connection
|
|
//
|
|
|
|
Status = ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"C-");
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if ( Status == STATUS_UNSUCCESSFUL ) {
|
|
#ifdef QFE_BUILD
|
|
Status = STATUS_TOO_MANY_SESSIONS;
|
|
#else
|
|
Status = STATUS_REMOTE_SESSION_LIMIT;
|
|
#endif
|
|
pNpScb->State = SCB_STATE_ATTACHING;
|
|
|
|
} else if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
|
|
|
|
//
|
|
// The connect timed out, suspect that the server is down
|
|
// and put it back in the attaching state.
|
|
//
|
|
|
|
pNpScb->State = SCB_STATE_ATTACHING;
|
|
}
|
|
|
|
goto ExitWithStatus;
|
|
}
|
|
|
|
pNpScb->SequenceNo++;
|
|
|
|
Stats.Sessions++;
|
|
|
|
//
|
|
// Get server information
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, "Get file server information\n", 0);
|
|
|
|
Status = ExchangeWithWait ( pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"S",
|
|
NCP_ADMIN_FUNCTION, NCP_GET_SERVER_INFO );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = ParseResponse( pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Nrbb",
|
|
OemName,
|
|
MAX_SERVER_NAME_LENGTH,
|
|
&pScb->MajorVersion,
|
|
&pScb->MinorVersion );
|
|
}
|
|
|
|
pNpScb->MajorVersion = pScb->MajorVersion;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ExitWithStatus;
|
|
}
|
|
|
|
//
|
|
// If this was an anonymous SCB, we need to check the name
|
|
// for a create collision before we do anything else.
|
|
//
|
|
|
|
if ( AnonymousScb ) {
|
|
|
|
//
|
|
// Grab the RCB to protect the server prefix table. We've
|
|
// spent the time sending the packet to look up the server
|
|
// name so we are a little greedy with the RCB to help
|
|
// minimize the chance of a collision.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
|
|
//
|
|
// Make the uid server name.
|
|
//
|
|
|
|
OemServerName.Buffer = OemName;
|
|
OemServerName.Length = 0;
|
|
OemServerName.MaximumLength = sizeof( OemName );
|
|
|
|
while ( ( OemServerName.Length < MAX_SERVER_NAME_LENGTH ) &&
|
|
( OemName[OemServerName.Length] != '\0' ) ) {
|
|
OemServerName.Length++;
|
|
}
|
|
|
|
ServerName.Buffer = Server;
|
|
ServerName.MaximumLength = sizeof( Server );
|
|
ServerName.Length = 0;
|
|
|
|
RtlOemStringToUnicodeString( &ServerName,
|
|
&OemServerName,
|
|
FALSE );
|
|
|
|
//
|
|
// If this is an extended credential create, munge the server name.
|
|
//
|
|
|
|
if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
|
|
|
|
Status = BuildExCredentialServerName( &ServerName,
|
|
pIrpContext->Specific.Create.puCredentialName,
|
|
&CredentialName );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
NwReleaseRcb( &NwRcb );
|
|
goto ExitWithStatus;
|
|
}
|
|
|
|
puConnectName = &CredentialName;
|
|
|
|
} else {
|
|
|
|
puConnectName = &ServerName;
|
|
}
|
|
|
|
//
|
|
// Tack on the uid.
|
|
//
|
|
|
|
Status = MakeUidServer( &UidServerName,
|
|
&pScb->UserUid,
|
|
puConnectName );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
NwReleaseRcb( &NwRcb );
|
|
goto ExitWithStatus;
|
|
}
|
|
|
|
//
|
|
// Actually do the look up in the prefix table.
|
|
//
|
|
|
|
PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServerName, 0 );
|
|
|
|
if ( PrefixEntry != NULL ) {
|
|
|
|
//
|
|
// There was a collision with this anonymous create. Dump
|
|
// the anonymous scb and pick up the new one.
|
|
//
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Anonymous create collided for %wZ.\n", &UidServerName );
|
|
|
|
//
|
|
// Disconnect this connection so we don't clutter the server.
|
|
//
|
|
|
|
ExchangeWithWait ( pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"D-" );
|
|
|
|
FREE_POOL( UidServerName.Buffer );
|
|
|
|
//
|
|
// Since there was a collision, we know for a fact that there's another
|
|
// good SCB for this server somewhere. We set the state on this anonymous
|
|
// SCB to SCB_STATE_FLAG_SHUTDOWN so that no one ever plays with the
|
|
// anonymous SCB again. The scavenger will clean it up soon.
|
|
//
|
|
|
|
pNpScb->State = SCB_STATE_FLAG_SHUTDOWN;
|
|
|
|
if ( pScbCollision ) {
|
|
*pScbCollision = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
|
|
NwReferenceScb( (*pScbCollision)->pNpScb );
|
|
Status = STATUS_SUCCESS;
|
|
goto ExitWithStatus;
|
|
} else {
|
|
DebugTrace( 0, Dbg, "Invalid path for an anonymous create.\n", 0 );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto ExitWithStatus;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This anonymous create didn't collide - cool! Fill in the server
|
|
// name, check the preferred, server setting, and put the SCB on the
|
|
// SCB queue in the correct location. This code is similar to pieces
|
|
// of code in NwAllocateAndInitScb() and NwFindScb().
|
|
//
|
|
|
|
DebugTrace( 0, Dbg, "Completing anonymous create for %wZ!\n", &UidServerName );
|
|
|
|
RtlCopyUnicodeString ( &pScb->UidServerName, &UidServerName );
|
|
pScb->UidServerName.Buffer[ UidServerName.Length / sizeof( WCHAR ) ] = L'\0';
|
|
|
|
pScb->UnicodeUid = pScb->UidServerName;
|
|
pScb->UnicodeUid.Length = UidServerName.Length -
|
|
puConnectName->Length -
|
|
sizeof(WCHAR);
|
|
|
|
//
|
|
// Make ServerName point partway down the buffer for UidServerName
|
|
//
|
|
|
|
pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
|
|
UidServerName.Length - puConnectName->Length);
|
|
|
|
pNpScb->ServerName.MaximumLength = puConnectName->Length;
|
|
pNpScb->ServerName.Length = puConnectName->Length;
|
|
|
|
//
|
|
// Determine if this is our preferred server.
|
|
//
|
|
|
|
Logon = FindUser( &pScb->UserUid, FALSE );
|
|
|
|
if (( Logon != NULL) &&
|
|
(RtlCompareUnicodeString( puConnectName, &Logon->ServerName, TRUE ) == 0 )) {
|
|
pScb->PreferredServer = TRUE;
|
|
NwReferenceScb( pNpScb );
|
|
}
|
|
|
|
FREE_POOL( UidServerName.Buffer );
|
|
|
|
//
|
|
// Insert the name of this server into the prefix table.
|
|
//
|
|
|
|
Success = RtlInsertUnicodePrefix( &NwRcb.ServerNameTable,
|
|
&pScb->UidServerName,
|
|
&pScb->PrefixEntry );
|
|
|
|
#ifdef NWDBG
|
|
if ( !Success ) {
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// This create is complete, release the RCB.
|
|
//
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
//
|
|
// If this is our preferred server, we have to move this guy
|
|
// to the head of the scb list. We do this after the create
|
|
// because we can't acquire the ScbSpinLock while holding the
|
|
// RCB.
|
|
//
|
|
|
|
if ( pScb->PreferredServer ) {
|
|
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
RemoveEntryList( &pNpScb->ScbLinks );
|
|
InsertHeadList( &ScbQueue, &pNpScb->ScbLinks );
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
}
|
|
|
|
}
|
|
|
|
if ( pScb->MajorVersion == 2 ) {
|
|
|
|
Stats.NW2xConnects++;
|
|
pNpScb->PageAlign = TRUE;
|
|
|
|
} else if ( pScb->MajorVersion == 3 ) {
|
|
|
|
Stats.NW3xConnects++;
|
|
|
|
if (pScb->MinorVersion > 0xb) {
|
|
pNpScb->PageAlign = FALSE;
|
|
} else {
|
|
pNpScb->PageAlign = TRUE;
|
|
}
|
|
|
|
} else if ( pScb->MajorVersion >= 4 ) {
|
|
|
|
Stats.NW4xConnects++;
|
|
pNpScb->PageAlign = FALSE;
|
|
|
|
NdsPing( pIrpContext, pScb );
|
|
|
|
}
|
|
|
|
//
|
|
// Get the local net max packet size. This is the max frame size
|
|
// does not include space for IPX or lower level headers.
|
|
//
|
|
|
|
Status = GetMaximumPacketSize( pIrpContext, &pNpScb->Server, &pNpScb->MaxPacketSize );
|
|
|
|
//
|
|
// If the transport won't tell us, pick the largest size that
|
|
// is guaranteed to work.
|
|
//
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
pNpScb->BufferSize = DEFAULT_PACKET_SIZE;
|
|
pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
|
|
} else {
|
|
pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
|
|
}
|
|
MaxSafeSize = pNpScb->MaxPacketSize ;
|
|
|
|
//
|
|
// Negotiate a burst mode connection. Keep track of that status.
|
|
//
|
|
|
|
Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
|
|
BurstStatus = Status ;
|
|
|
|
if (!NT_SUCCESS(Status) || !LIPNegotiated) {
|
|
|
|
//
|
|
// Negotiate buffer size with server if we didnt do burst
|
|
// sucessfully or if burst succeeded but we didnt do LIP.
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, "Negotiate Buffer Size\n", 0);
|
|
|
|
Status = ExchangeWithWait ( pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"Fw",
|
|
NCP_NEGOTIATE_BUFFER_SIZE,
|
|
pNpScb->BufferSize );
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
DebugTrace( +0, Dbg, " Parse response\n", 0);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = ParseResponse( pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Nw",
|
|
&pNpScb->BufferSize );
|
|
|
|
//
|
|
// Dont allow the server to fool us into using a
|
|
// packet size bigger than what the media can support.
|
|
// We have at least one case of server returning 4K while
|
|
// on ethernet.
|
|
//
|
|
// Use PacketThreshold so that the PacketAdjustment can be
|
|
// avoided on small packet sizes such as those on ethernet.
|
|
//
|
|
|
|
if (MaxSafeSize > (ULONG)PacketThreshold) {
|
|
MaxSafeSize -= (ULONG)LargePacketAdjustment;
|
|
}
|
|
|
|
//
|
|
// If larger than number we got from transport, taking in account
|
|
// IPX header (30) & NCP header (BURST_RESPONSE is a good worst
|
|
// case), we adjust accordingly.
|
|
//
|
|
if (pNpScb->BufferSize >
|
|
(MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))))
|
|
{
|
|
pNpScb->BufferSize = (USHORT)
|
|
(MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))) ;
|
|
}
|
|
|
|
//
|
|
// An SFT III server responded with a BufferSize of 0!
|
|
//
|
|
|
|
pNpScb->BufferSize = MAX(pNpScb->BufferSize,DEFAULT_PACKET_SIZE);
|
|
|
|
//
|
|
// If an explicit registry default was set, we honour that.
|
|
// Note that this only applies in the 'default' case, ie. we
|
|
// didnt negotiate LIP successfully. Typically, we dont
|
|
// expect to use this, because the server will drop to 512 if
|
|
// it finds routers in between. But if for some reason the server
|
|
// came back with a number that was higher than what some router
|
|
// in between can take, we have this as manual override.
|
|
//
|
|
|
|
if (DefaultMaxPacketSize != 0)
|
|
{
|
|
pNpScb->BufferSize = MIN (pNpScb->BufferSize,
|
|
(USHORT)DefaultMaxPacketSize) ;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(BurstStatus)) {
|
|
//
|
|
// We negotiated burst but not LIP. Save the packet size we
|
|
// have from above and renegotiate the burst so that the
|
|
// server knows how much it can send to us. And then take
|
|
// the minimum of the two to make sure we are safe.
|
|
//
|
|
USHORT SavedPacketSize = pNpScb->BufferSize ;
|
|
|
|
Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
|
|
|
|
pNpScb->BufferSize = MIN(pNpScb->BufferSize,SavedPacketSize) ;
|
|
}
|
|
}
|
|
|
|
ExitWithStatus:
|
|
|
|
if ( CredentialName.Buffer ) {
|
|
FREE_POOL( CredentialName.Buffer );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NegotiateBurstMode(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb,
|
|
BOOLEAN *LIPNegotiated
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine negotiates a burst mode connection with the specified
|
|
server.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies context and server information.
|
|
|
|
pNpScb - A pointer to the NONPAGED_SCB for the server we are
|
|
negotiating with.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
*LIPNegotiated = FALSE ;
|
|
|
|
if (pNpScb->MaxPacketSize == DEFAULT_PACKET_SIZE) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( NwBurstModeEnabled ) {
|
|
|
|
pNpScb->BurstRenegotiateReqd = TRUE;
|
|
|
|
pNpScb->SourceConnectionId = rand();
|
|
pNpScb->MaxSendSize = NwMaxSendSize;
|
|
pNpScb->MaxReceiveSize = NwMaxReceiveSize;
|
|
pNpScb->BurstSequenceNo = 0;
|
|
pNpScb->BurstRequestNo = 0;
|
|
|
|
Status = ExchangeWithWait(
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"FDdWdd",
|
|
NCP_NEGOTIATE_BURST_CONNECTION,
|
|
pNpScb->SourceConnectionId,
|
|
pNpScb->BufferSize,
|
|
pNpScb->Burst.Socket,
|
|
pNpScb->MaxSendSize,
|
|
pNpScb->MaxReceiveSize );
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
Status = ParseResponse(
|
|
pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Ned",
|
|
&pNpScb->DestinationConnectionId,
|
|
&pNpScb->MaxPacketSize );
|
|
|
|
if (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE) {
|
|
pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
|
|
if (NT_SUCCESS(GetMaxPacketSize( pIrpContext, pNpScb ))) {
|
|
*LIPNegotiated = TRUE ;
|
|
}
|
|
|
|
pNpScb->SendBurstModeEnabled = TRUE;
|
|
pNpScb->ReceiveBurstModeEnabled = TRUE;
|
|
|
|
//
|
|
// Use this size as the max read and write size instead of
|
|
// negotiating. This is what the VLM client does and is
|
|
// important because the negotiate will give a smaller value.
|
|
//
|
|
|
|
pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RenegotiateBurstMode(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine renegotiates a burst mode connection with the specified
|
|
server. I don't know why we need this but it seems to be required
|
|
by Netware latest burst implementation.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies context and server information.
|
|
|
|
pNpScb - A pointer to the NONPAGED_SCB for the server we are
|
|
negotiating with.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Re-negotiating burst mode.\n", 0);
|
|
|
|
pNpScb->SourceConnectionId = rand();
|
|
pNpScb->MaxSendSize = NwMaxSendSize;
|
|
pNpScb->MaxReceiveSize = NwMaxReceiveSize;
|
|
pNpScb->BurstSequenceNo = 0;
|
|
pNpScb->BurstRequestNo = 0;
|
|
|
|
Status = ExchangeWithWait(
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"FDdWdd",
|
|
NCP_NEGOTIATE_BURST_CONNECTION,
|
|
pNpScb->SourceConnectionId,
|
|
pNpScb->MaxPacketSize,
|
|
pNpScb->Burst.Socket,
|
|
pNpScb->MaxSendSize,
|
|
pNpScb->MaxReceiveSize );
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
Status = ParseResponse(
|
|
pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Ned",
|
|
&pNpScb->DestinationConnectionId,
|
|
&pNpScb->MaxPacketSize );
|
|
|
|
//
|
|
// Randomly downgrade the max burst size, because that is what
|
|
// the netware server does, and the new burst NLM requires.
|
|
//
|
|
|
|
pNpScb->MaxPacketSize -= 66;
|
|
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ||
|
|
(pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE)) {
|
|
|
|
pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
|
|
pNpScb->SendBurstModeEnabled = FALSE;
|
|
pNpScb->ReceiveBurstModeEnabled = FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use this size as the max read and write size instead of
|
|
// negotiating. This is what the VLM client does and is
|
|
// important because the negotiate will give a smaller value.
|
|
//
|
|
|
|
pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetMaxPacketSize(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to use the LIP protocol to find the true MTU of
|
|
the network.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - Supplies context and server information.
|
|
|
|
pNpScb - A pointer to the NONPAGED_SCB for the server we are '
|
|
negotiating with.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PUSHORT Buffer = NULL;
|
|
USHORT value;
|
|
int index;
|
|
PMDL PartialMdl = NULL, FullMdl = NULL;
|
|
PMDL ReceiveMdl;
|
|
NTSTATUS Status;
|
|
USHORT EchoSocket, LipPacketSize = 0;
|
|
int MinPacketSize, MaxPacketSize, CurrentPacketSize;
|
|
ULONG RxMdlLength = MdlLength(pIrpContext->RxMdl); // Save so we can restore it on exit.
|
|
|
|
BOOLEAN SecondTime = FALSE;
|
|
LARGE_INTEGER StartTime, Now, FirstPing, SecondPing, temp;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, DEBUG_TRACE_LIP, "GetMaxPacketSize...\n", 0);
|
|
|
|
//
|
|
// Negotiate LIP, attempt to negotiate a buffer of full network
|
|
// size.
|
|
//
|
|
|
|
Status = ExchangeWithWait(
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"Fwb",
|
|
NCP_NEGOTIATE_LIP_CONNECTION,
|
|
pNpScb->BufferSize,
|
|
0 ); // Flags
|
|
|
|
if ( NT_SUCCESS( Status )) {
|
|
Status = ParseResponse(
|
|
pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Nwx",
|
|
&LipPacketSize,
|
|
&EchoSocket );
|
|
}
|
|
|
|
//
|
|
// Speedup RAS
|
|
//
|
|
|
|
MaxPacketSize = (int) LipPacketSize - LipPacketAdjustment ;
|
|
|
|
if (( !NT_SUCCESS( Status )) ||
|
|
( MaxPacketSize <= DEFAULT_PACKET_SIZE ) ||
|
|
( EchoSocket == 0 )) {
|
|
|
|
//
|
|
// The server does not support LIP.
|
|
// Portable NW gives no error but socket 0.
|
|
// We have a report of a 3.11 server returning MaxPacketSize 0
|
|
//
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Account for the IPX header, which is not counted in
|
|
// the reported packet size. This causes problems for
|
|
// servers with poorly written net card drivers that
|
|
// abend when they get an oversize packet.
|
|
//
|
|
// This was reported by Richard Florance (richfl).
|
|
//
|
|
|
|
MaxPacketSize -= 30;
|
|
|
|
pNpScb->EchoCounter = MaxPacketSize;
|
|
|
|
//
|
|
// We will use the Echo address for the LIP protocol.
|
|
//
|
|
|
|
BuildIpxAddress(
|
|
pNpScb->ServerAddress.Net,
|
|
pNpScb->ServerAddress.Node,
|
|
EchoSocket,
|
|
&pNpScb->EchoAddress );
|
|
|
|
try {
|
|
|
|
Buffer = ALLOCATE_POOL_EX( NonPagedPool, MaxPacketSize );
|
|
|
|
//
|
|
// Avoid RAS compression algorithm from making the large and small
|
|
// buffers the same length since we want to see the difference in
|
|
// transmission times.
|
|
//
|
|
|
|
for (index = 0, value = 0; index < MaxPacketSize/2; index++, value++) {
|
|
Buffer[index] = value;
|
|
}
|
|
|
|
FullMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
|
|
if ( FullMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
PartialMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
|
|
if ( PartialMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
ReceiveMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
|
|
if ( ReceiveMdl == NULL ) {
|
|
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
} except( NwExceptionFilter( pIrpContext->pOriginalIrp, GetExceptionInformation() )) {
|
|
|
|
if ( Buffer != NULL ) {
|
|
FREE_POOL( Buffer );
|
|
}
|
|
|
|
if ( FullMdl != NULL ) {
|
|
FREE_MDL( FullMdl );
|
|
}
|
|
|
|
if ( PartialMdl != NULL ) {
|
|
FREE_MDL( FullMdl );
|
|
}
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
MmBuildMdlForNonPagedPool( FullMdl );
|
|
|
|
//
|
|
// Allocate a receive MDL and chain in to the IrpContext receive MDL.
|
|
//
|
|
|
|
pIrpContext->RxMdl->ByteCount = sizeof( NCP_RESPONSE ) + sizeof(ULONG);
|
|
MmBuildMdlForNonPagedPool( ReceiveMdl );
|
|
pIrpContext->RxMdl->Next = ReceiveMdl;
|
|
|
|
CurrentPacketSize = MaxPacketSize;
|
|
MinPacketSize = DEFAULT_PACKET_SIZE;
|
|
|
|
// Log values before we update them.
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
|
|
|
|
//
|
|
// The LIP sequence number is used to let us know if the response packet we are looking
|
|
// at goes with the packet that we just sent. On a really slow link, it could be a
|
|
// response that we have already given up on.
|
|
//
|
|
// The LIP tick adjustment tells ExchangeWithWait to try waiting a little longer.
|
|
//
|
|
|
|
pNpScb->LipSequenceNumber = 0;
|
|
pNpScb->LipTickAdjustment = 0;
|
|
|
|
//
|
|
// Loop using the bisection method to find the maximum packet size. Feel free to
|
|
// use shortcuts to avoid unnecessary timeouts.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Every time we send a packet, increment the LIP sequence number.
|
|
// We check the LIP sequence number in ServerDatagramHandler.
|
|
//
|
|
|
|
pNpScb->LipSequenceNumber++;
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Sending %d byte echo\n", CurrentPacketSize );
|
|
|
|
IoBuildPartialMdl(
|
|
FullMdl,
|
|
PartialMdl,
|
|
Buffer,
|
|
CurrentPacketSize - sizeof(NCP_RESPONSE) - sizeof(ULONG) );
|
|
|
|
//
|
|
// Send an echo packet. If we get a response, then we know that
|
|
// the minimum packet size we can use is at least as big as the
|
|
// echo packet size.
|
|
//
|
|
|
|
pIrpContext->pTdiStruct = &pIrpContext->pNpScb->Echo;
|
|
|
|
//
|
|
// Short-circuit the even better RAS compression.
|
|
//
|
|
|
|
for ( index = 0; index < MaxPacketSize/2; index++, value++) {
|
|
Buffer[index] = value;
|
|
}
|
|
|
|
KeQuerySystemTime( &StartTime );
|
|
|
|
Status = ExchangeWithWait(
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"E_DDf",
|
|
sizeof(NCP_RESPONSE ),
|
|
pNpScb->EchoCounter,
|
|
pNpScb->LipSequenceNumber,
|
|
PartialMdl );
|
|
|
|
if (( Status != STATUS_REMOTE_NOT_LISTENING ) ||
|
|
( SecondTime )) {
|
|
|
|
KeQuerySystemTime( &Now );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Response received %08lx\n", Status);
|
|
|
|
if (!SecondTime) {
|
|
|
|
MinPacketSize = CurrentPacketSize;
|
|
FirstPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
|
|
}
|
|
|
|
} else {
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "No response\n", 0);
|
|
MaxPacketSize = CurrentPacketSize;
|
|
}
|
|
|
|
pNpScb->EchoCounter++;
|
|
MmPrepareMdlForReuse( PartialMdl );
|
|
|
|
|
|
if (( MaxPacketSize - MinPacketSize <= LipAccuracy ) ||
|
|
( SecondTime )) {
|
|
|
|
//
|
|
// We have the maximum packet size.
|
|
// Now - StartTime is how long it takes for the round-trip. Now we'll
|
|
// try the same thing with a small packet and see how long it takes. From
|
|
// this we'll derive a throughput rating.
|
|
//
|
|
|
|
|
|
if ( SecondTime) {
|
|
|
|
SecondPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
|
|
break;
|
|
|
|
} else {
|
|
SecondTime = TRUE;
|
|
// Use a small packet size to verify that the server is still up.
|
|
CurrentPacketSize = sizeof(NCP_RESPONSE) + sizeof(ULONG) * 2;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Calculate the next packet size guess.
|
|
//
|
|
|
|
if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
|
|
( MaxPacketSize == 1463 )) {
|
|
|
|
CurrentPacketSize = 1458;
|
|
|
|
} else if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
|
|
( MaxPacketSize == 1458 )) {
|
|
|
|
CurrentPacketSize = 1436;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We didn't try one of our standard sizes so use the chop search
|
|
// to get to the next value.
|
|
//
|
|
|
|
CurrentPacketSize = ( MaxPacketSize + MinPacketSize ) / 2;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Set maximum burst packet size to %d\n", MinPacketSize );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing H = %08lx\n", FirstPing.HighPart );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing L = %08lx\n", FirstPing.LowPart );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing H = %08lx\n", SecondPing.HighPart );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing L = %08lx\n", SecondPing.LowPart );
|
|
|
|
//
|
|
// Avoid a divide by zero error if something bad happened.
|
|
//
|
|
|
|
if ( FirstPing.QuadPart != 0 ) {
|
|
pNpScb->LipDataSpeed = (ULONG) ( ( (LONGLONG)MinPacketSize * (LONGLONG)1600000 )
|
|
/ FirstPing.QuadPart );
|
|
} else {
|
|
pNpScb->LipDataSpeed = 0;
|
|
}
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "LipDataSpeed: %d\n", pNpScb->LipDataSpeed );
|
|
|
|
if ((NT_SUCCESS(Status)) &&
|
|
( MinPacketSize > DEFAULT_PACKET_SIZE )) {
|
|
|
|
temp.QuadPart = FirstPing.QuadPart - SecondPing.QuadPart;
|
|
|
|
if (temp.QuadPart > 0) {
|
|
|
|
//
|
|
// Convert to single trip instead of both ways.
|
|
//
|
|
|
|
temp.QuadPart = temp.QuadPart / (2 * 1000);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Small packet ping is slower or the same speed as the big ping.
|
|
// We can't time a small enough interval so go for no delay at all.
|
|
//
|
|
|
|
temp.QuadPart = 0;
|
|
|
|
}
|
|
|
|
|
|
ASSERT(temp.HighPart == 0);
|
|
|
|
pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay =
|
|
MAX(temp.LowPart, (ULONG)MinSendDelay);
|
|
|
|
pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay =
|
|
MAX(temp.LowPart, (ULONG)MinReceiveDelay);
|
|
|
|
//
|
|
// Time for a big packet to go one way.
|
|
//
|
|
|
|
pNpScb->NwSingleBurstPacketTime = pNpScb->NwReceiveDelay;
|
|
|
|
pNpScb->NtSendDelay.QuadPart = pNpScb->NwReceiveDelay * -1000;
|
|
|
|
|
|
//
|
|
// Maximum that SendDelay is allowed to reach
|
|
//
|
|
|
|
pNpScb->NwMaxSendDelay = MAX( 52, MIN( pNpScb->NwSendDelay, MaxSendDelay ));
|
|
pNpScb->NwMaxReceiveDelay = MAX( 52, MIN( pNpScb->NwReceiveDelay, MaxReceiveDelay ));
|
|
|
|
//
|
|
// Time for a small packet to get to the server and back.
|
|
//
|
|
|
|
temp.QuadPart = SecondPing.QuadPart / 1000;
|
|
pNpScb->NwLoopTime = temp.LowPart;
|
|
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxSendDelay = %08lx\n", pNpScb->NwMaxSendDelay );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxReceiveDelay = %08lx\n", pNpScb->NwMaxReceiveDelay );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwLoopTime = %08lx\n", pNpScb->NwLoopTime );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
|
|
DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
|
|
|
|
//
|
|
// Reset Tdi struct so that we send future NCPs from the server socket.
|
|
//
|
|
|
|
pIrpContext->pTdiStruct = NULL;
|
|
|
|
//
|
|
// Now decouple the MDL
|
|
//
|
|
|
|
pIrpContext->TxMdl->Next = NULL;
|
|
pIrpContext->RxMdl->Next = NULL;
|
|
pIrpContext->RxMdl->ByteCount = RxMdlLength;
|
|
|
|
//
|
|
// Calculate the maximum amount of data we can send in a burst write
|
|
// packet after all the header info is stripped.
|
|
//
|
|
|
|
pNpScb->MaxPacketSize = MinPacketSize - sizeof( NCP_BURST_WRITE_REQUEST );
|
|
|
|
FREE_MDL( PartialMdl );
|
|
FREE_MDL( ReceiveMdl );
|
|
FREE_MDL( FullMdl );
|
|
FREE_POOL( Buffer );
|
|
|
|
|
|
DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the small packet couldn't echo then assume the worst.
|
|
//
|
|
|
|
//
|
|
// Reset Tdi struct so that we send future NCPs from the server socket.
|
|
//
|
|
|
|
pIrpContext->pTdiStruct = NULL;
|
|
|
|
//
|
|
// Now decouple the MDL
|
|
//
|
|
|
|
pIrpContext->TxMdl->Next = NULL;
|
|
pIrpContext->RxMdl->Next = NULL;
|
|
pIrpContext->RxMdl->ByteCount = RxMdlLength;
|
|
|
|
FREE_MDL( PartialMdl );
|
|
FREE_MDL( ReceiveMdl );
|
|
FREE_MDL( FullMdl );
|
|
FREE_POOL( Buffer );
|
|
|
|
|
|
DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyAllScb(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys all server control blocks.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PNONPAGED_SCB pNpScb;
|
|
|
|
DebugTrace(+1, Dbg, "DestroyAllScbs....\n", 0);
|
|
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
|
|
//
|
|
// Walk the list of SCBs and kill them all.
|
|
//
|
|
|
|
while (!IsListEmpty(&ScbQueue)) {
|
|
|
|
ScbQueueEntry = RemoveHeadList( &ScbQueue );
|
|
pNpScb = CONTAINING_RECORD(ScbQueueEntry, NONPAGED_SCB, ScbLinks);
|
|
|
|
//
|
|
// We can't hold the spin lock while deleting an SCB, so release
|
|
// it now.
|
|
//
|
|
|
|
KeReleaseSpinLock(&ScbSpinLock, OldIrql);
|
|
|
|
NwDeleteScb( pNpScb->pScb );
|
|
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
}
|
|
|
|
KeReleaseSpinLock(&ScbSpinLock, OldIrql);
|
|
|
|
DebugTrace(-1, Dbg, "DestroyAllScb\n", 0 );
|
|
}
|
|
|
|
|
|
VOID
|
|
NwDeleteScb(
|
|
PSCB pScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes an SCB. The SCB must not be in use.
|
|
|
|
*** The caller must own the RCB exclusive.
|
|
|
|
Arguments:
|
|
|
|
Scb - The SCB to delete
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PNONPAGED_SCB pNpScb;
|
|
PLIST_ENTRY CacheEntry;
|
|
PNDS_OBJECT_CACHE_ENTRY ObjectCache;
|
|
BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace(+1, Dbg, "NwDeleteScb...\n", 0);
|
|
|
|
pNpScb = pScb->pNpScb;
|
|
|
|
//
|
|
// Make sure we are not deleting a logged in connection
|
|
// or we will hang up the license until the server times
|
|
// it out.
|
|
//
|
|
|
|
ASSERT( pNpScb->State != SCB_STATE_IN_USE );
|
|
ASSERT( pNpScb->Reference == 0 );
|
|
ASSERT( !pNpScb->Sending );
|
|
ASSERT( !pNpScb->Receiving );
|
|
ASSERT( !pNpScb->OkToReceive );
|
|
ASSERT( IsListEmpty( &pNpScb->Requests ) );
|
|
ASSERT( IsListEmpty( &pScb->IcbList ) );
|
|
ASSERT( pScb->IcbCount == 0 );
|
|
ASSERT( pScb->VcbCount == 0 );
|
|
|
|
|
|
DebugTrace(0, Dbg, "Cleaning up SCB %08lx\n", pScb);
|
|
|
|
if ( AnonymousScb ) {
|
|
DebugTrace(0, Dbg, "SCB is anonymous\n", &pNpScb->ServerName );
|
|
} else {
|
|
ASSERT( IsListEmpty( &pScb->ScbSpecificVcbQueue ) );
|
|
DebugTrace(0, Dbg, "SCB is %wZ\n", &pNpScb->ServerName );
|
|
}
|
|
|
|
DebugTrace(0, Dbg, "SCB state is %d\n", &pNpScb->State );
|
|
|
|
if ( !AnonymousScb ) {
|
|
RtlRemoveUnicodePrefix ( &NwRcb.ServerNameTable, &pScb->PrefixEntry );
|
|
}
|
|
|
|
IPX_Close_Socket( &pNpScb->Server );
|
|
IPX_Close_Socket( &pNpScb->WatchDog );
|
|
IPX_Close_Socket( &pNpScb->Send );
|
|
IPX_Close_Socket( &pNpScb->Echo);
|
|
IPX_Close_Socket( &pNpScb->Burst);
|
|
|
|
NwUninitializePidTable( pNpScb );
|
|
|
|
FREE_POOL( pNpScb );
|
|
|
|
if ( pScb->UserName.Buffer != NULL ) {
|
|
FREE_POOL( pScb->UserName.Buffer );
|
|
}
|
|
|
|
//
|
|
// Free the object cache buffer if there is one.
|
|
//
|
|
|
|
if( pScb->ObjectCacheBuffer != NULL ) {
|
|
|
|
//
|
|
// Remove any references this cache has to other SCBs.
|
|
//
|
|
// NOTE: We do not need the lock here, since no one else
|
|
// can be using this SCB.
|
|
//
|
|
|
|
CacheEntry = pScb->ObjectCacheList.Flink;
|
|
|
|
while( CacheEntry != &(pScb->ObjectCacheList) ) {
|
|
|
|
ObjectCache = CONTAINING_RECORD( CacheEntry, NDS_OBJECT_CACHE_ENTRY, Links );
|
|
|
|
if( ObjectCache->Scb != NULL ) {
|
|
|
|
NwDereferenceScb( ObjectCache->Scb->pNpScb );
|
|
ObjectCache->Scb = NULL;
|
|
}
|
|
|
|
CacheEntry = CacheEntry->Flink;
|
|
}
|
|
|
|
FREE_POOL( pScb->ObjectCacheBuffer );
|
|
}
|
|
|
|
FREE_POOL( pScb );
|
|
|
|
DebugTrace(-1, Dbg, "NwDeleteScb -> VOID\n", 0);
|
|
}
|
|
|
|
|
|
PNONPAGED_SCB
|
|
SelectConnection(
|
|
PNONPAGED_SCB NpScb OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a default server (which is also the nearest server).
|
|
If NpScb is not supplied, simply return the first server in
|
|
the list. If it is supplied return the next server in the
|
|
list after the given server.
|
|
|
|
Arguments:
|
|
|
|
NpScb - The starting point for the server search.
|
|
|
|
Return Value:
|
|
|
|
Scb to be used or NULL.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
KIRQL OldIrql;
|
|
PNONPAGED_SCB pNextNpScb;
|
|
|
|
DebugTrace(+1, Dbg, "SelectConnection....\n", 0);
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
|
|
if ( NpScb == NULL ) {
|
|
ScbQueueEntry = ScbQueue.Flink ;
|
|
} else {
|
|
ScbQueueEntry = NpScb->ScbLinks.Flink;
|
|
}
|
|
|
|
for ( ;
|
|
ScbQueueEntry != &ScbQueue ;
|
|
ScbQueueEntry = ScbQueueEntry->Flink ) {
|
|
|
|
pNextNpScb = CONTAINING_RECORD(
|
|
ScbQueueEntry,
|
|
NONPAGED_SCB,
|
|
ScbLinks );
|
|
|
|
//
|
|
// Check to make sure that this SCB is usable.
|
|
//
|
|
|
|
if (( pNextNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
|
|
( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
|
|
( pNextNpScb->State == SCB_STATE_IN_USE )) {
|
|
|
|
NwReferenceScb( pNextNpScb );
|
|
|
|
KeReleaseSpinLock(&ScbSpinLock, OldIrql);
|
|
DebugTrace(+0, Dbg, " NpScb = %lx\n", pNextNpScb );
|
|
DebugTrace(-1, Dbg, " NpScb->State = %x\n", pNextNpScb->State );
|
|
return pNextNpScb;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql);
|
|
DebugTrace(-1, Dbg, " NULL\n", 0);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
NwLogoffAllServers(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PLARGE_INTEGER Uid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a logoff to all connected servers created by the Logon
|
|
user or all servers if Logon is NULL.
|
|
|
|
Arguments:
|
|
|
|
Uid - Supplies the servers to disconnect from.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY ScbQueueEntry;
|
|
PLIST_ENTRY NextScbQueueEntry;
|
|
PNONPAGED_SCB pNpScb;
|
|
|
|
DebugTrace( 0, Dbg, "NwLogoffAllServers\n", 0 );
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
for (ScbQueueEntry = ScbQueue.Flink ;
|
|
ScbQueueEntry != &ScbQueue ;
|
|
ScbQueueEntry = NextScbQueueEntry ) {
|
|
|
|
pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
|
|
//
|
|
// Reference the SCB so that it doesn't disappear while we
|
|
// are disconnecting.
|
|
//
|
|
|
|
NwReferenceScb( pNpScb );
|
|
|
|
//
|
|
// Release the SCB spin lock so that we can send a logoff
|
|
// NCP.
|
|
//
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
|
|
//
|
|
// Destroy this Scb if its not the permanent Scb and either we
|
|
// are destroying all Scb's or it was created for this user.
|
|
//
|
|
|
|
if (( pNpScb->pScb != NULL ) &&
|
|
(( Uid == NULL ) ||
|
|
( pNpScb->pScb->UserUid.QuadPart == (*Uid).QuadPart))) {
|
|
|
|
NwLogoffAndDisconnect( pIrpContext, pNpScb );
|
|
}
|
|
|
|
KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
|
|
|
|
//
|
|
// Release the temporary reference.
|
|
//
|
|
|
|
NextScbQueueEntry = pNpScb->ScbLinks.Flink;
|
|
NwDereferenceScb( pNpScb );
|
|
}
|
|
|
|
KeReleaseSpinLock( &ScbSpinLock, OldIrql );
|
|
}
|
|
|
|
|
|
VOID
|
|
NwLogoffAndDisconnect(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a logoff and disconnects from the name server.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - A pointer to the current IRP context.
|
|
pNpScb - A pointer to the server to logoff and disconnect.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PSCB pScb = pNpScb->pScb;
|
|
|
|
PAGED_CODE();
|
|
|
|
pIrpContext->pNpScb = pNpScb;
|
|
pIrpContext->pScb = pScb;
|
|
|
|
//
|
|
// Queue ourselves to the SCB, and wait to get to the front to
|
|
// protect access to server State.
|
|
//
|
|
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
//
|
|
// If we are logging out from the preferred server, free the preferred
|
|
// server reference.
|
|
//
|
|
|
|
if ( pScb != NULL &&
|
|
pScb->PreferredServer ) {
|
|
pScb->PreferredServer = FALSE;
|
|
NwDereferenceScb( pNpScb );
|
|
}
|
|
|
|
//
|
|
// Nothing to do if we are not connected.
|
|
//
|
|
|
|
if ( pNpScb->State != SCB_STATE_IN_USE &&
|
|
pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) {
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we timeout then we don't want to go to the bother of
|
|
// reconnecting.
|
|
//
|
|
|
|
ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
|
|
|
|
//
|
|
// Logout and disconnect.
|
|
//
|
|
|
|
if ( pNpScb->State == SCB_STATE_IN_USE ) {
|
|
|
|
ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"F",
|
|
NCP_LOGOUT );
|
|
}
|
|
|
|
ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"D-" ); // Disconnect
|
|
|
|
Stats.Sessions--;
|
|
|
|
if ( pScb->MajorVersion == 2 ) {
|
|
Stats.NW2xConnects--;
|
|
} else if ( pScb->MajorVersion == 3 ) {
|
|
Stats.NW3xConnects--;
|
|
} else if ( pScb->MajorVersion >= 4 ) {
|
|
Stats.NW4xConnects--;
|
|
}
|
|
|
|
//
|
|
// dfergus 19 Apr 2001 #302137
|
|
// Removed the following fix for fix #302137
|
|
//
|
|
// tommye - MS 25584 / MCS 274
|
|
//
|
|
// Added code to look for this tree in the user's cached
|
|
// credentials and remove it if there is one. This fixes
|
|
// a problem where the user could perform a tree connect
|
|
// providing the user name and password, delete the connection
|
|
// then connect again successfully providing only the user name
|
|
// (we would get the password from the cached credentials).
|
|
//
|
|
// 5/31/00 - made changes to locking per conversation with Anoop
|
|
//
|
|
/* // Begin 302137
|
|
{
|
|
PLOGON pLogon = NULL;
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &pScb->UserUid, FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
if (pLogon) {
|
|
PLIST_ENTRY pFirst, pNext;
|
|
PNDS_SECURITY_CONTEXT pNdsContext;
|
|
|
|
// Lock the credential list while we walk it
|
|
|
|
NwAcquireExclusiveCredList( pLogon, pIrpContext );
|
|
|
|
pFirst = &pLogon->NdsCredentialList;
|
|
pNext = pLogon->NdsCredentialList.Flink;
|
|
|
|
// Go through the credential list and find a match for the tree name
|
|
|
|
while ( pNext && ( pFirst != pNext ) ) {
|
|
|
|
pNdsContext = (PNDS_SECURITY_CONTEXT)
|
|
CONTAINING_RECORD( pNext,
|
|
NDS_SECURITY_CONTEXT,
|
|
Next );
|
|
|
|
ASSERT( pNdsContext->ntc == NW_NTC_NDS_CREDENTIAL );
|
|
|
|
// If this is the same tree, free the entry
|
|
|
|
if ( !RtlCompareUnicodeString( &pScb->NdsTreeName,
|
|
&pNdsContext->NdsTreeName,
|
|
TRUE ) ) {
|
|
|
|
DebugTrace(0, Dbg, " Removing cached credential for tree %wZ\n", &pNdsContext->NdsTreeName);
|
|
|
|
RemoveEntryList( &pNdsContext->Next );
|
|
FreeNdsContext( pNdsContext );
|
|
|
|
break;
|
|
}
|
|
|
|
pNext = pNdsContext->Next.Flink;
|
|
}
|
|
|
|
// Release the lock
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
}
|
|
}
|
|
*/ // End 302137
|
|
//
|
|
// Free the remembered username and password.
|
|
//
|
|
|
|
if ( pScb != NULL && pScb->UserName.Buffer != NULL ) {
|
|
FREE_POOL( pScb->UserName.Buffer );
|
|
RtlInitUnicodeString( &pScb->UserName, NULL );
|
|
RtlInitUnicodeString( &pScb->Password, NULL );
|
|
}
|
|
|
|
pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
InitializeAttach (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize global structures for attaching to servers.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
KeInitializeSpinLock( &ScbSpinLock );
|
|
InitializeListHead(&ScbQueue);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
OpenScbSockets(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNpScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open the communications sockets for an SCB.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - The IRP context pointers for the request in progress.
|
|
|
|
pNpScb - The SCB to connect to the network.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Auto allocate to the server socket.
|
|
//
|
|
|
|
pNpScb->Server.Socket = 0;
|
|
|
|
Status = IPX_Open_Socket (pIrpContext, &pNpScb->Server);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Watchdog Socket is Server.Socket+1
|
|
//
|
|
|
|
pNpScb->WatchDog.Socket = NextSocket( pNpScb->Server.Socket );
|
|
Status = IPX_Open_Socket ( pIrpContext, &pNpScb->WatchDog );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Send Socket is WatchDog.Socket+1
|
|
//
|
|
|
|
pNpScb->Send.Socket = NextSocket( pNpScb->WatchDog.Socket );
|
|
Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Send );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Echo socket
|
|
//
|
|
|
|
pNpScb->Echo.Socket = NextSocket( pNpScb->Send.Socket );
|
|
Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Echo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Burst socket
|
|
//
|
|
|
|
pNpScb->Burst.Socket = NextSocket( pNpScb->Echo.Socket );
|
|
Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Burst );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
NTSTATUS
|
|
DoBinderyLogon(
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING Password
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs a bindery based encrypted logon.
|
|
|
|
Note: Rcb is held exclusively so that we can access the Logon queue
|
|
safely.
|
|
|
|
Arguments:
|
|
|
|
pIrpContext - The IRP context pointers for the request in progress.
|
|
|
|
UserName - The user name to use to login.
|
|
|
|
Password - The password to use to login.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PNONPAGED_SCB pNpScb;
|
|
PSCB pScb;
|
|
UNICODE_STRING Name;
|
|
UNICODE_STRING PWord;
|
|
UCHAR EncryptKey[ENCRYPTION_KEY_SIZE ];
|
|
NTSTATUS Status;
|
|
PVOID Buffer;
|
|
PLOGON Logon = NULL;
|
|
PWCH OldBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, "DoBinderyLogon...\n", 0);
|
|
|
|
//
|
|
// First get an encryption key.
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, " Get Login key\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"S",
|
|
NCP_ADMIN_FUNCTION, NCP_GET_LOGIN_KEY );
|
|
|
|
pNpScb = IrpContext->pNpScb;
|
|
pScb = pNpScb->pScb;
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = ParseResponse(
|
|
IrpContext,
|
|
IrpContext->rsp,
|
|
IrpContext->ResponseLength,
|
|
"Nr",
|
|
EncryptKey, sizeof(EncryptKey) );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
//
|
|
// Choose a name and password to use to connect to the server. Use
|
|
// the user supplied if the exist. Otherwise if the server already
|
|
// has a remembered username use the remembered name. If nothing
|
|
// else is available use the defaults from logon. Finally, if the
|
|
// user didn't even logon, use GUEST no password.
|
|
//
|
|
|
|
|
|
if ( UserName != NULL && UserName->Buffer != NULL ) {
|
|
|
|
Name = *UserName;
|
|
|
|
} else if ( pScb->UserName.Buffer != NULL ) {
|
|
|
|
Name = pScb->UserName;
|
|
|
|
} else {
|
|
|
|
Logon = FindUser( &pScb->UserUid, FALSE );
|
|
|
|
if (Logon != NULL ) {
|
|
Name = Logon->UserName;
|
|
} else {
|
|
ASSERT( FALSE && "No logon record found" );
|
|
return( STATUS_ACCESS_DENIED );
|
|
}
|
|
}
|
|
|
|
if ( Password != NULL && Password->Buffer != NULL ) {
|
|
|
|
PWord = *Password;
|
|
|
|
} else if ( pScb->Password.Buffer != NULL ) {
|
|
/*
|
|
* Password is not passed in, or implied.
|
|
* If the user name matches or was not passed in,
|
|
* then use the SCB password,
|
|
* else use a null password.
|
|
*/
|
|
if ( UserName == NULL || UserName->Buffer == NULL ||
|
|
( RtlEqualUnicodeString( &pScb->UserName, UserName, TRUE )) ) {
|
|
PWord = pScb->Password;
|
|
} else {
|
|
PWord.Buffer = L"";
|
|
PWord.Length = PWord.MaximumLength = 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( Logon == NULL ) {
|
|
Logon = FindUser( &pScb->UserUid, FALSE );
|
|
}
|
|
|
|
if ( Logon != NULL ) {
|
|
PWord = Logon->PassWord;
|
|
} else {
|
|
ASSERT( FALSE && "No logon record found" );
|
|
return( STATUS_ACCESS_DENIED );
|
|
}
|
|
}
|
|
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// Failed to get an encryption key. Login to server, plain text
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, " Plain Text Login\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"SwJU", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
|
|
NCP_ADMIN_FUNCTION, NCP_PLAIN_TEXT_LOGIN,
|
|
OT_USER,
|
|
&Name,
|
|
&PWord);
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = ParseResponse(
|
|
IrpContext,
|
|
IrpContext->rsp,
|
|
IrpContext->ResponseLength,
|
|
"N" );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
return( STATUS_WRONG_PASSWORD);
|
|
}
|
|
|
|
} else if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// We have an encryption key. Get the ObjectId
|
|
//
|
|
|
|
UCHAR Response[ENCRYPTION_KEY_SIZE];
|
|
UCHAR ObjectId[OBJECT_ID_SIZE];
|
|
OEM_STRING UOPassword;
|
|
|
|
DebugTrace( +0, Dbg, " Query users objectid\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"SwJ", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
|
|
NCP_ADMIN_FUNCTION, NCP_QUERY_OBJECT_ID,
|
|
OT_USER,
|
|
&Name);
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Save the new address in a local copy so that we can logout.
|
|
//
|
|
|
|
Status = ParseResponse(
|
|
IrpContext,
|
|
IrpContext->rsp,
|
|
IrpContext->ResponseLength,
|
|
"Nr",
|
|
ObjectId, OBJECT_ID_SIZE );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return( STATUS_NO_SUCH_USER );
|
|
}
|
|
|
|
//
|
|
// Convert the unicode password to uppercase and then the oem
|
|
// character set.
|
|
//
|
|
|
|
if ( PWord.Length > 0 ) {
|
|
|
|
Status = RtlUpcaseUnicodeStringToOemString( &UOPassword, &PWord, TRUE );
|
|
if (!NT_SUCCESS(Status)) {
|
|
return( Status );
|
|
}
|
|
|
|
} else {
|
|
UOPassword.Buffer = "";
|
|
UOPassword.Length = UOPassword.MaximumLength = 0;
|
|
}
|
|
|
|
RespondToChallenge( ObjectId, &UOPassword, EncryptKey, Response);
|
|
|
|
if ( PWord.Length > 0) {
|
|
RtlFreeAnsiString( &UOPassword );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " Encrypted Login\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"SrwJ", // JimTh 5/19/2001 - Send Name using 'J' option, DBCS translation if on Far East system
|
|
NCP_ADMIN_FUNCTION, NCP_ENCRYPTED_LOGIN,
|
|
Response, sizeof(Response),
|
|
OT_USER,
|
|
&Name);
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Save the new address in a local copy so that we can logout
|
|
//
|
|
|
|
Status = ParseResponse(
|
|
IrpContext,
|
|
IrpContext->rsp,
|
|
IrpContext->ResponseLength,
|
|
"N" );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// Special case error mappings.
|
|
//
|
|
|
|
if (( Status == STATUS_UNSUCCESSFUL ) ||
|
|
( Status == STATUS_UNEXPECTED_NETWORK_ERROR /* 2.2 servers */ )) {
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
}
|
|
|
|
if ( Status == STATUS_LOCK_NOT_GRANTED ) {
|
|
Status = STATUS_ACCOUNT_RESTRICTION; // Bindery locked
|
|
}
|
|
|
|
if ( Status == STATUS_DISK_FULL ) {
|
|
#ifdef QFE_BUILD
|
|
Status = STATUS_TOO_MANY_SESSIONS;
|
|
#else
|
|
Status = STATUS_REMOTE_SESSION_LIMIT;
|
|
#endif
|
|
}
|
|
|
|
if ( Status == STATUS_FILE_LOCK_CONFLICT ) {
|
|
Status = STATUS_SHARING_PAUSED;
|
|
}
|
|
|
|
if ( Status == STATUS_NO_MORE_ENTRIES ) {
|
|
Status = STATUS_NO_SUCH_USER; // No such object on "Login Object Encrypted" NCP.
|
|
}
|
|
|
|
//
|
|
// Netware 4.x servers return a different NCP error for
|
|
// a disabled account (from intruder lockout) on bindery login,
|
|
// and nwconvert maps this to a dos error. In this special case,
|
|
// we'll catch it and map it back.
|
|
//
|
|
|
|
if ( ( IrpContext->pNpScb->pScb->MajorVersion >= 4 ) &&
|
|
( Status == 0xC001003B ) ) {
|
|
Status = STATUS_ACCOUNT_DISABLED;
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
} else {
|
|
|
|
return( Status );
|
|
|
|
}
|
|
|
|
//
|
|
// If the Uid is for the system process then the username must be
|
|
// in the NtGateway group on the server.
|
|
//
|
|
|
|
if ( IrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
|
|
|
|
NTSTATUS Status1 ;
|
|
|
|
// IsBinderyObjectInSet?
|
|
Status1 = ExchangeWithWait (
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"SwppwU",
|
|
NCP_ADMIN_FUNCTION, NCP_IS_OBJECT_IN_SET,
|
|
OT_GROUP,
|
|
"NTGATEWAY",
|
|
"GROUP_MEMBERS",
|
|
OT_USER,
|
|
&Name);
|
|
|
|
if ( !NT_SUCCESS( Status1 ) ) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Success. Save the username & password for reconnect.
|
|
//
|
|
|
|
//
|
|
// Setup to free the old user name and password buffer.
|
|
//
|
|
|
|
if ( pScb->UserName.Buffer != NULL ) {
|
|
OldBuffer = pScb->UserName.Buffer;
|
|
} else {
|
|
OldBuffer = NULL;
|
|
}
|
|
|
|
Buffer = ALLOCATE_POOL( NonPagedPool, Name.Length + PWord.Length );
|
|
if ( Buffer == NULL ) {
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
pScb->UserName.Buffer = Buffer;
|
|
pScb->UserName.Length = pScb->UserName.MaximumLength = Name.Length;
|
|
RtlMoveMemory( pScb->UserName.Buffer, Name.Buffer, Name.Length );
|
|
|
|
pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + Name.Length);
|
|
pScb->Password.Length = pScb->Password.MaximumLength = PWord.Length;
|
|
RtlMoveMemory( pScb->Password.Buffer, PWord.Buffer, PWord.Length );
|
|
|
|
if ( OldBuffer != NULL ) {
|
|
FREE_POOL( OldBuffer );
|
|
}
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS
|
|
NwAllocateAndInitScb(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PUNICODE_STRING UidServerName OPTIONAL,
|
|
IN PUNICODE_STRING ServerName OPTIONAL,
|
|
OUT PSCB *ppScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a pointer to a newly created SCB. If
|
|
the UidServerName and ServerName are supplied, the SCB name
|
|
fields are initialized to this name. Otherwise, the name
|
|
fields are left blank to be filled in later.
|
|
|
|
If UidServerName is provided, ServerName MUST also be provided!!
|
|
|
|
The returned SCB is NOT filed in the server prefix table since
|
|
it might not yet have a name.
|
|
|
|
Return Value:
|
|
|
|
The created SCB or NULL.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PSCB pScb = NULL;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
USHORT ServerNameLength;
|
|
PLOGON Logon;
|
|
PNDS_OBJECT_CACHE_ENTRY ObjectEntry;
|
|
ULONG EntrySize;
|
|
ULONG i;
|
|
|
|
//
|
|
// Allocate enough space for a credential munged tree name.
|
|
//
|
|
|
|
pScb = ALLOCATE_POOL ( PagedPool,
|
|
sizeof( SCB ) +
|
|
( ( NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2 ) * sizeof( WCHAR ) ) );
|
|
|
|
if ( !pScb ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory( pScb, sizeof( SCB ) );
|
|
RtlInitializeBitMap( &pScb->DriveMapHeader, pScb->DriveMap, MAX_DRIVES );
|
|
|
|
//
|
|
// Initialize pointers to ensure cleanup on error case operates
|
|
// correctly.
|
|
//
|
|
|
|
if ( UidServerName &&
|
|
UidServerName->Length ) {
|
|
|
|
ServerNameLength = UidServerName->Length + sizeof( WCHAR );
|
|
|
|
} else {
|
|
|
|
ServerNameLength = ( MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) ) +
|
|
( MAX_UNICODE_UID_LENGTH * sizeof( WCHAR ) ) +
|
|
( 2 * sizeof( WCHAR ) );
|
|
|
|
}
|
|
|
|
pScb->pNpScb = ALLOCATE_POOL ( NonPagedPool,
|
|
sizeof( NONPAGED_SCB ) + ServerNameLength );
|
|
|
|
if ( !pScb->pNpScb ) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
RtlZeroMemory( pScb->pNpScb, sizeof( NONPAGED_SCB ) );
|
|
|
|
pNpScb = pScb->pNpScb;
|
|
pNpScb->pScb = pScb;
|
|
|
|
//
|
|
// If we know the server name, copy it to the allocated buffer.
|
|
// Append a NULL so that we can use the name a nul-terminated string.
|
|
//
|
|
|
|
pScb->UidServerName.Buffer = (PWCHAR)( pScb->pNpScb + 1 );
|
|
pScb->UidServerName.MaximumLength = ServerNameLength;
|
|
pScb->UidServerName.Length = 0;
|
|
|
|
RtlInitUnicodeString( &(pNpScb->ServerName), NULL );
|
|
|
|
if ( UidServerName &&
|
|
UidServerName->Length ) {
|
|
|
|
RtlCopyUnicodeString ( &pScb->UidServerName, UidServerName );
|
|
pScb->UidServerName.Buffer[ UidServerName->Length / sizeof( WCHAR ) ] = L'\0';
|
|
|
|
pScb->UnicodeUid = pScb->UidServerName;
|
|
pScb->UnicodeUid.Length = UidServerName->Length -
|
|
ServerName->Length -
|
|
sizeof(WCHAR);
|
|
|
|
//
|
|
// Make ServerName point partway down the buffer for UidServerName
|
|
//
|
|
|
|
pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
|
|
UidServerName->Length - ServerName->Length);
|
|
|
|
pNpScb->ServerName.MaximumLength = ServerName->Length;
|
|
pNpScb->ServerName.Length = ServerName->Length;
|
|
|
|
}
|
|
|
|
pScb->NdsTreeName.MaximumLength = NDS_TREE_NAME_LEN * sizeof( WCHAR );
|
|
pScb->NdsTreeName.Buffer = (PWCHAR)(pScb + 1);
|
|
|
|
pScb->NodeTypeCode = NW_NTC_SCB;
|
|
pScb->NodeByteSize = sizeof(SCB);
|
|
InitializeListHead( &pScb->ScbSpecificVcbQueue );
|
|
InitializeListHead( &pScb->IcbList );
|
|
|
|
//
|
|
// Remember UID of the file creator so we can find the username and
|
|
// password to use for this Scb when we need it.
|
|
//
|
|
|
|
pScb->UserUid = pIrpContext->Specific.Create.UserUid;
|
|
|
|
//
|
|
// Initialize the non-paged part of the SCB.
|
|
//
|
|
|
|
pNpScb->NodeTypeCode = NW_NTC_SCBNP;
|
|
pNpScb->NodeByteSize = sizeof(NONPAGED_SCB);
|
|
|
|
//
|
|
// Set the initial SCB reference count.
|
|
//
|
|
|
|
if ( UidServerName &&
|
|
UidServerName->Length ) {
|
|
|
|
Logon = FindUser( &pScb->UserUid, FALSE );
|
|
|
|
if (( Logon != NULL) &&
|
|
(RtlCompareUnicodeString( ServerName, &Logon->ServerName, TRUE ) == 0 )) {
|
|
pScb->PreferredServer = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( pScb->PreferredServer ) {
|
|
pNpScb->Reference = 2;
|
|
} else {
|
|
pNpScb->Reference = 1;
|
|
}
|
|
|
|
//
|
|
// Finish linking the two parts of the Scb together.
|
|
//
|
|
|
|
pNpScb->pScb = pScb;
|
|
|
|
KeInitializeSpinLock( &pNpScb->NpScbSpinLock );
|
|
KeInitializeSpinLock( &pNpScb->NpScbInterLock );
|
|
InitializeListHead( &pNpScb->Requests );
|
|
|
|
RtlFillMemory( &pNpScb->LocalAddress, sizeof(IPXaddress), 0xff);
|
|
|
|
pNpScb->State = SCB_STATE_ATTACHING;
|
|
pNpScb->SequenceNo = 1;
|
|
|
|
Status = OpenScbSockets( pIrpContext, pNpScb );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
Status = SetEventHandler (
|
|
pIrpContext,
|
|
&pNpScb->Server,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
&ServerDatagramHandler,
|
|
pNpScb );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
Status = SetEventHandler (
|
|
pIrpContext,
|
|
&pNpScb->WatchDog,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
&WatchDogDatagramHandler,
|
|
pNpScb );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
Status = SetEventHandler (
|
|
pIrpContext,
|
|
&pNpScb->Send,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
&SendDatagramHandler,
|
|
pNpScb );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
Status = SetEventHandler (
|
|
pIrpContext,
|
|
&pNpScb->Echo,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
&ServerDatagramHandler,
|
|
pNpScb );
|
|
|
|
pNpScb->EchoCounter = 2;
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
Status = SetEventHandler (
|
|
pIrpContext,
|
|
&pNpScb->Burst,
|
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
|
&ServerDatagramHandler,
|
|
pNpScb );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
goto ExitWithCleanup;
|
|
}
|
|
|
|
KeQuerySystemTime( &pNpScb->LastUsedTime );
|
|
|
|
//
|
|
// Set burst mode data.
|
|
//
|
|
|
|
pNpScb->BurstRequestNo = 0;
|
|
pNpScb->BurstSequenceNo = 0;
|
|
|
|
//
|
|
// Initialize the NDS object cache if it is enabled.
|
|
//
|
|
|
|
if( NdsObjectCacheSize != 0 ) {
|
|
|
|
//
|
|
// Determine the size of each entry. This must be eight byte aligned,
|
|
// since they will all be allocated in one big block.
|
|
//
|
|
// NOTE: The NDS_OBJECT_CACHE_ENTRY structure must already be aligned.
|
|
//
|
|
|
|
EntrySize = sizeof(NDS_OBJECT_CACHE_ENTRY) + (((sizeof(WCHAR) * MAX_NDS_NAME_CHARS) + 7) & ~7);
|
|
|
|
//
|
|
// Allocate the buffer for the cache. If memory cannot be allocated, it is not
|
|
// fatal. In this case, the object cache is just disabled for this SCB.
|
|
//
|
|
|
|
pScb->ObjectCacheBuffer = ALLOCATE_POOL( PagedPool,
|
|
(EntrySize * NdsObjectCacheSize) );
|
|
|
|
if( pScb->ObjectCacheBuffer != NULL ) {
|
|
|
|
//
|
|
// Initialize the object cache buffer. It is one big block of memory that
|
|
// is broken up into cache entries. Each entry is connected to the next
|
|
// via a list entry. This way the entries can be manipulated on the linked
|
|
// list, but there is only one allocation to deal with.
|
|
//
|
|
|
|
InitializeListHead( &(pScb->ObjectCacheList) );
|
|
|
|
RtlZeroMemory( pScb->ObjectCacheBuffer,
|
|
(EntrySize * NdsObjectCacheSize) );
|
|
|
|
for( i = 0; i < NdsObjectCacheSize; i++ ) {
|
|
|
|
ObjectEntry = (PNDS_OBJECT_CACHE_ENTRY)(((PBYTE)(pScb->ObjectCacheBuffer)) + (EntrySize * i));
|
|
|
|
InsertTailList( &(pScb->ObjectCacheList), &(ObjectEntry->Links) );
|
|
|
|
//
|
|
// The buffer for the object name string immediately follows the cache entry.
|
|
//
|
|
|
|
ObjectEntry->ObjectName.Buffer = (PWCHAR)(ObjectEntry + 1);
|
|
ObjectEntry->ObjectName.MaximumLength = MAX_NDS_NAME_CHARS * sizeof(WCHAR);
|
|
}
|
|
|
|
//
|
|
// This semaphore protects the object cache.
|
|
//
|
|
|
|
KeInitializeSemaphore( &(pScb->ObjectCacheLock),
|
|
1,
|
|
1 );
|
|
}
|
|
}
|
|
|
|
if ( ppScb ) {
|
|
*ppScb = pScb;
|
|
}
|
|
|
|
if (NwInitializePidTable( pNpScb )) {
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ExitWithCleanup:
|
|
|
|
if ( pNpScb != NULL ) {
|
|
|
|
IPX_Close_Socket( &pNpScb->Server );
|
|
IPX_Close_Socket( &pNpScb->WatchDog );
|
|
IPX_Close_Socket( &pNpScb->Send );
|
|
IPX_Close_Socket( &pNpScb->Echo );
|
|
IPX_Close_Socket( &pNpScb->Burst );
|
|
|
|
FREE_POOL( pNpScb );
|
|
}
|
|
|
|
FREE_POOL(pScb);
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NwFindScb(
|
|
OUT PSCB *Scb,
|
|
PIRP_CONTEXT IrpContext,
|
|
PUNICODE_STRING UidServerName,
|
|
PUNICODE_STRING ServerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a pointer to the SCB for the named server.
|
|
The name is looked up in the SCB table. If it is found, a
|
|
pointer to the SCB is returned. If none is found an SCB is
|
|
created and initialized.
|
|
|
|
This routine returns with the SCB referenced and the SCB
|
|
resources held.
|
|
|
|
Arguments:
|
|
|
|
Scb - Returns a pointer to the found / created SCB.
|
|
|
|
IrpContext - The IRP context pointers for the request in progress.
|
|
|
|
ServerName - The name of the server to find / create.
|
|
|
|
Return Value:
|
|
|
|
TRUE - An old SCB was found.
|
|
|
|
FALSE - A new SCB was created, or an attempt to create the SCB failed.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN RcbHeld;
|
|
PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
|
|
NTSTATUS Status;
|
|
PSCB pScb = NULL;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
KIRQL OldIrql;
|
|
BOOLEAN Success, PreferredServerIsSet;
|
|
|
|
//
|
|
// Acquire the RCB exclusive to protect the prefix table.
|
|
// Then lookup the name of this server.
|
|
//
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
RcbHeld = TRUE;
|
|
PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, UidServerName, 0 );
|
|
|
|
if ( PrefixEntry != NULL ) {
|
|
|
|
PSCB pScb = NULL;
|
|
PNONPAGED_SCB pNpScb = NULL;
|
|
|
|
//
|
|
// We found the SCB, increment the reference count and return
|
|
// success.
|
|
//
|
|
|
|
pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
|
|
pNpScb = pScb->pNpScb;
|
|
|
|
NwReferenceScb( pNpScb );
|
|
|
|
//
|
|
// Release the RCB.
|
|
//
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
DebugTrace(-1, Dbg, "NwFindScb -> %08lx\n", pScb );
|
|
*Scb = pScb;
|
|
return( TRUE );
|
|
}
|
|
|
|
//
|
|
// We do not have a connection to this server so create the new Scb if requested.
|
|
//
|
|
|
|
if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
|
|
NwReleaseRcb( &NwRcb );
|
|
*Scb = NULL;
|
|
return(FALSE);
|
|
}
|
|
|
|
try {
|
|
|
|
Status = NwAllocateAndInitScb( IrpContext,
|
|
UidServerName,
|
|
ServerName,
|
|
&pScb );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
ExRaiseStatus( Status );
|
|
}
|
|
|
|
ASSERT( pScb != NULL );
|
|
|
|
pNpScb = pScb->pNpScb;
|
|
|
|
PreferredServerIsSet = pScb->PreferredServer;
|
|
|
|
//
|
|
//*******************************************************************
|
|
//
|
|
// From this point on we must not fail to create the Scb because the
|
|
// another thread will be able to reference the Scb causing severe
|
|
// problems in the finaly clause or in the other thread.
|
|
//
|
|
//*******************************************************************
|
|
//
|
|
|
|
//
|
|
// Insert this SCB in the global list if SCBs.
|
|
// If it is the default (i.e. preferred) server, stick it at the
|
|
// front of the queue so that SelectConnection() will select it
|
|
// for bindery queries.
|
|
//
|
|
|
|
KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
|
|
|
|
if ( PreferredServerIsSet ) {
|
|
InsertHeadList(&ScbQueue, &pNpScb->ScbLinks);
|
|
} else {
|
|
InsertTailList(&ScbQueue, &pNpScb->ScbLinks);
|
|
}
|
|
|
|
KeReleaseSpinLock(&ScbSpinLock, OldIrql);
|
|
|
|
//
|
|
// Insert the name of this server into the prefix table.
|
|
//
|
|
|
|
Success = RtlInsertUnicodePrefix(
|
|
&NwRcb.ServerNameTable,
|
|
&pScb->UidServerName,
|
|
&pScb->PrefixEntry );
|
|
|
|
#ifdef NWDBG
|
|
if ( !Success ) {
|
|
DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// The Scb is now in the prefix table. Any new requests for this
|
|
// connection can be added to the Scb->Requests queue while we
|
|
// attach to the server.
|
|
//
|
|
|
|
NwReleaseRcb( &NwRcb );
|
|
RcbHeld = FALSE;
|
|
|
|
//
|
|
// If we got an error we should have raised an exception.
|
|
//
|
|
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
} finally {
|
|
|
|
if ( !NT_SUCCESS( Status ) || AbnormalTermination() ) {
|
|
*Scb = NULL;
|
|
} else {
|
|
*Scb = pScb;
|
|
}
|
|
|
|
if (RcbHeld) {
|
|
NwReleaseRcb( &NwRcb );
|
|
}
|
|
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
NTSTATUS
|
|
QueryServersAddress(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PNONPAGED_SCB pNearestScb,
|
|
PUNICODE_STRING pServerName,
|
|
IPXaddress *pServerAddress
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING NewServer;
|
|
USHORT CurrChar = 0;
|
|
BOOLEAN SeedServerRedirect = FALSE;
|
|
PNONPAGED_SCB pOrigNpScb;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
|
|
|
|
//
|
|
// Unmunge the server name if this is a
|
|
// supplemental credential connect.
|
|
//
|
|
|
|
UnmungeCredentialName( pServerName, &NewServer );
|
|
|
|
} else if ( EnableMultipleConnects ) {
|
|
|
|
//
|
|
// Strip server name trailer, if it exists. If there
|
|
// was no trailer, the length will end up being exactly
|
|
// the same as when we started.
|
|
//
|
|
|
|
Status = DuplicateUnicodeStringWithString(
|
|
&NewServer,
|
|
pServerName,
|
|
PagedPool
|
|
);
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
return Status;
|
|
}
|
|
|
|
while ( (CurrChar < (NewServer.Length / sizeof(WCHAR))) &&
|
|
NewServer.Buffer[CurrChar] != ((WCHAR)L'#') ) {
|
|
CurrChar++;
|
|
}
|
|
NewServer.Length = CurrChar * sizeof(WCHAR);
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we support seed server indirection, check the server
|
|
// name for a seed server. If there is a seed server specified,
|
|
// connect us to it.
|
|
//
|
|
|
|
|
|
if ( AllowSeedServerRedirection ) {
|
|
|
|
pOrigNpScb = pIrpContext->pNpScb;
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
|
|
Status = IndirectToSeedServer( pIrpContext,
|
|
pServerName,
|
|
&NewServer );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
SeedServerRedirect = TRUE;
|
|
|
|
} else {
|
|
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't get redirected to a seed server, then
|
|
// just set up the server name like normal and try that.
|
|
//
|
|
|
|
if ( !SeedServerRedirect ) {
|
|
|
|
NewServer.Length = pServerName->Length;
|
|
NewServer.MaximumLength = pServerName->MaximumLength;
|
|
NewServer.Buffer = pServerName->Buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Query the bindery of the nearest server looking for
|
|
// the network address of the target server.
|
|
//
|
|
|
|
DebugTrace( +0, Dbg, "Query servers address\n", 0);
|
|
|
|
Status = ExchangeWithWait (
|
|
pIrpContext,
|
|
SynchronousResponseCallback,
|
|
"SwUbp",
|
|
NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
|
|
OT_FILESERVER,
|
|
&NewServer,
|
|
1, // Segment number
|
|
NET_ADDRESS_PROPERTY );
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Save the new address.
|
|
//
|
|
|
|
Status = ParseResponse(
|
|
pIrpContext,
|
|
pIrpContext->rsp,
|
|
pIrpContext->ResponseLength,
|
|
"Nr",
|
|
pServerAddress, sizeof(TDI_ADDRESS_IPX) );
|
|
}
|
|
|
|
DebugTrace( +0, Dbg, " %X\n", Status);
|
|
|
|
//
|
|
// Map the server not found error to something sensible.
|
|
//
|
|
|
|
if (( Status == STATUS_NO_MORE_ENTRIES ) ||
|
|
( Status == STATUS_VIRTUAL_CIRCUIT_CLOSED ) ||
|
|
( Status == NwErrorToNtStatus(ERROR_UNEXP_NET_ERR))) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
if ( SeedServerRedirect ) {
|
|
|
|
//
|
|
// Dequeue from the seed server and free the ref count.
|
|
// There should always be an original server, but just
|
|
// in case there's not, we check.
|
|
//
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
NwDereferenceScb( pIrpContext->pNpScb );
|
|
|
|
ASSERT( pOrigNpScb != NULL );
|
|
|
|
if ( pOrigNpScb ) {
|
|
|
|
pIrpContext->pNpScb = pOrigNpScb;
|
|
pIrpContext->pScb = pOrigNpScb->pScb;
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
|
|
} else {
|
|
|
|
pIrpContext->pNpScb = NULL;
|
|
pIrpContext->pScb = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
TreeConnectScb(
|
|
IN PSCB Scb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the tree connect count for a SCB.
|
|
|
|
Arguments:
|
|
|
|
Scb - A pointer to the SCB to connect to.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
Scb->AttachCount++;
|
|
Scb->OpenFileCount++;
|
|
NwReferenceScb( Scb->pNpScb );
|
|
NwReleaseRcb( &NwRcb );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
TreeDisconnectScb(
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PSCB Scb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the tree connect count for a SCB.
|
|
|
|
*** This routine must be called with the RCB resource held.
|
|
|
|
Arguments:
|
|
|
|
Scb - A pointer to the SCB to disconnect.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if ( Scb->AttachCount > 0 ) {
|
|
|
|
Scb->AttachCount--;
|
|
Scb->OpenFileCount--;
|
|
NwDereferenceScb( Scb->pNpScb );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if ( Scb->OpenFileCount == 0 ) {
|
|
|
|
//
|
|
// Logoff and disconnect from the server now.
|
|
// Hold on to the SCB lock.
|
|
// This prevents another thread from trying to access
|
|
// SCB will this thread is logging off.
|
|
//
|
|
|
|
NwLogoffAndDisconnect( IrpContext, Scb->pNpScb );
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
NwDequeueIrpContext( IrpContext, FALSE );
|
|
return( Status );
|
|
}
|
|
|
|
|
|
VOID
|
|
ReconnectScb(
|
|
IN PIRP_CONTEXT pIrpContext,
|
|
IN PSCB pScb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reconnects all the dir handles to a server
|
|
when reconnecting an SCB.
|
|
|
|
|
|
Arguments:
|
|
|
|
pScb - A pointer to the SCB that has just been reconnected.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If this is a reconnect, kill all old ICB and VCB handles
|
|
//
|
|
|
|
if ( pScb->VcbCount != 0 ) {
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
|
|
//
|
|
// Invalid all ICBs
|
|
//
|
|
|
|
NwInvalidateAllHandlesForScb( pScb );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
//
|
|
// Acquire new VCB handles for all VCBs.
|
|
//
|
|
|
|
NwReopenVcbHandlesForScb( pIrpContext, pScb );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
IndirectToSeedServer(
|
|
PIRP_CONTEXT pIrpContext,
|
|
PUNICODE_STRING pServerName,
|
|
PUNICODE_STRING pNewServer
|
|
)
|
|
/*+++
|
|
|
|
Description: This function takes a server name. If that server name
|
|
is in the format target_server(seed_server), this routine will:
|
|
|
|
1) put the target server in pNewServer
|
|
2) lookup the seed server and queue the irp context to it
|
|
|
|
---*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
UNICODE_STRING SeedServer;
|
|
PWCHAR pwCurrent;
|
|
PSCB pScb;
|
|
|
|
RtlInitUnicodeString( &SeedServer, NULL );
|
|
RtlInitUnicodeString( pNewServer, NULL );
|
|
|
|
pwCurrent = pServerName->Buffer;
|
|
|
|
DebugTrace( 0, Dbg, "IndirectToSeedServer: %wZ\n", pServerName );
|
|
|
|
while ( pNewServer->Length <= pServerName->Length ) {
|
|
|
|
if ( *pwCurrent == L'(' ) {
|
|
|
|
pNewServer->Buffer = pServerName->Buffer;
|
|
pNewServer->MaximumLength = pNewServer->Length;
|
|
DebugTrace( 0, Dbg, "Target server is %wZ.\n", pNewServer );
|
|
|
|
SeedServer.Length = pServerName->Length -
|
|
pNewServer->Length;
|
|
|
|
if ( SeedServer.Length <= ( 2 * sizeof( WCHAR ) ) ) {
|
|
|
|
SeedServer.Length = 0;
|
|
|
|
} else {
|
|
|
|
SeedServer.Length -= ( 2 * sizeof( WCHAR ) );
|
|
SeedServer.Buffer = pwCurrent + 1;
|
|
SeedServer.MaximumLength = SeedServer.Length;
|
|
DebugTrace( 0, Dbg, "Seed server is %wZ.\n", &SeedServer );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
pNewServer->Length += sizeof( WCHAR );
|
|
pwCurrent++;
|
|
}
|
|
}
|
|
|
|
if ( SeedServer.Length == 0 ) {
|
|
DebugTrace( 0, Dbg, "IndirectToSeedServer failed to decode nested name.\n", 0 );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Now do something about it.
|
|
//
|
|
|
|
Status = CreateScb( &pScb,
|
|
pIrpContext,
|
|
&SeedServer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
FALSE );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DebugTrace( 0, Dbg, "Couldn't contact seed server.\n", 0 );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
return STATUS_SUCCESS;
|
|
}
|