Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3474 lines
75 KiB

/*++
Copyright (c) 1993/4 Microsoft Corporation
Module Name:
ncp.c
Abstract:
Contains routine which accepts the bop from a 16 bit
application and processes the request appropriately.
Normally it performes an NCP exchange on behalf of the
application.
Author:
Colin Watson (colinw) 07-Jul-1993
Environment:
Revision History:
--*/
#include "procs.h"
#define BASE_DOS_ERROR ((NTSTATUS )0xC0010000L)
#include <packon.h>
typedef struct _TTSOUTPACKET {
UCHAR SubFunction;
USHORT cx;
USHORT dx;
} TTSOUTPACKET, *PTTSOUTPACKET ;
typedef struct _TTSINPACKET{
USHORT cx;
USHORT dx;
} TTSINPACKET, *PTTSINPACKET;
#include <packoff.h>
VOID
InitDosTable(
PNWDOSTABLE pdt
);
VOID
LoadPreferredServerName(
VOID
);
VOID
ProcessResourceArray(
LPNETRESOURCE NetResource,
DWORD NumElements
);
VOID
ProcessResource(
LPNETRESOURCE NetResource
);
VOID
SendNCP(
ULONG Command
);
VOID
SendF2NCP(
ULONG Command
);
UCHAR
AttachmentControl(
ULONG Command
);
VOID
SendNCP2(
ULONG Command,
PUCHAR Request,
ULONG RequestLength,
PUCHAR Reply,
ULONG ReplyLength
);
VOID
CloseConnection(
CONN_INDEX Connection
);
NTSTATUS
InitConnection(
CONN_INDEX Connection
);
VOID
GetDirectoryHandle(
VOID
);
VOID
LoadDriveHandleTable(
VOID
);
VOID
AllocateDirectoryHandle(
VOID
);
VOID
ResetDrive(
UCHAR Drive
);
VOID
AllocateDirectoryHandle2(
VOID
);
PWCHAR
BuildUNC(
IN PUCHAR aName,
IN ULONG aLength
);
VOID
GetServerDateAndTime(
VOID
);
VOID
GetShellVersion(
IN USHORT Command
);
VOID
TTS(
VOID
);
VOID
OpenCreateFile(
VOID
);
BOOL
IsItNetWare(
PUCHAR Name
);
VOID
SetCompatibility(
VOID
);
VOID
OpenQueueFile(
VOID
);
VOID
AttachHandle(
VOID
);
VOID
ProcessExit(
VOID
);
VOID
SystemLogout(
VOID
);
VOID
ServerFileCopy(
VOID
);
VOID
SetStatus(
NTSTATUS Status
);
//
// The following pointer contains the 32 bit virtual address of where
// the nw16.exe tsr holds the workstation structures.
//
PNWDOSTABLE pNwDosTable;
//
// Global variables used to hold the state for this process
//
UCHAR OriginalPrimary = 0;
HANDLE ServerHandles[MC];
HANDLE Win32DirectoryHandleTable[MD];
PWCHAR Drives[MD]; // Strings such as R: or a unc name
UCHAR SearchDriveTable[16];
BOOLEAN Initialized = FALSE;
BOOLEAN TablesValid = FALSE; // Reload each time a process starts
BOOLEAN DriveHandleTableValid = FALSE; // Reload first time process does NW API
WORD DosTableSegment;
WORD DosTableOffset;
extern UCHAR LockMode;
#if NWDBG
BOOL GotDebugState = FALSE;
extern int DebugCtrl;
#endif
VOID
Nw16Register(
VOID
)
/*++
Routine Description:
This function is called by wow when nw16.sys is loaded.
Arguments:
Return Value:
None.
--*/
{
DWORD status;
HANDLE enumHandle;
LPNETRESOURCE netResource;
DWORD numElements;
DWORD bufferSize;
DWORD dwScope = RESOURCE_CONNECTED;
NwPrint(("Nw16Register\n"));
if ( !Initialized) {
UCHAR CurDir[256];
DosTableSegment = getAX();
DosTableOffset = getDX();
//
// this call always made from Real Mode (hence FALSE for last param)
//
pNwDosTable = (PNWDOSTABLE) GetVDMPointer (
(ULONG)((DosTableSegment << 16)|DosTableOffset),
sizeof(NWDOSTABLE),
FALSE
);
InitDosTable( pNwDosTable );
if ((GetCurrentDirectoryA(sizeof(CurDir)-1, CurDir) >= 2) &&
(CurDir[1] == ':')) {
pNwDosTable->CurrentDrive = tolower(CurDir[0]) - 'a';
}
InitLocks();
}
#if NWDBG
{
WCHAR Value[80];
if (GetEnvironmentVariableW(L"NWDEBUG",
Value,
sizeof(Value)/sizeof(Value[0]) - 1)) {
DebugCtrl = Value[0] - '0';
// 0 Use logfile
// 1 Use debugger
// 2/undefined No debug output
// 4 Use logfile, close on process exit
// 8 Use logfile, verbose, close on process exit
DebugControl( DebugCtrl );
GotDebugState = TRUE; // Don't look again until process exits vdm
}
}
#endif
LoadPreferredServerName();
//
// Attempt to allow for MD drives
//
bufferSize = (MD*sizeof(NETRESOURCE))+1024;
netResource = (LPNETRESOURCE) LocalAlloc(LPTR, bufferSize);
if (netResource == NULL) {
NwPrint(("Nw16Register: LocalAlloc Failed %d\n",GetLastError));
setCF(1);
return;
}
//-----------------------------------//
// Get a handle for a top level enum //
//-----------------------------------//
status = NPOpenEnum(
dwScope,
RESOURCETYPE_DISK,
0,
NULL,
&enumHandle);
if ( status != WN_SUCCESS) {
NwPrint(("Nw16Register:WNetOpenEnum failed %d\n",status));
//
// If there is an extended error, display it.
//
if (status == WN_EXTENDED_ERROR) {
DisplayExtendedError();
}
goto LoadLocal;
}
// ---- Multi-user code change : Add "while" ----
while ( status == WN_SUCCESS ) {
//-----------------------------//
// Enumerate the disk devices. //
//-----------------------------//
numElements = 0xffffffff;
status = NwEnumConnections(
enumHandle,
&numElements,
netResource,
&bufferSize,
TRUE); // Include implicit connections
if ( status == WN_SUCCESS) {
//----------------------------------------//
// Insert the results in the Nw Dos Table //
//----------------------------------------//
ProcessResourceArray( netResource, numElements);
}
} // end while
if ( status == WN_NO_MORE_ENTRIES ) {
status = WN_SUCCESS;
} else
if ( status != WN_SUCCESS) {
NwPrint(("Nw16Register:NwEnumResource failed %d\n",status));
//
// If there is an extended error, display it.
//
if (status == WN_EXTENDED_ERROR) {
DisplayExtendedError();
}
WNetCloseEnum(enumHandle);
goto LoadLocal;
}
//------------------------------------------//
// Close the EnumHandle & print the results //
//------------------------------------------//
status = NPCloseEnum(enumHandle);
if (status != WN_SUCCESS) {
NwPrint(("Nw16Register:WNetCloseEnum failed %d\n",status));
//
// If there is an extended error, display it.
//
if (status == WN_EXTENDED_ERROR) {
DisplayExtendedError();
}
goto LoadLocal;
}
LoadLocal:
//
// Add the local devices so that NetWare apps don't try to map them
// to remote servers.
//
{
USHORT Drive;
WCHAR DriveString[4];
UINT Type;
DriveString[1] = L':';
DriveString[2] = L'\\';
DriveString[3] = L'\0';
//
// Hardwire A: and B: because hitting the floppy drive with
// GetDriveType takes too long.
//
pNwDosTable->DriveFlagTable[0] = LOCAL_DRIVE;
pNwDosTable->DriveFlagTable[1] = LOCAL_DRIVE;
for (Drive = 2; Drive <= 'Z' - 'A'; Drive++ ) {
if (pNwDosTable->DriveFlagTable[Drive] == 0) {
DriveString[0] = L'A' + Drive;
Type = GetDriveTypeW( DriveString );
//
// 0 means drive type cannot be determined, all others are
// provided by other filesystems.
//
if (Type != 1) {
pNwDosTable->DriveFlagTable[Drive] = LOCAL_DRIVE;
}
}
}
#ifdef NWDBG
for (Drive = 0; Drive < MD; Drive++ ) {
DriveString[0] = L'A' + Drive;
NwPrint(("%c(%d)=%x,",'A' + Drive,
GetDriveTypeW( DriveString ),
pNwDosTable->DriveFlagTable[Drive] ));
if (!((Drive + 1) % 8)) {
NwPrint(("\n",0));
}
}
NwPrint(("\n"));
#endif
}
if ( !Initialized ) {
Initialized = TRUE;
pNwDosTable->PrimaryServer = OriginalPrimary;
}
TablesValid = TRUE;
LocalFree(netResource);
setCF(0);
NwPrint(("Nw16Register: End\n"));
}
VOID
LoadPreferredServerName(
VOID
)
{
//
// If we already have a connection to somewhere then we already have a
// preferred servername of some sort.
//
if (pNwDosTable->ConnectionIdTable[0].ci_InUse == IN_USE) {
return;
}
//
// Load the server name table with the preferred/nearest server.
//
CopyMemory( pNwDosTable->ServerNameTable[0], "*", sizeof("*"));
if (NT_SUCCESS(OpenConnection( 0 ))) {
if( NT_SUCCESS(InitConnection(0)) ) {
//
// Close the handle so that the rdr can be stopped if
// user is not running a netware aware application.
//
CloseConnection(0);
pNwDosTable->PrimaryServer = 1;
return;
}
}
pNwDosTable->PrimaryServer = 0;
}
VOID
ProcessResourceArray(
LPNETRESOURCE NetResource,
DWORD NumElements
)
{
DWORD i;
for (i=0; i<NumElements ;i++ ) {
ProcessResource(&(NetResource[i]));
}
return;
}
VOID
ProcessResource(
LPNETRESOURCE NetResource
)
{
SERVERNAME ServerName;
int ServerNameLength;
int i;
int Connection;
BOOLEAN Found = FALSE;
//
// Extract Server Name from RemoteName, skipping first 2 chars that
// contain backslashes and taking care to handle entries that only
// contain a servername.
//
ServerNameLength = wcslen( NetResource->lpRemoteName );
ASSERT(NetResource->lpRemoteName[0] == '\\');
ASSERT(NetResource->lpRemoteName[1] == '\\');
for (i = 2; i <= ServerNameLength; i++) {
if ((NetResource->lpRemoteName[i] == '\\') ||
(i == ServerNameLength )){
ServerNameLength = i - 2;
WideCharToMultiByte(
CP_OEMCP,
0,
&NetResource->lpRemoteName[2],
ServerNameLength,
ServerName,
sizeof( ServerName ),
NULL,
NULL );
CharUpperBuffA( ServerName, ServerNameLength );
ZeroMemory( &ServerName[ServerNameLength],
SERVERNAME_LENGTH - ServerNameLength );
break;
}
}
//
// Now try to find ServerName in the connection table. If there are
// more than MC servers in the table already then skip this one.
//
for (Connection = 0; Connection < MC ; Connection++ ) {
if ((pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) &&
(!memcmp( pNwDosTable->ServerNameTable[Connection], ServerName, SERVERNAME_LENGTH))) {
Found = TRUE;
break;
}
}
NwPrint(("Nw16ProcessResource Server: %s\n",ServerName));
if ( Found == FALSE ) {
for (Connection = 0; Connection < MC ; Connection++ ) {
if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == FREE) {
CopyMemory( pNwDosTable->ServerNameTable[Connection],
ServerName,
SERVERNAME_LENGTH);
if ((NT_SUCCESS(OpenConnection( (CONN_INDEX)Connection ))) &&
( NT_SUCCESS(InitConnection( (CONN_INDEX)Connection ) ))) {
Found = TRUE;
} else {
// Couldn't talk to the server so ignore it.
ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
}
break; // Escape from for (Connection =...
}
}
}
//
// Build the drive id and drive flag tables. Entries 0 - 25
// are reserved for drives redirected to letters. We use drives
// 26 - 31 for UNC drives.
//
if ( Found == TRUE ) {
DRIVE Drive;
DRIVE NextUncDrive = 26;
if ( NetResource->dwType != RESOURCETYPE_DISK ) {
return;
}
if ( NetResource->lpLocalName != NULL) {
Drive = NetResource->lpLocalName[0] - L'A';
} else {
if ( NextUncDrive < MD ) {
Drive = NextUncDrive++;
} else {
//
// No room in the table for this UNC drive.
//
return;
}
}
//
// We have a drive and a connection. Complete the table
// mappings.
//
pNwDosTable->DriveIdTable[ Drive ] = Connection + 1;
pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
}
}
VOID
InitDosTable(
PNWDOSTABLE pdt
)
/*++
Routine Description:
This routine Initializes the NetWare Dos Table to its empty values.
Arguments:
pdt - Supplies the table to be initialized.
Return Value:
None
--*/
{
ZeroMemory( ServerHandles, sizeof(ServerHandles) );
ZeroMemory( Drives, sizeof(Drives) );
ZeroMemory( (PVOID) pdt, sizeof(NWDOSTABLE) );
ZeroMemory( Win32DirectoryHandleTable, sizeof(Win32DirectoryHandleTable) );
FillMemory( SearchDriveTable, sizeof(SearchDriveTable), 0xff );
}
UCHAR CpuInProtectMode;
VOID
Nw16Handler(
VOID
)
/*++
Routine Description:
This function is called by wow when nw16.sys traps an Int and
bop's into 32 bit mode.
Arguments:
Return Value:
None,
--*/
{
USHORT Command;
WORD offset;
//
// get the CPU mode once: the memory references it's required for won't
// change during this call. Cuts down number of calls to getMSW()
//
CpuInProtectMode = IS_PROTECT_MODE();
setCF(0);
if ( TablesValid == FALSE ) {
//
// Load the tables unless the process is exiting.
//
if ((pNwDosTable->SavedAx & 0xff00) != 0x4c00) {
Nw16Register();
}
#if NWDBG
if (GotDebugState == FALSE) {
WCHAR Value[80];
if (GetEnvironmentVariableW(L"NWDEBUG",
Value,
sizeof(Value)/sizeof(Value[0]) - 1)) {
DebugCtrl = Value[0] - '0';
// 0 Use logfile
// 1 Use debugger
// 2/undefined No debug output
// 4 Use logfile, close on process exit
// 8 Use logfile, verbose, close on process exit
DebugControl( DebugCtrl );
}
GotDebugState = TRUE; // Don't look again until process exits vdm
}
#endif
}
//
// Normal AX register is used to get into 32 bit code so get applications
// AX from the shared datastructure.
//
Command = pNwDosTable->SavedAx;
//
// set AX register so that AH gets preserved
//
setAX( Command );
NwPrint(("Nw16Handler process command %x\n", Command ));
VrDumpRealMode16BitRegisters( FALSE );
VrDumpNwData();
switch (Command & 0xff00) {
case 0x3C00:
case 0x3D00:
OpenCreateFile();
break;
case 0x4C00:
ProcessExit(); // Close all handles
goto default_dos_handler; // Let Dos handle rest of processing
break;
case 0x9f00:
OpenQueueFile();
break;
case 0xB300: // Packet Signing
setAL(0); // not supported
break;
case 0xB400:
AttachHandle();
break;
case 0xB500:
switch (Command & 0x00ff) {
case 03:
setAX((WORD)pNwDosTable->TaskModeByte);
break;
case 04:
setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
setBX((WORD)(DosTableOffset + &((PNWDOSTABLE)0)->TaskModeByte));
break;
case 06:
setAX(2);
break;
default:
goto default_dos_handler;
}
break;
case 0xB800: // Capture - Not supported
setAL(0xff);
setCF(1);
break;
case 0xBB00: // Set EOJ status
{
static UCHAR EOJstatus = 1;
setAL(EOJstatus);
EOJstatus = pNwDosTable->SavedAx & 0x00ff;
}
break;
case 0xBC00:
case 0xBD00:
case 0xBE00:
case 0xC200:
case 0xC300:
case 0xC400:
case 0xC500:
case 0xC600:
Locks(Command);
break;
case 0xC700:
TTS();
break;
case 0xCB00:
case 0xCD00:
case 0xCF00:
case 0xD000:
case 0xD100:
case 0xD200:
case 0xD300:
case 0xD400:
case 0xD500:
Locks(Command);
break;
case 0xD700:
SystemLogout();
break;
case 0xDB00:
{
UCHAR Drive;
UCHAR Count = 0;
for (Drive = 0; Drive < MD; Drive++) {
if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE ) {
Count++;
}
}
setAL(Count);
}
break;
case 0xDC00: // Get station number
{
CONN_INDEX Connection = SelectConnection();
if (Connection == 0xff) {
setAL(0xff);
setCF(1);
} else {
PCONNECTIONID pConnection =
&pNwDosTable->ConnectionIdTable[Connection];
setAL(pConnection->ci_ConnectionLo);
setAH(pConnection->ci_ConnectionHi);
setCH( (UCHAR)((pConnection->ci_ConnectionHi == 0) ?
pConnection->ci_ConnectionLo / 10 + '0':
'X'));
setCL((UCHAR)(pConnection->ci_ConnectionLo % 10 + '0'));
}
}
break;
case 0xDD00: // Set NetWare Error mode
{
static UCHAR ErrorMode = 0;
setAL( ErrorMode );
ErrorMode = getDL();
}
break;
case 0xDE00:
{
static UCHAR BroadCastMode = 0;
UCHAR OpCode = getDL();
if ( OpCode < 4) {
BroadCastMode = OpCode;
}
setAL(BroadCastMode);
}
break;
case 0xDF00: // Capture - Not supported
setAL(0xff);
setCF(1);
break;
case 0xE000:
case 0xE100:
case 0xE300:
SendNCP(Command);
break;
case 0xE200:
AllocateDirectoryHandle();
break;
case 0xE700:
GetServerDateAndTime();
break;
case 0xE900:
switch (Command & 0x00ff) {
PUCHAR ptr;
case 0:
GetDirectoryHandle();
break;
case 1:
ptr = GetVDMPointer (
(ULONG)((getDS() << 16)|getDX()),
sizeof(SearchDriveTable),
CpuInProtectMode
);
RtlMoveMemory( ptr, SearchDriveTable, sizeof(SearchDriveTable) );
break;
case 2:
ptr = GetVDMPointer (
(ULONG)((getDS() << 16)|getDX()),
sizeof(SearchDriveTable),
CpuInProtectMode
);
RtlMoveMemory( SearchDriveTable, ptr, sizeof(SearchDriveTable) );
break;
case 5:
AllocateDirectoryHandle2();
break;
case 7:
setAL(0xff); // Drive depth not yet implemented
break;
#ifdef NWDBG
// Debug control
case 0xf0: // Use logfile
case 0xf1: // Use debugger
case 0xf2: // No debug output
DebugControl(Command & 0x000f);
break;
#endif
default:
NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
}
break;
case 0xEA00:
GetShellVersion(Command);
break;
case 0xEB00:
case 0xEC00:
case 0xED00:
Locks(Command);
break;
case 0xEF00:
NwPrint(("Nw32: %x\n", pNwDosTable->SavedAx ));
switch (Command & 0xff) {
case 00:
if (DriveHandleTableValid == FALSE) {
LoadDriveHandleTable();
}
offset = (WORD)&((PNWDOSTABLE)0)->DriveHandleTable;
break;
case 01:
offset = (WORD)&((PNWDOSTABLE)0)->DriveFlagTable;
break;
case 02:
offset = (WORD)&((PNWDOSTABLE)0)->DriveIdTable;
break;
case 03:
offset = (WORD)&((PNWDOSTABLE)0)->ConnectionIdTable;
break;
case 04:
offset = (WORD)&((PNWDOSTABLE)0)->ServerNameTable;
break;
default:
goto default_dos_handler;
}
setSI((WORD)(DosTableOffset + offset));
setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
setAL(0);
break;
case 0xF100:
setAL(AttachmentControl(Command));
break;
case 0xF200:
SendF2NCP(Command);
break;
case 0xF300:
ServerFileCopy();
break;
default:
default_dos_handler:
NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
//
// if we don't handle this call, we modify the return ip to point to
// code that will restore the stack and jump far into dos
//
setIP((WORD)(getIP() + 3));
}
#if NWDBG
pNwDosTable->SavedAx = getAX();
#endif
VrDumpRealMode16BitRegisters( FALSE );
}
CONN_INDEX
SelectConnection(
VOID
)
/*++
Routine Description:
Pick target connection for current transaction
Arguments:
None
Return Value:
Index into ConnectionIdTable or 0xff,
--*/
{
UCHAR IndexConnection;
if ( pNwDosTable->PreferredServer != 0 ) {
return(pNwDosTable->PreferredServer - 1);
}
// select default server if current drive is mapped by us?
if ( pNwDosTable->PrimaryServer != 0 ) {
return(pNwDosTable->PrimaryServer - 1);
}
// Need to pick another
for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
pNwDosTable->PrimaryServer = IndexConnection + 1;
return(pNwDosTable->PrimaryServer - 1);
}
}
// No servers in the table so find the nearest/preferred.
LoadPreferredServerName();
return(pNwDosTable->PrimaryServer - 1);
}
CONN_INDEX
SelectConnectionInCWD(
VOID
)
/*++
Routine Description:
Pick target connection for current transaction. Give preference to
the current working directory.
Arguments:
None
Return Value:
Index into ConnectionIdTable or 0xff,
--*/
{
UCHAR IndexConnection;
CHAR CurDir[256];
USHORT Drive;
// Try to return the connection for CWD first.
if ((GetCurrentDirectoryA(sizeof(CurDir)-1, CurDir) >= 2) &&
(CurDir[1] = ':')) {
Drive = tolower(CurDir[0]) - 'a';
}
IndexConnection = pNwDosTable->DriveIdTable[ Drive ] - 1 ;
if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
return IndexConnection ;
}
if ( pNwDosTable->PreferredServer != 0 ) {
return(pNwDosTable->PreferredServer - 1);
}
if ( pNwDosTable->PrimaryServer != 0 ) {
return(pNwDosTable->PrimaryServer - 1);
}
// Need to pick another
for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
pNwDosTable->PrimaryServer = IndexConnection + 1;
return(pNwDosTable->PrimaryServer - 1);
}
}
// No servers in the table so find the nearest/preferred.
LoadPreferredServerName();
return(pNwDosTable->PrimaryServer - 1);
}
VOID
SendNCP(
ULONG Command
)
/*++
Routine Description:
Implement generic Send NCP function.
ASSUMES called from Nw16Handler
Arguments:
Command - Supply the opcode 0xexxx
DS:SI - Supply Request buffer & length
ES:DI - Supply Reply buffer & length
On return AL = Status of operation.
Return Value:
None.
--*/
{
PUCHAR Request, Reply;
ULONG RequestLength, ReplyLength;
UCHAR OpCode;
OpCode = (UCHAR)((Command >> 8) - 0xcc);
Request = GetVDMPointer (
(ULONG)((getDS() << 16)|getSI()),
sizeof(WORD),
CpuInProtectMode
);
Reply = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()),
sizeof(WORD),
CpuInProtectMode
);
RequestLength = *(WORD UNALIGNED*)Request;
ReplyLength = *(WORD UNALIGNED*)Reply;
Request = GetVDMPointer (
(ULONG)((getDS() << 16)|getSI() + sizeof(WORD)),
(USHORT)RequestLength,
CpuInProtectMode
);
Reply = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()) + sizeof(WORD),
(USHORT)ReplyLength,
CpuInProtectMode
);
NwPrint(("SubRequest %x, RequestLength %x\n", Request[0], RequestLength ));
SendNCP2( NWR_ANY_NCP(OpCode ),
Request,
RequestLength,
Reply,
ReplyLength);
}
VOID
SendF2NCP(
ULONG Command
)
/*++
Routine Description:
Implement generic Send NCP function. No length to be inseted by
the redirector in the request buffer.
ASSUMES called from Nw16Handler
Arguments:
Command - Supply the opcode 0xf2xx
DS:SI CX - Supply Request buffer & length
ES:DI DX - Supply Reply buffer & length
On return AL = Status of operation.
Return Value:
None.
--*/
{
PUCHAR Request, Reply;
ULONG RequestLength, ReplyLength;
UCHAR OpCode;
OpCode = (UCHAR)(Command & 0x00ff);
RequestLength = getCX();
ReplyLength = getDX();
Request = GetVDMPointer (
(ULONG)((getDS() << 16)|getSI()),
(USHORT)RequestLength,
CpuInProtectMode
);
Reply = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()),
(USHORT)ReplyLength,
CpuInProtectMode
);
NwPrint(("F2SubRequest %x, RequestLength %x\n", Request[2], RequestLength ));
#if 0
if ((RequestLength != 0) &&
(OpCode == 0x17)) {
if ((Request[2] == 0x17) ||
(Request[2] == 0x18)) {
//
// The request was for an encryption key. Tell the
// application that encryption is not supported.
//
setAL(0xfb);
return;
} else if ((Request[2] == 0x14 ) ||
(Request[2] == 0x3f )) {
//
// Plaintext login or Verify Bindery Object Password.
// Convert to its WNET equivalent version.
//
UCHAR Name[256];
UCHAR Password[256];
UCHAR ServerName[sizeof(SERVERNAME)+3];
PUCHAR tmp;
CONN_INDEX Connection;
NETRESOURCEA Nr;
Connection = SelectConnection();
if ( Connection == 0xff ) {
setAL(0xff);
setCF(1);
return;
}
ZeroMemory( &Nr, sizeof(NETRESOURCE));
ServerName[0] = '\\';
ServerName[1] = '\\';
RtlCopyMemory( ServerName+2, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
ServerName[sizeof(ServerName)-1] = '\0';
Nr.lpRemoteName = ServerName;
Nr.dwType = RESOURCETYPE_DISK;
// point to password length.
tmp = &Request[6] + Request[5];
Name[Request[5]] = '\0';
RtlMoveMemory( Name, &Request[6], Request[5]);
Password[tmp[0]] = '\0';
RtlMoveMemory( Password, tmp+1, tmp[0]);
NwPrint(("Connect to %s as %s password %s\n", ServerName, Name, Password ));
if (NO_ERROR == WNetAddConnection2A( &Nr, Password, Name, 0)) {
setAL(0);
} else {
setAL(255);
}
return;
}
}
#endif
SendNCP2( NWR_ANY_F2_NCP(OpCode ),
Request,
RequestLength,
Reply,
ReplyLength);
}
VOID
SendNCP2(
ULONG Command,
PUCHAR Request,
ULONG RequestLength,
PUCHAR Reply,
ULONG ReplyLength
)
/*++
Routine Description:
Pick target connection for current transaction
This routine effectively opens a handle for each NCP sent. This means that
we don't keep handles open to servers unnecessarily which would cause
problems if a user tries to delete the connection or stop the workstation.
If this causes to much of a load then the fallback is to spin off a thread
that waits on an event with a timeout and periodically sweeps the
server handle table removing stale handles. Setting the event would cause
the thread to exit. Critical sections would need to be added to protect
handles. Dll Init/exit routine to kill the thread and close the handles
would also be needed.
Arguments:
Command - Supply the opcode
Request, RequestLength - Supply Request buffer & length
Reply, ReplyLength - Supply Reply buffer & length
On return AL = Status of operation.
Return Value:
None.
--*/
{
CONN_INDEX Connection = SelectConnection();
NTSTATUS status;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE Handle;
NwPrint(("Send NCP %x to %d:%s\n", Command, Connection, pNwDosTable->ServerNameTable[Connection] ));
NwPrint(("RequestLength %x\n", RequestLength ));
NwPrint(("Reply %x, ReplyLength %x\n", Reply, ReplyLength ));
if (Connection == 0xff) {
setAL(0xff);
setCF(1);
return;
};
if ( ServerHandles[Connection] == NULL ) {
status = OpenConnection( Connection );
if (!NT_SUCCESS(status)) {
SetStatus(status);
return;
} else {
InitConnection( Connection );
}
}
Handle = ServerHandles[Connection];
//
// If its a CreateJobandFile NCP then we need to use the handle
// created through Dos so that the writes go into the spoolfile created
// by this NCP.
//
if (Command == NWR_ANY_F2_NCP(0x17)) {
if ((Request[2] == 0x68) ||
(Request[2] == 0x79)) {
Handle = GET_NT_HANDLE();
}
} else if (Command == NWR_ANY_NCP(0x17)) {
if ((Request[0] == 0x68) ||
(Request[0] == 0x79)) {
Handle = GET_NT_HANDLE();
}
}
FormattedDump( Request, RequestLength );
//
// Make the NCP request on the appropriate handle
//
status = NtFsControlFile(
Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
Command,
(PVOID) (Request),
RequestLength,
(PVOID) Reply,
ReplyLength);
if (NT_SUCCESS(status)) {
status = IoStatusBlock.Status;
FormattedDump( Reply, ReplyLength );
}
if (!NT_SUCCESS(status)) {
SetStatus(status);
setCF(1);
NwPrint(("NtStatus %x, DosError %x\n", status, getAL() ));
} else {
setAL(0);
}
}
NTSTATUS
OpenConnection(
CONN_INDEX Connection
)
/*++
Routine Description:
Open the handle to the redirector to access the specified server.
Arguments:
Connection - Supplies the index to use for the handle
Return Value:
Status of the operation
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
LPWSTR FullName;
UCHAR AnsiName[SERVERNAME_LENGTH+sizeof(UCHAR)];
UNICODE_STRING UServerName;
OEM_STRING AServerName;
if ( Connection >= MC) {
return( BASE_DOS_ERROR + 249 ); // No free connection slots
}
if (ServerHandles[Connection] != NULL ) {
CloseConnection(Connection);
}
FullName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
sizeof( DD_NWFS_DEVICE_NAME_U ) +
(SERVERNAME_LENGTH + 1) * sizeof(WCHAR)
);
if ( FullName == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
CopyMemory(AnsiName,
pNwDosTable->ServerNameTable[Connection],
SERVERNAME_LENGTH);
AnsiName[SERVERNAME_LENGTH] = '\0';
RtlInitAnsiString( &AServerName, AnsiName );
Status = RtlOemStringToUnicodeString( &UServerName,
&AServerName,
TRUE);
if (!NT_SUCCESS(Status)) {
LocalFree( FullName );
return(Status);
}
wcscpy( FullName, DD_NWFS_DEVICE_NAME_U );
wcscat( FullName, L"\\");
wcscat( FullName, UServerName.Buffer );
RtlFreeUnicodeString(&UServerName);
RtlInitUnicodeString( &UServerName, FullName );
InitializeObjectAttributes(
&ObjectAttributes,
&UServerName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
//
// Open a handle to the server.
//
//
// Try to login to the nearest server. This is necessary for
// the real preferred server if there are no redirections to
// it. The rdr can logout and disconnect. SYSCON doesn't like
// running from such a server.
//
Status = NtOpenFile(
&ServerHandles[Connection],
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if ( NT_SUCCESS(Status)) {
Status = IoStatusBlock.Status;
}
if (!NT_SUCCESS(Status)) {
//
// Failed to login. Use the non-login method. This allows the
// app to do a bindery login or query the bindery.
//
Status = NtOpenFile(
&ServerHandles[Connection],
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if ( NT_SUCCESS(Status)) {
Status = IoStatusBlock.Status;
}
}
NwPrint(("Nw16:OpenConnection %d: %wZ status = %08lx\n", Connection, &UServerName, Status));
LocalFree( FullName );
if (!NT_SUCCESS(Status)) {
SetStatus(Status);
return Status;
}
return Status;
}
VOID
CloseConnection(
CONN_INDEX Connection
)
/*++
Routine Description:
Close the connection handle
Arguments:
Connection - Supplies the index to use for the handle
Return Value:
None.
--*/
{
if (ServerHandles[Connection]) {
NwPrint(("CloseConnection: %d\n",Connection));
NtClose(ServerHandles[Connection]);
ServerHandles[Connection] = NULL;
}
}
NTSTATUS
InitConnection(
CONN_INDEX Connection
)
/*++
Routine Description:
Get the connection status from the redirector.
Arguments:
Connection - Supplies the index to use for the handle
Return Value:
Status of the operation
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
NWR_GET_CONNECTION_DETAILS Details;
Status = NtFsControlFile(
ServerHandles[Connection],
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_NWR_GET_CONN_DETAILS,
NULL,
0,
(PVOID) &Details,
sizeof(Details));
if (Status == STATUS_SUCCESS) {
Status = IoStatusBlock.Status;
}
NwPrint(("Nw16:InitConnection: %d status = %08lx\n",Connection, Status));
if (!NT_SUCCESS(Status)) {
SetStatus(Status);
CloseConnection(Connection);
} else {
PCONNECTIONID pConnection =
&pNwDosTable->ConnectionIdTable[Connection];
pConnection->ci_OrderNo= Details.OrderNumber;
CopyMemory(pNwDosTable->ServerNameTable[Connection],
Details.ServerName,
sizeof(SERVERNAME));
CopyMemory(pConnection->ci_ServerAddress,
Details.ServerAddress,
sizeof(pConnection->ci_ServerAddress));
pConnection->ci_ConnectionNo= Details.ConnectionNumberLo;
pConnection->ci_ConnectionLo= Details.ConnectionNumberLo;
pConnection->ci_ConnectionHi= Details.ConnectionNumberHi;
pConnection->ci_MajorVersion= Details.MajorVersion;
pConnection->ci_MinorVersion= Details.MinorVersion;
pConnection->ci_InUse = IN_USE;
pConnection->ci_1 = 0;
pConnection->ci_ConnectionStatus = 2;
//
// If this is the preferred conection then record it as special.
// If this is the first drive then also record it. Usually it gets
// overwritten by the preferred.
//
if (( Details.Preferred ) ||
( OriginalPrimary == 0 )) {
NwPrint(("Nw16InitConnection: Primary Connection is %d\n", Connection+1));
pNwDosTable->PrimaryServer = OriginalPrimary = (UCHAR)Connection + 1;
}
setAL(0);
}
return Status;
}
VOID
GetDirectoryHandle(
VOID
)
/*++
Routine Description:
Obtain a NetWare handle for the current directory.
If a NetWare handle is assigned then the Win32 handle created for
the directory handle is kept open. When the process exits, the Win32
handle will close. When all the Win32 handles from this process are
closed an endjob NCP will be sent freeing the directory handle on the
server.
Arguments:
DX supplies the drive.
AL returns the handle.
AH returns the status flags.
Return Value:
None.
--*/
{
USHORT Drive = getDX();
NwPrint(("Nw32:GetDirectoryHandle %c: ", 'A' + Drive));
GetDirectoryHandle2( Drive );
setAL(pNwDosTable->DriveHandleTable[Drive]);
setAH(pNwDosTable->DriveFlagTable[Drive]);
NwPrint(("Handle = %x, Flags =%x\n", pNwDosTable->DriveHandleTable[Drive],
pNwDosTable->DriveFlagTable[Drive] ));
}
ULONG
GetDirectoryHandle2(
DWORD Drive
)
/*++
Routine Description:
Obtain a NetWare handle for the current directory.
If a NetWare handle is assigned then the Win32 handle created for
the directory handle is kept open. When the process exits, the Win32
handle will close. When all the Win32 handles from this process are
closed an endjob NCP will be sent freeing the directory handle on the
server.
Note: Updates DriveHandleTable.
Arguments:
Drive supplies the drive index (0 = a:).
Return Value:
returns the handle.
--*/
{
DWORD BytesReturned;
if (Drive >= MD) {
setAL( 0x98 ); // Volume does not exist
return 0xffffffff;
}
NwPrint(("Nw32:GetDirectoryHandle2 %c:\n", 'A' + Drive));
//
// If we don't have a handle and its either a temporary or
// permanent drive then create it.
//
if (( Win32DirectoryHandleTable[Drive] == 0 ) &&
( (pNwDosTable->DriveFlagTable[Drive] & 3) != 0 )){
WCHAR DriveString[4];
PWCHAR Name;
//
// We don't have a handle for this drive.
// Open an NT handle to the current directory and
// ask the redirector for a NetWare directory handle.
//
if (Drive <= ('Z' - 'A')) {
DriveString[0] = L'A' + (WCHAR)Drive;
DriveString[1] = L':';
DriveString[2] = L'.';
DriveString[3] = L'\0';
Name = DriveString;
} else {
Name = Drives[Drive];
if( Name == NULL ) {
NwPrint(("\nNw32:GetDirectoryHandle2 Drive not mapped\n",0));
return 0xffffffff;
}
}
Win32DirectoryHandleTable[Drive] =
CreateFileW( Name,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
0);
if (Win32DirectoryHandleTable[Drive] != INVALID_HANDLE_VALUE) {
if ( DeviceIoControl(
Win32DirectoryHandleTable[Drive],
IOCTL_NWR_RAW_HANDLE,
NULL,
0,
(PUCHAR)&pNwDosTable->DriveHandleTable[Drive],
sizeof(pNwDosTable->DriveHandleTable[Drive]),
&BytesReturned,
NULL ) == FALSE ) {
NwPrint(("\nNw32:GetDirectoryHandle2 DeviceIoControl %x\n", GetLastError()));
CloseHandle( Win32DirectoryHandleTable[Drive] );
Win32DirectoryHandleTable[Drive] = 0;
return 0xffffffff;
} else {
ASSERT( BytesReturned == sizeof(pNwDosTable->DriveHandleTable[Drive]));
NwPrint(("\nNw32:GetDirectoryHandle2 Success %x\n", pNwDosTable->DriveHandleTable[Drive]));
}
} else {
NwPrint(("\nNw32:GetDirectoryHandle2 CreateFile %x\n", GetLastError()));
Win32DirectoryHandleTable[Drive] = 0;
return 0xffffffff;
}
}
return (ULONG)pNwDosTable->DriveHandleTable[Drive];
}
VOID
LoadDriveHandleTable(
VOID
)
/*++
Routine Description:
Open handles to all the NetWare drives
Arguments:
none.
Return Value:
none.
--*/
{
USHORT Drive;
for (Drive = 0; Drive < MD; Drive++ ) {
GetDirectoryHandle2(Drive);
}
DriveHandleTableValid = TRUE;
}
VOID
AllocateDirectoryHandle(
VOID
)
/*++
Routine Description:
Allocate permanent or temporary handle for drive.
For a permanent handle, we map this to a "net use".
ASSUMES called from Nw16Handler
Arguments:
DS:SI supplies the request.
ES:DI supplies the response.
AL returns the completion code.
Return Value:
None.
--*/
{
PUCHAR Request=GetVDMPointer (
(ULONG)((getDS() << 16)|getSI()),
2,
CpuInProtectMode
);
PUCHAR Reply = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()),
4,
CpuInProtectMode
);
USHORT RequestLength = *(USHORT UNALIGNED *)( Request );
Request=GetVDMPointer (
(ULONG)((getDS() << 16)|getSI()),
RequestLength+2,
CpuInProtectMode
);
FormattedDump( Request, RequestLength+2 );
if (( Request[2] == 0x12) ||
( Request[2] == 0x13)) {
// do temp drives need different handling?
UCHAR Drive = Request[4] - 'A';
if (Drive >= MD) {
setAL( 0x98 ); // Volume does not exist
return;
}
if (pNwDosTable->DriveHandleTable[Drive] != 0) {
NwPrint(("Nw32: Move directory handle %d\n", Drive));
//
// We already have a directory handle assigned for this
// process. Ask the server to point the handle at the requested
// position.
//
SendNCP2(FSCTL_NWR_NCP_E2H, Request+2, RequestLength, Reply+2, 2);
if (getAL() == 0) {
// Record the new handle.
pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
if (Request[2] == 0x12) {
pNwDosTable->DriveFlagTable[ Drive ] =
PERMANENT_NETWORK_DRIVE;
} else {
pNwDosTable->DriveFlagTable[ Drive ] =
TEMPORARY_NETWORK_DRIVE;
}
pNwDosTable->DriveHandleTable[Drive] = Reply[2];
NwPrint(("Nw32: Move directory handle -> %x\n", Reply[2]));
}
} else {
NETRESOURCE Nr;
WCHAR DriveString[3];
ULONG Handle;
if (Request[2] == 0x12) {
NwPrint(("Nw32: Allocate permanent directory handle %d\n", Drive));
} else {
NwPrint(("Nw32: Allocate temporary directory handle %d\n", Drive));
}
if (Drives[Drive] != NULL) {
// Tidy up the old name for this drive.
LocalFree( Drives[Drive] );
Drives[Drive] = NULL;
}
DriveString[0] = L'A' + Drive; // A through Z
DriveString[1] = L':';
DriveString[2] = L'\0';
//
// This is effectively a net use!
//
ZeroMemory( &Nr, sizeof(NETRESOURCE));
Nr.lpRemoteName = BuildUNC(&Request[6], Request[5]);
Nr.dwType = RESOURCETYPE_DISK;
// Save where this drive points.
Drives[Drive] = Nr.lpRemoteName;
if (DriveString[0] <= L'Z') {
Nr.lpLocalName = DriveString;
if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
NwPrint(("Nw32: Allocate ->%d\n", GetLastError()));
setAL(0x98); // Volume does not exist
return;
}
}
if (Request[2] == 0x12) {
pNwDosTable->DriveFlagTable[ Drive ] =
PERMANENT_NETWORK_DRIVE;
} else {
pNwDosTable->DriveFlagTable[ Drive ] =
TEMPORARY_NETWORK_DRIVE;
}
Handle = GetDirectoryHandle2( Drive );
if (Handle == 0xffffffff) {
if (DriveString[0] <= L'Z') {
WNetCancelConnection2W( DriveString, 0, TRUE);
}
ResetDrive( Drive );
setAL(0x9c); // Invalid path
} else {
//
// We have a drive and a connection. Complete the table
// mappings.
//
pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
Reply[2] = (UCHAR)(Handle & 0xff);
Reply[3] = (UCHAR)(0xff); //should be effective rights
setAL(0); // Successful
}
}
} else if ( Request[2] == 0x14 ) {
UCHAR DirHandle = Request[3];
UCHAR Drive;
CONN_INDEX Connection = SelectConnection();
NwPrint(("Nw32: Deallocate directory handle %d on Connection %d\n", DirHandle, Connection));
for (Drive = 0; Drive < MD; Drive++) {
NwPrint(("Nw32: Drive %c: is DirHandle %d, Connection %d\n",
'A' + Drive,
pNwDosTable->DriveHandleTable[Drive],
pNwDosTable->DriveIdTable[ Drive ]-1 ));
if ((pNwDosTable->DriveHandleTable[Drive] == DirHandle) &&
(pNwDosTable->DriveIdTable[ Drive ] == Connection+1)) {
//
// This is effectively a net use del!
//
NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
ResetDrive(Drive);
setAL(0);
return;
}
}
setAL(0x9b); // Bad directory handle
return;
} else {
SendNCP(pNwDosTable->SavedAx);
}
FormattedDump( Reply, Reply[0] );
}
VOID
ResetDrive(
UCHAR Drive
)
/*++
Routine Description:
Do a net use del
Arguments:
Drive - Supplies the target drive.
Return Value:
None.
--*/
{
NwPrint(("Nw32: Reset Drive %c:\n", 'A' + Drive ));
if ((pNwDosTable->DriveFlagTable[ Drive ] &
( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE )) == 0) {
return;
}
if (Win32DirectoryHandleTable[Drive] != 0) {
CloseHandle( Win32DirectoryHandleTable[Drive] );
Win32DirectoryHandleTable[Drive] = 0;
}
if (Drive <= (L'Z' - L'A')) {
DWORD WStatus;
WCHAR DriveString[3];
DriveString[0] = L'A' + Drive;
DriveString[1] = L':';
DriveString[2] = L'\0';
WStatus = WNetCancelConnection2W( DriveString, 0, TRUE);
if( WStatus != NO_ERROR ) {
NwPrint(("Nw32: WNetCancelConnection2W failed %d\n", WStatus ));
}
}
// Turn off flags that show this drive as redirected
pNwDosTable->DriveFlagTable[ Drive ] &=
~( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE );
pNwDosTable->DriveHandleTable[Drive] = 0;
}
VOID
AllocateDirectoryHandle2(
VOID
)
/*++
Routine Description:
Allocate root drive
ASSUMES called from Nw16Handler
Arguments:
BL supplies drive to map.
DS:DX supplies the pathname
AL returns the completion code.
Return Value:
None.
--*/
{
UCHAR Drive = getBL()-1;
PUCHAR Name=GetVDMPointer (
(ULONG)((getDS() << 16)|getDX()),
256, // longest valid path
CpuInProtectMode
);
NETRESOURCE Nr;
WCHAR DriveString[3];
ULONG Handle;
NwPrint(("Nw32: e905 map drive %c to %s\n", Drive + 'A', Name ));
if (Drive >= MD) {
setAL( 0x98 ); // Volume does not exist
setCF(1);
return;
}
if (pNwDosTable->DriveHandleTable[Drive] != 0) {
NwPrint(("Nw32: Drive already redirected\n"));
ResetDrive(Drive);
}
NwPrint(("Nw32: Allocate permanent directory handle\n"));
if (Drives[Drive] != NULL) {
// Tidy up the old name for this drive.
LocalFree( Drives[Drive] );
Drives[Drive] = NULL;
}
//
// This is effectively a net use!
//
ZeroMemory( &Nr, sizeof(NETRESOURCE));
Nr.lpRemoteName = BuildUNC( Name, strlen(Name));
// Save where this drive points.
Drives[Drive] = Nr.lpRemoteName;
if (Drive <= (L'Z' - L'A')) {
DriveString[0] = L'A' + Drive; // A through Z
DriveString[1] = L':';
DriveString[2] = L'\0';
Nr.lpLocalName = DriveString;
Nr.dwType = RESOURCETYPE_DISK;
if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
NwPrint(("Nw32: Allocate0 ->%d\n", GetLastError()));
if (GetLastError() == ERROR_ALREADY_ASSIGNED) {
WNetCancelConnection2W( DriveString, 0, TRUE);
if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
NwPrint(("Nw32: Allocate1 ->%d\n", GetLastError()));
ResetDrive( Drive );
setAL(0x03); // Volume does not exist
setCF(1);
return;
}
} else {
NwPrint(("Nw32: Allocate2 ->%d\n", GetLastError()));
ResetDrive( Drive );
setAL(0x03); // Volume does not exist
setCF(1);
return;
}
}
}
//
// Set flags so that GetDirectory2 will open handle
//
pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
Handle = GetDirectoryHandle2( Drive );
if (Handle == 0xffffffff) {
ResetDrive( Drive );
setAL(0x03); // Invalid path
setCF(1);
} else {
setAL(0); // Successful
}
NwPrint(("Nw32: Returning %x\n",getAL()));
}
PWCHAR
BuildUNC(
IN PUCHAR aName,
IN ULONG aLength
)
/*++
Routine Description:
This routine takes the ansi name, prepends the appropriate server name
(if appropriate) and converts to Unicode.
Arguments:
IN aName - Supplies the ansi name.
IN aLength - Supplies the ansi name length in bytes.
Return Value:
Unicode string
--*/
{
UNICODE_STRING Name;
UCHAR ServerName[sizeof(SERVERNAME)+1];
CONN_INDEX Connection;
ANSI_STRING TempAnsi;
UNICODE_STRING TempUnicode;
USHORT x;
// conversion rules for aName to Name are:
// foo: "\\server\foo\"
// foo:bar\baz "\\server\foo\bar\baz"
// foo:\bar\baz "\\server\foo\bar\baz"
#ifdef NWDBG
TempAnsi.Buffer = aName;
TempAnsi.Length = (USHORT)aLength;
TempAnsi.MaximumLength = (USHORT)aLength;
NwPrint(("Nw32: BuildUNC %Z\n", &TempAnsi));
#endif
Connection = SelectConnection();
if ( Connection == 0xff ) {
return NULL;
}
Name.MaximumLength = (USHORT)(aLength + sizeof(SERVERNAME) + 5) * sizeof(WCHAR);
Name.Buffer = (PWSTR)LocalAlloc( LMEM_FIXED, (ULONG)Name.MaximumLength);
if (Name.Buffer == NULL) {
return NULL;
}
Name.Length = 4;
Name.Buffer[0] = L'\\';
Name.Buffer[1] = L'\\';
//
// Be careful because ServerName might be 48 bytes long and therefore
// not null terminated.
//
RtlCopyMemory( ServerName, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
ServerName[sizeof(ServerName)-1] = '\0';
RtlInitAnsiString( &TempAnsi, ServerName );
RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE);
RtlAppendUnicodeStringToString( &Name, &TempUnicode );
RtlFreeUnicodeString( &TempUnicode );
// Now pack servername to volume seperator if necessary.
if ((aLength != 0) &&
(aName[0] != '\\')) {
RtlAppendUnicodeToString( &Name, L"\\");
}
// aName might not be null terminated so be careful creating TempAnsi
TempAnsi.Buffer = aName;
TempAnsi.Length = (USHORT)aLength;
TempAnsi.MaximumLength = (USHORT)aLength;
if (!NT_SUCCESS(RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE))) {
LocalFree( Name.Buffer );
return NULL;
}
RtlAppendUnicodeStringToString( &Name, &TempUnicode );
// If the name already has a volume seperator then don't add another.
for (x=0; x < (Name.Length/sizeof(WCHAR)) ; x++ ) {
if (Name.Buffer[x] == L':') {
// Strip the colon if it is immediately followed by a backslash
if (((Name.Length/sizeof(WCHAR))-1 > x) &&
(Name.Buffer[x+1] == L'\\')) {
RtlMoveMemory( &Name.Buffer[x],
&Name.Buffer[x+1],
Name.Length - ((x + 1) * sizeof(WCHAR)));
Name.Length -= sizeof(WCHAR);
} else {
// Replace the colon with a backslash
Name.Buffer[x] = L'\\';
}
goto skip;
}
}
skip:
RtlFreeUnicodeString( &TempUnicode );
// Strip trailing backslash if present.
if ((Name.Length >= sizeof(WCHAR) ) &&
(Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1 ] == L'\\')) {
Name.Length -= sizeof(WCHAR);
}
// Return pointer to a null terminated wide char string.
Name.Buffer[Name.Length/sizeof(WCHAR)] = L'\0';
NwPrint(("Nw32: BuildUNC %ws\n", Name.Buffer));
return Name.Buffer;
}
VOID
GetServerDateAndTime(
VOID
)
/*++
Routine Description:
Implement Funtion E7h
ASSUMES called from Nw16Handler
Arguments:
none.
Return Value:
none.
--*/
{
PUCHAR Reply = GetVDMPointer (
(ULONG)((getDS() << 16)|getDX()),
7,
CpuInProtectMode
);
SendNCP2( NWR_ANY_NCP(0x14), NULL, 0, Reply, 7 );
}
VOID
GetShellVersion(
IN USHORT Command
)
/*++
Routine Description:
Get the environment variables. Needs to be configurable for
Japanese machines.
Arguments:
Command supplies the callers AX.
Return Value:
none.
--*/
{
setAX(0); // MSDOS, PC
setBX(0x031a); // Shell version
setCX(0);
if ( (Command & 0x00ff) != 0) {
LONG tmp;
HKEY Key = NULL;
HINSTANCE hInst;
int retval;
PUCHAR Reply = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()),
40,
CpuInProtectMode
);
if ( Reply == NULL ) {
return;
}
hInst = GetModuleHandleA( "nwapi16.dll" );
if (hInst == NULL) {
return;
}
retval = LoadStringA( hInst, IsNEC_98 ? IDS_CLIENT_ID_STRING_NEC98 : IDS_CLIENT_ID_STRING, Reply, 40 );
//
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
// \NWCWorkstation\Parameters
//
tmp = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_WORKSTATION_REGKEY,
REG_OPTION_NON_VOLATILE, // options
KEY_READ, // desired access
&Key
);
if (tmp != ERROR_SUCCESS) {
return;
}
tmp = 40; // Max size for the string.
RegQueryValueExA(
Key,
"ShellVersion",
NULL,
NULL,
Reply,
&tmp);
ASSERT( tmp <= 40 );
RegCloseKey( Key );
}
}
#include <packon.h>
typedef struct _TTSOUTPACKETTYPE {
UCHAR SubFunction;
USHORT cx;
USHORT dx;
} TTSOUTPACKETTYPE;
typedef struct _TTSINPACKETTYPE {
USHORT cx;
USHORT dx;
} TTSINPACKETTYPE;
#include <packoff.h>
VOID
TTS(
VOID
)
/*++
Routine Description:
Transaction Tracking System
Arguments:
none.
Return Value:
none.
--*/
{
UCHAR bOutput;
UCHAR bInput[2];
TTSINPACKET TTsInPacket;
TTSOUTPACKET TTsOutPacket;
switch ( pNwDosTable->SavedAx & 0x00ff )
{
case 2:
// NCP Tts Available
bOutput = 0;
SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
if (getAL() == 0xFF) {
setAL(01);
}
break;
case 0:
// NCP Tts Begin/Abort
bOutput = 1;
SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
break;
case 3:
// NCP Tts Begin/Abort
bOutput = 3;
SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
break;
case 1:
// NCP Tts End
bOutput = 2;
SendNCP2( NWR_ANY_F2_NCP(0x22),
&bOutput, sizeof(UCHAR),
(PUCHAR)&TTsInPacket, sizeof(TTsInPacket));
setCX(TTsInPacket.cx);
setDX(TTsInPacket.dx);
break;
case 4:
// NCP Tts Status
TTsOutPacket.SubFunction = 4;
TTsOutPacket.cx = getCX();
TTsOutPacket.dx = getDX();
SendNCP2( NWR_ANY_F2_NCP(0x22),
(PUCHAR)&TTsOutPacket, sizeof(TTsOutPacket),
NULL, 0);
break;
case 5:
case 7:
// NCP Tts Get App/Station Thresholds
bOutput = (pNwDosTable->SavedAx & 0x00ff);
SendNCP2( NWR_ANY_F2_NCP(0x22),
&bOutput, sizeof(UCHAR),
bInput, sizeof(bInput));
setCX( (USHORT)((bInput[0] << 8 ) || bInput[1]) );
break;
case 6:
case 8:
// NCP Tts Set App/Station Thresholds
TTsOutPacket.SubFunction = (pNwDosTable->SavedAx & 0x00ff);
TTsOutPacket.cx = getCX();
SendNCP2( NWR_ANY_F2_NCP(0x22),
(PUCHAR)&TTsOutPacket, sizeof(UCHAR) + sizeof(USHORT),
NULL, 0);
break;
default:
pNwDosTable->SavedAx = 0xc7FF;
break;
}
return;
}
VOID
OpenCreateFile(
VOID
)
/*++
Routine Description:
Look at the file being opened to determine if it is
a compatibility mode open to a file on a NetWare drive.
Arguments:
none.
Return Value:
none.
--*/
{
WORD Command = pNwDosTable->SavedAx;
PUCHAR Name;
if ((Command & OF_SHARE_MASK ) != OF_SHARE_COMPAT) {
return;
}
Name = GetVDMPointer (
(ULONG)((getDS() << 16)|getDX()),
256,
CpuInProtectMode
);
NwPrint(("Nw16Handler Compatibility Open of %s\n", Name ));
//
// We already know its a Create or Open with sharing options
// set to compatibility mode or the tsr wouldn't have bopped to us.
//
if (IsItNetWare(Name)) {
SetCompatibility();
}
}
BOOL
IsItNetWare(
PUCHAR Name
)
/*++
Routine Description:
Look at the filename being opened to determine if it is on a NetWare drive.
Arguments:
none.
Return Value:
none.
--*/
{
UCHAR Drive;
Drive = tolower(Name[0])-'a';
NwPrint(("Nw16Handler IsItNetWare %s\n", Name ));
if (Name[1] == ':') {
if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE) {
// Definitely not a netware drive.
return FALSE;
}
} else if ((IS_ASCII_PATH_SEPARATOR(Name[0])) &&
(IS_ASCII_PATH_SEPARATOR(Name[0]))) {
// Assume only UNC names that the tsr built are NetWare
if ((getDS() == DosTableSegment ) &&
(getDX() == (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )))) {
return TRUE;
}
return FALSE;
} else {
Drive = pNwDosTable->CurrentDrive;
}
//
// If this is a drive we don't know about, refresh our tables.
//
if (pNwDosTable->DriveFlagTable[Drive] == 0 ) {
Nw16Register();
}
if (pNwDosTable->DriveFlagTable[Drive] &
(TEMPORARY_NETWORK_DRIVE | PERMANENT_NETWORK_DRIVE )) {
return TRUE;
}
return FALSE;
}
VOID
SetCompatibility(
VOID
)
/*++
Routine Description:
Take the Create/Open file request in AX and modify appropriately
Arguments:
none.
Return Value:
none.
--*/
{
WORD Command = getAX();
if (( Command & OF_READ_WRITE_MASK) == OF_READ ) {
setAX((WORD)(Command | OF_SHARE_DENY_WRITE));
} else {
setAX((WORD)(Command | OF_SHARE_EXCLUSIVE));
}
}
VOID
OpenQueueFile(
VOID
)
/*++
Routine Description:
Build the UNC filename \\server\queue using the contents of the shared
datastructures and the CreateJobandFile NCP.
Arguments:
none.
Return Value:
none.
--*/
{
CONN_INDEX Connection = SelectConnection();
PUCHAR Request;
PUCHAR Buffer = pNwDosTable->DeNovellBuffer;
int index;
if ( Connection == 0xff ) {
//
// No need to return an errorcode. The NCP exchange
// will fail and give an appropriate call to the application.
//
return;
}
if ( ServerHandles[Connection] == NULL ) {
NTSTATUS status;
status = OpenConnection( Connection );
if (!NT_SUCCESS(status)) {
SetStatus(status);
return;
}
}
//
// CreateJobandQueue open in progress. The purpose of this
// open being processed is to translate the information in
// the CreateJob NCP into a pathname to be opened by the 16
// bit code.
//
//
// Users DS:SI points at a CreateJob NCB. Inside the request is
// the objectid of the queue. Ask the server for the queue name.
//
Request = GetVDMPointer (
(ULONG)((getDS() << 16)|getSI()),
8,
CpuInProtectMode);
NwlibMakeNcp(
ServerHandles[Connection],
FSCTL_NWR_NCP_E3H,
7, // RequestSize
61, // ResponseSize
"br|_r",
0x36, // Get Bindery Object Name
Request+3, 4,
6, // Skip ObjectId and Type
pNwDosTable->DeNovellBuffer2, 48 );
pNwDosTable->DeNovellBuffer2[54] = '\0';
Buffer[0] = '\\';
Buffer[1] = '\\';
Buffer += 2; // Point to after backslashes
// Copy the servername
for (index = 0; index < sizeof(SERVERNAME); index++) {
Buffer[index] = pNwDosTable->ServerNameTable[Connection][index];
if (Buffer[index] == '\0') {
break;
}
}
Buffer[index] = '\\';
RtlCopyMemory( &Buffer[index+1], &pNwDosTable->DeNovellBuffer2[0], 48 );
NwPrint(("Nw32: CreateQueue Job and File %s\n", pNwDosTable->DeNovellBuffer));
//
// Set up 16 bit registers to do the DOS OpenFile for \\server\queue
//
setDS((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
setDX( (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )) );
setAX(0x3d02); // Set to OpenFile
}
VOID
AttachHandle(
VOID
)
/*++
Routine Description:
This routine implements Int 21 B4. Which is supposed to create a
Dos Handle that corresponds to a specified 6 byte NetWare handle.
This is used as a replacement for doing a DosOpen on "NETQ" and usin the
handle returned from there.
Arguments:
none.
Return Value:
none.
--*/
{
if ( pNwDosTable->CreatedJob ) {
NwPrint(("Nw32: AttachHandle %x\n", pNwDosTable->JobHandle));
setAX( pNwDosTable->JobHandle );
pNwDosTable->CreatedJob = 0; // Only return it once.
} else {
NwPrint(("Nw32: AttachHandle failed, no job\n"));
setAX(ERROR_FILE_NOT_FOUND);
setCF(1);
}
}
VOID
ProcessExit(
VOID
)
/*++
Routine Description:
Cleanup all cached handles. Unmap all temporary drives.
Cleanup the server name table so that if another dos app
is started we reload all the useful information such as
the servers connection number.
Note: Dos always completes processing after we complete.
Arguments:
none.
Return Value:
none.
--*/
{
UCHAR Connection;
UCHAR Drive;
USHORT Command = pNwDosTable->SavedAx;
ResetLocks();
for (Drive = 0; Drive < MD; Drive++) {
NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
if (Win32DirectoryHandleTable[Drive] != 0) {
CloseHandle( Win32DirectoryHandleTable[Drive] );
Win32DirectoryHandleTable[Drive] = 0;
pNwDosTable->DriveHandleTable[Drive] = 0;
}
}
for (Connection = 0; Connection < MC ; Connection++ ) {
if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
CloseConnection(Connection);
pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
}
}
pNwDosTable->PreferredServer = 0;
LockMode = 0;
TablesValid = FALSE;
DriveHandleTableValid = FALSE;
#if NWDBG
if (DebugCtrl & ~3 ) {
DebugControl( 2 ); // Close logfile
}
GotDebugState = FALSE;
#endif
//
// set AX register so that AH gets preserved
//
setAX( Command );
}
VOID
SystemLogout(
VOID
)
/*++
Routine Description:
This api is called by the NetWare login.
Remove all NetWare redirected drives and logout connections
that don't have open handles on them. Don't detach the connections.
Arguments:
none.
Return Value:
none.
--*/
{
UCHAR Connection;
UCHAR Drive;
USHORT Command = pNwDosTable->SavedAx;
ResetLocks();
for (Drive = 0; Drive < MD; Drive++) {
ResetDrive(Drive);
}
for (Connection = 0; Connection < MC ; Connection++ ) {
if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
if ( ServerHandles[Connection] == NULL ) {
OpenConnection( Connection );
}
if (ServerHandles[Connection] != NULL ) {
NwlibMakeNcp(
ServerHandles[Connection],
NWR_ANY_F2_NCP(NCP_LOGOUT),
0, // RequestSize
0, // ResponseSize
"");
CloseConnection(Connection);
}
//pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
//ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
}
}
pNwDosTable->PreferredServer = 0;
pNwDosTable->PrimaryServer = 0;
// No servers in the table so find the nearest/preferred.
LoadPreferredServerName();
//
// set AX register so that AH gets preserved
// and AL says success.
//
setAX( (USHORT)(Command & 0xff00) );
}
UCHAR
AttachmentControl(
ULONG Command
)
/*++
Routine Description:
Implement Funtion F1h
Arguments:
none.
Return Value:
Return status.
--*/
{
UCHAR Connection = getDL();
if ((Connection < 1) ||
(Connection > MC)) {
return 0xf7;
}
Connection -= 1;
switch (Command & 0x00ff) {
case 0: // Attach
NwPrint(("Nw16AttachmentControl: Attach connection %d\n", Connection));
pNwDosTable->ConnectionIdTable[Connection].ci_InUse = IN_USE;
if ( ServerHandles[Connection] == NULL ) {
NTSTATUS status = OpenConnection( Connection );
if (!NT_SUCCESS(status)) {
pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
return (UCHAR)RtlNtStatusToDosError(status);
} else {
InitConnection(Connection);
}
}
return 0;
break;
case 1: // Detach
NwPrint(("Nw16AttachmentControl: Detach connection %d\n", Connection));
if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
return 0xff;
} else {
pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
if (ServerHandles[Connection] != NULL ) {
CloseConnection(Connection);
}
ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
if (pNwDosTable->PrimaryServer == (UCHAR)Connection + 1 ) {
// Need to pick another
UCHAR IndexConnection;
pNwDosTable->PrimaryServer = 0;
for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
pNwDosTable->PrimaryServer = IndexConnection + 1;
}
}
}
if (pNwDosTable->PreferredServer == (UCHAR)Connection + 1 ) {
pNwDosTable->PreferredServer = 0;
}
return 0;
}
case 2: // Logout
NwPrint(("Nw16AttachmentControl: Logout connection %d\n", Connection));
if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
return 0xff;
} else {
UCHAR Drive;
if ( ServerHandles[Connection] == NULL ) {
OpenConnection( Connection );
}
for (Drive = 0; Drive < MD; Drive++ ) {
if (pNwDosTable->DriveIdTable[ Drive ] == (Connection + 1)) {
ResetDrive(Drive);
}
}
if (ServerHandles[Connection] != NULL ) {
NwlibMakeNcp(
ServerHandles[Connection],
NWR_ANY_F2_NCP(NCP_LOGOUT),
0, // RequestSize
0, // ResponseSize
"");
CloseConnection(Connection);
}
return 0;
}
}
return 0xff;
}
VOID
ServerFileCopy(
VOID
)
/*++
Routine Description:
Build the NCP that tells the server to move a file on the server.
Arguments:
none.
Return Value:
none.
--*/
{
DWORD BytesReturned;
UCHAR SrcHandle[6];
UCHAR DestHandle[6];
NTSTATUS status;
PUCHAR Buffer;
Buffer = GetVDMPointer (
(ULONG)((getES() << 16)|getDI()),
16,
CpuInProtectMode
);
if ( DeviceIoControl(
GET_NT_SRCHANDLE(),
IOCTL_NWR_RAW_HANDLE,
NULL,
0,
(PUCHAR)&SrcHandle,
sizeof(SrcHandle),
&BytesReturned,
NULL ) == FALSE ) {
setAL(0xff);
return;
}
if ( DeviceIoControl(
GET_NT_HANDLE(),
IOCTL_NWR_RAW_HANDLE,
NULL,
0,
(PUCHAR)&DestHandle,
sizeof(DestHandle),
&BytesReturned,
NULL ) == FALSE ) {
setAL(0xff);
return;
}
status = NwlibMakeNcp(
GET_NT_SRCHANDLE(),
NWR_ANY_F2_NCP(0x4A),
25, // RequestSize
4, // ResponseSize
"brrddd|d",
0,
SrcHandle, 6,
DestHandle, 6,
*(DWORD UNALIGNED*)&Buffer[4],
*(DWORD UNALIGNED*)&Buffer[8],
*(DWORD UNALIGNED*)&Buffer[12],
&BytesReturned
);
setDX((WORD)(BytesReturned >> 16));
setCX((WORD)BytesReturned);
if (!NT_SUCCESS(status)) {
SetStatus(status);
return;
} else {
setAL(0);
}
}
VOID
SetStatus(
NTSTATUS Status
)
/*++
Routine Description:
Convert an NTSTATUS into the appropriate register settings and updates
to the dos tables.
Arguments:
none.
Return Value:
none.
--*/
{
UCHAR DosStatus = (UCHAR)RtlNtStatusToDosError(Status);
if ((!DosStatus) &&
(Status != 0)) {
//
// We have a connection bit set
//
if ( Status & (NCP_STATUS_BAD_CONNECTION << 8)) {
DosStatus = 0xfc;
} else {
DosStatus = 0xff;
}
}
if (DosStatus) {
setCF(1);
}
setAL(DosStatus);
}