|
|
/* (C) 1997-1999 Microsoft Corp.
* * file : IcaIFace.c * author : Erik Mavrinac * * description: MCS setup/shutdown and direct entry points for use with the * ICA programming model. See also Decode.c for IcaRawInput() handling. * * History: * 10-Aug-1997 jparsons Revised for new calling model * 05-Aug-1998 jparsons Added shadowing support * */
#include "precomp.h"
#pragma hdrstop
#include <MCSImpl.h>
// Prototype for WD function used below, so we don't need to include
// lots of extra headers.
void WDW_OnClientDisconnected(void *);
/*
* Main initialization entry point for kernel-mode MCS. * Called by the WD during its processing of WdOpen(). */ MCSError APIENTRY MCSInitialize( PSDCONTEXT pContext, PSD_OPEN pSdOpen, DomainHandle *phDomain, void *pSMData) { Domain *pDomain; unsigned i; ULONG ulBufferLen; TraceOut(pContext, "MCSInitialize(): entry");
//
// Alloc the Domain struct. We allocate the basic size plus the typical
// input buffer size. The default in the registry today is 2048 which
// nicely fits the max virtual channel PDU of about 1640. If we get a
// message that exceeds this length we will dynamically allocate a buffer
// just for use in a one time reassembly then delete it.
//
if (pSdOpen->StackClass != Stack_Passthru) { ulBufferLen = pSdOpen->WdConfig.WdInputBufferLength; } else { ulBufferLen = 1024 * 10; }
pDomain = ExAllocatePoolWithTag(PagedPool, sizeof(Domain) + ulBufferLen + INPUT_BUFFER_BIAS, MCS_POOL_TAG); if (pDomain != NULL) { memset(pDomain, 0, sizeof(Domain)); } else { ErrOut(pContext, "MCSInitialize(kernel): Alloc failure " "allocating Domain"); return MCS_ALLOCATION_FAILURE; }
// Save pContext -- it is needed for future tracing and ICA interaction.
pDomain->pContext = pContext;
// Save pSMData - needed for calling fast-path input decoding function.
pDomain->pSMData = pSMData;
// Store what we need from SD_OPEN.
pDomain->pStat = pSdOpen->pStatus; pDomain->ReceiveBufSize = ulBufferLen;
// We have one reference to this pDomain
pDomain->PseudoRefCount = 1;
// Indicate that we do not want TermDD managing outbuf headers/trailers
pSdOpen->SdOutBufHeader = 0; pSdOpen->SdOutBufTrailer = 0;
// Initialize MCS-specific Domain members. We already zeroed mem
// so set only nonzero variables.
SListInit(&pDomain->ChannelList, DefaultNumChannels); SListInit(&pDomain->UserAttachmentList, DefaultNumUserAttachments); pDomain->bTopProvider = TRUE; pDomain->NextAvailDynChannel = MinDynamicChannel; pDomain->State = State_Unconnected; pDomain->StackClass = pSdOpen->StackClass; for (i = 0; i < NumPreallocUA; i++) { pDomain->PreallocUA[i].bInUse = FALSE; pDomain->PreallocUA[i].bPreallocated = TRUE; } for (i = 0; i < NumPreallocChannel; i++) { pDomain->PreallocChannel[i].bInUse = FALSE; pDomain->PreallocChannel[i].bPreallocated = TRUE; }
// Give the Domain to the caller.
*phDomain = pDomain;
// If this is a shadow or passthru stack, the default all the info
// otherwise, all this info gets built when the client connects
if ((pDomain->StackClass == Stack_Passthru) || (pDomain->StackClass == Stack_Shadow)) MCSCreateDefaultDomain(pContext, *phDomain);
return MCS_NO_ERROR; }
/*
* Called by WD during shadow connect processing to retrieve the client MCS * domain parameters for use by the shadow target stack. */ MCSError APIENTRY MCSGetDomainInfo( DomainHandle hDomain, PDomainParameters pDomParams, unsigned *MaxSendSize, unsigned *MaxX224DataSize, unsigned *X224SourcePort) { Domain *pDomain = hDomain;
TraceOut(pDomain->pContext, "MCSGetDomainInfo(): entry");
*pDomParams = pDomain->DomParams; *MaxSendSize = pDomain->MaxSendSize; *MaxX224DataSize = pDomain->MaxX224DataSize; *X224SourcePort = pDomain->X224SourcePort; return MCS_NO_ERROR; }
/*
* Called by WD during shadow connect processing to initialize the MCS * domain for the shadow & passthru stacks. */ MCSError APIENTRY MCSCreateDefaultDomain(PSDCONTEXT pContext, DomainHandle hDomain) { Domain *pDomain = hDomain; TraceOut(pContext, "MCSCreateDefaultDomain(): entry");
pDomain->DomParams.MaxChannels = 34; pDomain->DomParams.MaxUsers = RequiredMinUsers; pDomain->DomParams.MaxTokens = 0; pDomain->DomParams.NumPriorities = RequiredPriorities; pDomain->DomParams.MinThroughput = 0; pDomain->DomParams.MaxDomainHeight = RequiredDomainHeight; pDomain->DomParams.MaxPDUSize = X224_DefaultDataSize; pDomain->DomParams.ProtocolVersion = RequiredProtocolVer; pDomain->MaxSendSize = X224_DefaultDataSize - 6 - GetTotalLengthDeterminantEncodingSize(X224_DefaultDataSize); pDomain->MaxX224DataSize = X224_DefaultDataSize; pDomain->X224SourcePort = 0x1234; pDomain->State = State_MCS_Connected; pDomain->bCanSendData = 1;
return MCS_NO_ERROR; }
/*
/*
* Called by WD during shadow connect processing to get the default domain * params for the shadow target stack. */ MCSError APIENTRY MCSGetDefaultDomain(PSDCONTEXT pContext, PDomainParameters pDomParams, unsigned *MaxSendSize, unsigned *MaxX224DataSize, unsigned *X224SourcePort) { TraceOut(pContext, "MCSGetDefaultDomain(): entry"); pDomParams->MaxChannels = 34; pDomParams->MaxUsers = RequiredMinUsers; pDomParams->MaxTokens = 0; pDomParams->NumPriorities = RequiredPriorities; pDomParams->MinThroughput = 0; pDomParams->MaxDomainHeight = RequiredDomainHeight; pDomParams->MaxPDUSize = X224_DefaultDataSize; pDomParams->ProtocolVersion = RequiredProtocolVer; *MaxSendSize = pDomParams->MaxPDUSize - 6 - GetTotalLengthDeterminantEncodingSize(pDomParams->MaxPDUSize); *MaxX224DataSize = X224_DefaultDataSize; *X224SourcePort = 0x1234;
return MCS_NO_ERROR; }
/*
* Called by WD during shadow connect processing to register which channel * should receive all shadow data. */ MCSError APIENTRY MCSSetShadowChannel( DomainHandle hDomain, ChannelID shadowChannel) { Domain *pDomain = hDomain; TraceOut(pDomain->pContext, "MCSSetShadowChannel: entry");
pDomain->shadowChannel = shadowChannel; return MCS_NO_ERROR; }
/*
* Main destruction entry point for kernel-mode MCS. * Called by the WD during its processing of WdClose(). */ MCSError APIENTRY MCSCleanup(DomainHandle *phDomain) { Domain *pDomain; UINT_PTR ChannelID; MCSChannel *pMCSChannel; UserHandle hUser; UserAttachment *pUA;
pDomain = (Domain *)(*phDomain); TraceOut1(pDomain->pContext, "MCSCleanup(): pDomain=%X", pDomain);
/*
* Free any remaining data in the Domain. */
// Deallocate all remaining channels, if present. Note we should take care
// of channels first since they're usually attached to other objects and
// need to have their bPreallocated status determined first.
for (;;) { SListRemoveFirst(&pDomain->ChannelList, &ChannelID, &pMCSChannel); if (pMCSChannel == NULL) break; SListDestroy(&pMCSChannel->UserList); if (!pMCSChannel->bPreallocated) ExFreePool(pMCSChannel); }
// Deallocate all remaining user attachments, if present.
for (;;) { SListRemoveFirst(&pDomain->UserAttachmentList, (UINT_PTR *)&hUser, &pUA); if (pUA == NULL) break; SListDestroy(&pUA->JoinedChannelList); if (!pUA->bPreallocated) ExFreePool(pUA); }
// Kill lists.
SListDestroy(&pDomain->ChannelList); SListDestroy(&pDomain->UserAttachmentList);
// Free outstanding dynamic input reassembly buffer if present.
if (pDomain->pReassembleData != NULL && pDomain->pReassembleData != pDomain->PacketBuf) ExFreePool(pDomain->pReassembleData);
// Free the Domain.
PDomainRelease(pDomain); *phDomain = NULL;
return MCS_NO_ERROR; }
/*
* Callout from WD when an IOCTL_ICA_VIRTUAL_QUERY_BINDINGS is received. * pVBind is a pointer to an empty SD_VCBIND struct. */ NTSTATUS MCSIcaVirtualQueryBindings( DomainHandle hDomain, PSD_VCBIND *ppVBind, unsigned *pBytesReturned) { Domain *pDomain; NTSTATUS Status; PSD_VCBIND pVBind;
pDomain = (Domain *)hDomain; pVBind = *ppVBind;
// Define the user mode T120 channel.
if (!pDomain->bChannelBound) { RtlCopyMemory(pVBind->VirtualName, Virtual_T120, sizeof(Virtual_T120)); pVBind->VirtualClass = Virtual_T120ChannelNum; *pBytesReturned = sizeof(SD_VCBIND); pDomain->bChannelBound = TRUE; // Skip our entry and advance the caller's pointer.
pVBind++; *ppVBind = pVBind; } else { *pBytesReturned = 0; }
Status = STATUS_SUCCESS;
// This is one of the events which must occur before the data flow can be
// sent across the net. If we have gotten an MCS_T120_START indication
// already, and an X.224 connect-request, then it is now time to send
// the X.224 response and kick off the data flow.
if (pDomain->bCanSendData && pDomain->State == State_X224_Requesting) { TraceOut(pDomain->pContext, "IcaQueryVirtBind(): Sending X.224 response"); Status = SendX224Confirm(pDomain); } return Status; }
/*
* Callout from WD upon reception of a IOCTL_T120_REQUEST, i.e. a user-mode * ioctl. */ NTSTATUS MCSIcaT120Request(DomainHandle hDomain, PSD_IOCTL pSdIoctl) { Domain *pDomain; IoctlHeader *pHeader; pDomain = (Domain *)hDomain; // Get the request type.
ASSERT(pSdIoctl->InputBufferLength >= sizeof(IoctlHeader)); pHeader = (IoctlHeader *)pSdIoctl->InputBuffer;
// Make sure request within bounds.
if (pHeader->Type < MCS_ATTACH_USER_REQUEST || pHeader->Type > MCS_T120_START) { ErrOut(pDomain->pContext, "Invalid IOCTL_T120_REQUEST type"); return STATUS_INVALID_DEVICE_REQUEST; }
// Check that request is supported.
if (g_T120RequestDispatch[pHeader->Type] == NULL) { ErrOut(pDomain->pContext, "IOCTL_T120_REQUEST type unsupported"); return STATUS_INVALID_DEVICE_REQUEST; }
// Make the call. The entry points are defined in MCSIoctl.c.
return (g_T120RequestDispatch[pHeader->Type])(pDomain, pSdIoctl); }
/*
* Processes channel inputs from TD. For MCS we only need to check for * upward-bound command channel inputs for broken-connection indications; * everything else can be passed up the stack. */ // Utility function. Used here and in Decode.c for X.224 disconnection.
void SignalBrokenConnection(Domain *pDomain) { NTSTATUS Status; DisconnectProviderIndicationIoctl DPin;
// Check if disconnection already happened.
if (pDomain->State != State_MCS_Connected) return;
if (!pDomain->bChannelBound) { TraceOut(pDomain->pContext, "SignalBrokenConnection(): Cannot " "send disconnect-provider indication: user mode link broken"); return; }
TraceOut(pDomain->pContext, "SignalBrokenConnection(): Sending " "disconnect-provider indication to user mode");
// Begin filling out disconnect-provider indication for the node controller.
DPin.Header.Type = MCS_DISCONNECT_PROVIDER_INDICATION; DPin.Header.hUser = NULL; // Node controller.
DPin.hConn = NULL; DPin.Reason = REASON_DOMAIN_DISCONNECTED; TraceOut1(pDomain->pContext, "%s: SignalBrokenConnection!!!", pDomain->StackClass == Stack_Primary ? "Primary" : (pDomain->StackClass == Stack_Shadow ? "Shadow" : "PassThru")); // Send the DPin to the node controller channel.
Status = IcaChannelInput(pDomain->pContext, Channel_Virtual, Virtual_T120ChannelNum, NULL, (BYTE *)&DPin, sizeof(DPin)); if (!NT_SUCCESS(Status)) { ErrOut(pDomain->pContext, "SignalBrokenConn(): Could not send " "disconnect-provider indication: error on ChannelInput()"); // Ignore errors sending disconnect-provider upward. If the stack is
// going down we will no longer have connectivity.
} // Transition to state unconnected, detach nonlocal users.
DisconnectProvider(pDomain, FALSE, REASON_DOMAIN_DISCONNECTED); }
/*
* This function is called directly by TermDD with a pointer to the WD data * structure. By convention, we assume that the DomainHandle is first in * that struct so we can simply do a double-indirection to get to our data. */ NTSTATUS MCSIcaChannelInput( void *pTSWd, CHANNELCLASS ChannelClass, VIRTUALCHANNELCLASS VirtualClass, PINBUF pInBuf, BYTE *pBuffer, ULONG ByteCount) { Domain *pDomain; NTSTATUS Status; PICA_CHANNEL_COMMAND pCommand;
pDomain = (Domain *)(*((HANDLE *)pTSWd)); if (ChannelClass != Channel_Command) goto SendUpStack;
if (ByteCount < sizeof(ICA_CHANNEL_COMMAND)) { ErrOut(pDomain->pContext, "ChannelInput(): Channel_Command bad " "byte count"); goto SendUpStack; }
pCommand = (PICA_CHANNEL_COMMAND)pBuffer;
if (pCommand->Header.Command != ICA_COMMAND_BROKEN_CONNECTION) goto SendUpStack; TraceOut1(pDomain->pContext, "%s: ChannelInput(): broken connection received", pDomain->StackClass == Stack_Primary ? "Primary" : (pDomain->StackClass == Stack_Shadow ? "Shadow" : "PassThru")); // Block further send attempts from MCS. We will eventually receive an
// IOCTL_ICA_STACK_CANCEL_IO which means the same thing, but that is
// only done after we issue the ICA_COMMAND_BROKEN_CONNECTION
// upward.
pDomain->bCanSendData = FALSE;
// Signal that the client closed the connection, both for MCS and
// directly to the WD to release any session locks waiting on
// the client to complete a connection protocol sequence.
if (pDomain->pBrokenEvent) KeSetEvent (pDomain->pBrokenEvent, EVENT_INCREMENT, FALSE); WDW_OnClientDisconnected(pTSWd);
// If we have not already received a disconnect-provider request from
// user mode, send an indication.
if (pDomain->State == State_MCS_Connected && pDomain->bChannelBound) SignalBrokenConnection(pDomain);
SendUpStack: Status = IcaChannelInput(pDomain->pContext, ChannelClass, VirtualClass, pInBuf, pBuffer, ByteCount); if (!NT_SUCCESS(Status)) ErrOut(pDomain->pContext, "MCSIcaChannelInput(): Failed to forward " "input upward");
return Status; }
/*
* Receives signal from WD that an IOCTL_ICA_STACK_CANCEL_IO was received * which signals that I/O on the stack is no longer allowed. After this * point no further ICA buffer allocation, freeing, or data sends should be * performed. */ void MCSIcaStackCancelIo(DomainHandle hDomain) { TraceOut(((Domain *)hDomain)->pContext, "Received STACK_CANCEL_IO");
((Domain *)hDomain)->bCanSendData = FALSE; }
/*
* Returns number of bytes (octets) consumed finding the size in NBytesConsumed. * Returns length in Result. Sets *pbLarge to nonzero if there are more * encoded blocks following this one. * Note that the maximum size encoded is 64K -- 0xC4 indicates * that 4 16K blocks are encoded here. If the block is larger, for instance * in a large MCS Send Data PDU, multiple blocks will be encoded one * after another. If the block is an exact multiple of 16K, a trailing byte * code 0x00 is appended as a placemarker to indicate that the encoding is * complete. * Examples of large encodings: * * 16K: 0xC1, then 16K of data, then 0x00 as the final placeholder. * 16K + 1: 0xC1, then 16K of data, then 0x01, and finally the extra byte of data. * 64K: 0xC4, then 64K of data, then 0x00 as the final placeholder. * 128K + 1: 0xC4 then 64K of data, 0xC4 + 64K of data, 0x01 + 1 byte of data. * * pStart is assumed to be an octet(BYTE)-aligned address -- this function is * designed for ALIGNED-PER encoding type, which is the type used in MCS. * Note that bit references here are in the range 7..0 where 7 is the high bit. * The ASN.1 spec uses 8..1. * Returns FALSE if length could not be retrieved. */ BOOLEAN __fastcall DecodeLengthDeterminantPER( BYTE *pStart, // [IN], points to start of encoded bytes.
unsigned BytesLeft, // [IN], number of bytes remaining in frame.
BOOLEAN *pbLarge, // [OUT] TRUE if there are more encoded blocks following.
unsigned *Length, // [OUT] Number of bytes encoded here.
unsigned *pNBytesConsumed) // [OUT] Count of bytes consumed in decoding.
{ if (BytesLeft >= 1) { if (*pStart <= 0x7F) { *pNBytesConsumed = 1; *Length = *pStart; *pbLarge = FALSE; } else { // High bit 7 set, check to see if bit 6 is set.
if (*pStart & 0x40) { // Bit 6 is set, the lowest 3 bits encode the number (1..4) of
// full 16K chunks following.
*pNBytesConsumed = 1; *Length = 16384 * (*pStart & 0x07); *pbLarge = TRUE; } else { // Bit 6 is clear, length is encoded in 14 bits of last 6 bits
// of *pStart as most significant bits and all of the next
// byte as least significant.
if (BytesLeft >= 2) { *pNBytesConsumed = 2; *Length = ((unsigned)((*pStart & 0x3F) << 8) + (unsigned)(*(pStart + 1))); *pbLarge = FALSE; } else { return FALSE; } } }
return TRUE; } else { return FALSE; } }
|