Leaked source code of windows server 2003
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.
 
 
 
 
 
 

638 lines
23 KiB

/* (C) 1997-2000 Microsoft Corp.
*
* file : MCSIoctl.c
* author : Erik Mavrinac
*
* description: MCS API calls received from MCSMUX through ICA stack IOCTLs
* and returned through ICA virtual channel inputs. These entry points
* simply provide an IOCTL translation layer for the kernel-mode API.
* ONLY MCSMUX should make these calls.
*/
#include "PreComp.h"
#pragma hdrstop
#include <MCSImpl.h>
/*
* Prototypes for forward references for locally-defined functions.
*/
NTSTATUS AttachUserRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS DetachUserRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS ChannelJoinRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS ChannelLeaveRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS SendDataRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS ConnectProviderResponseFunc(PDomain, PSD_IOCTL);
NTSTATUS DisconnectProviderRequestFunc(PDomain, PSD_IOCTL);
NTSTATUS T120StartFunc(PDomain, PSD_IOCTL);
/*
* Globals
*/
// Table of function entry points for ChannelWrite() request calls.
// These entry points correspond to request defines in MCSIOCTL.h.
// NULL means unsupported, which will be handled by dispatch code in
// PdChannelWrite() in PDAPI.c.
const PT120RequestFunc g_T120RequestDispatch[] =
{
AttachUserRequestFunc,
DetachUserRequestFunc,
ChannelJoinRequestFunc,
ChannelLeaveRequestFunc,
SendDataRequestFunc, // Handles both uniform and regular.
SendDataRequestFunc, // Handles both uniform and regular.
NULL, // MCS_CHANNEL_CONVENE_REQUEST unsupported.
NULL, // MCS_CHANNEL_DISBAND_REQUEST unsupported.
NULL, // MCS_CHANNEL_ADMIT_REQUEST unsupported.
NULL, // MCS_CHANNEL_EXPEL_REQUEST unsupported.
NULL, // MCS_TOKEN_GRAB_REQUEST unsupported.
NULL, // MCS_TOKEN_INHIBIT_REQUEST unsupported.
NULL, // MCS_TOKEN_GIVE_REQUEST unsupported.
NULL, // MCS_TOKEN_GIVE_RESPONSE unsupported.
NULL, // MCS_TOKEN_PLEASE_REQUEST unsupported.
NULL, // MCS_TOKEN_RELEASE_REQUEST unsupported.
NULL, // MCS_TOKEN_TEST_REQUEST unsupported.
NULL, // MCS_CONNECT_PROVIDER_REQUEST unsupported.
ConnectProviderResponseFunc,
DisconnectProviderRequestFunc,
T120StartFunc,
};
/*
* Main callback for user attachment indications/confirms from kernel mode API.
* Translate and send to user mode.
*/
void __stdcall UserModeUserCallback(
UserHandle hUser,
unsigned Message,
void *Params,
void *UserDefined)
{
BYTE *pData;
unsigned DataLength;
NTSTATUS Status;
UserAttachment *pUA;
pUA = (UserAttachment *)hUser;
//MCS FUTURE: Handle all callbacks. Right now we support only those
// we know are going to pass by.
switch (Message) {
case MCS_DETACH_USER_INDICATION: {
DetachUserIndication *pDUin;
DetachUserIndicationIoctl DUinIoctl;
pDUin = (DetachUserIndication *)Params;
DUinIoctl.Header.Type = Message;
DUinIoctl.Header.hUser = hUser;
DUinIoctl.UserDefined = UserDefined;
DUinIoctl.DUin = *pDUin;
pData = (BYTE *)&DUinIoctl;
DataLength = sizeof(DetachUserIndicationIoctl);
// Send data below.
break;
}
default:
ErrOut1(pUA->pDomain->pContext, "UserModeUserCallback: "
"Unsupported callback %d received", Message);
return;
}
// Send the data to user mode.
ASSERT(pUA->pDomain->bChannelBound);
Status = IcaChannelInput(pUA->pDomain->pContext, Channel_Virtual,
Virtual_T120ChannelNum, NULL, pData, DataLength);
if (!NT_SUCCESS(Status)) {
ErrOut2(pUA->pDomain->pContext, "UserModeUserCallback: "
"Error %X on IcaChannelInput() for callback %d",
Status, Message);
// Ignore errors here. This should not happen unless the stack is
// going down.
}
}
/*
* Handles MCS kernel API callbacks for MCS send-data indications.
* Translates into user mode call.
*/
BOOLEAN __fastcall UserModeSendDataCallback(
BYTE *pData,
unsigned DataLength,
void *UserDefined,
UserHandle hUser,
BOOLEAN bUniform,
ChannelHandle hChannel,
MCSPriority Priority,
UserID SenderID,
Segmentation Segmentation)
{
BOOLEAN result = TRUE;
NTSTATUS Status;
UserAttachment *pUA;
SendDataIndicationIoctl SDinIoctl;
pUA = (UserAttachment *)hUser;
//MCS FUTURE: Need to alloc data and copy or, better yet,
// utilize a header at the beginning of the input buffer
// to send this upward.
#if 0
SDinIoctl.Header.Type = bUniform ? MCS_UNIFORM_SEND_DATA_INDICATION :
MCS_SEND_DATA_INDICATION;
SDinIoctl.Header.hUser = hUser;
SDinIoctl.UserDefined = UserDefined;
SDinIoctl.hChannel = hChannel;
SDinIoctl.SenderID = SenderID;
SDinIoctl.Priority = Priority;
SDinIoctl.Segmentation = Segmentation;
SDinIoctl.DataLength = DataLength;
// Send the data to user mode.
ASSERT(pUA->pDomain->bChannelBound);
Status = IcaChannelInput(pUA->pDomain->pContext, Channel_Virtual,
Virtual_T120ChannelNum, NULL, pData, DataLength);
if (!NT_SUCCESS(Status)) {
ErrOut2(pUA->pDomain->pContext, "UserModeUserCallback: "
"Error %X on IcaChannelInput() for callback %d",
Status, Message);
// Ignore errors here. This should not happen unless the stack is
// going down.
}
#endif
ErrOut(pUA->pDomain->pContext, "UserModeUserCallback: "
"Unsupported send-data indication received, code incomplete");
ASSERT(FALSE);
return result;
}
/*
* Handles an MCS attach-user request from user mode. Translates the ioctl
* into a kernel-mode MCS API call.
*/
NTSTATUS AttachUserRequestFunc(Domain *pDomain, PSD_IOCTL pSdIoctl)
{
AttachUserReturnIoctl *pAUrt;
AttachUserRequestIoctl *pAUrq;
ASSERT(pSdIoctl->InputBufferLength == sizeof(AttachUserRequestIoctl));
ASSERT(pSdIoctl->OutputBufferLength == sizeof(AttachUserReturnIoctl));
pAUrq = (AttachUserRequestIoctl *) pSdIoctl->InputBuffer;
pAUrt = (AttachUserReturnIoctl *) pSdIoctl->OutputBuffer;
ASSERT(pAUrq->Header.Type == MCS_ATTACH_USER_REQUEST);
// Call kernel-mode API which will handle creating local data and,
// if necessary, will forward the request to the top provider.
// Provide a kernel-mode callback that will package the data and send it
// to the appropriate user.
pAUrt->MCSErr = MCSAttachUserRequest((DomainHandle)pDomain,
UserModeUserCallback, UserModeSendDataCallback,
pAUrq->UserDefined, &pAUrt->hUser, &pAUrt->MaxSendSize,
&pAUrt->bCompleted);
pAUrt->UserID = ((UserAttachment *)pAUrt->hUser)->UserID;
pSdIoctl->BytesReturned = sizeof(AttachUserReturnIoctl);
// Return STATUS_SUCCESS even if there was an error code returned --
// the MCSError code is returned above too.
return STATUS_SUCCESS;
}
/*
* Handles an MCS detach-user request channel write. There is no callback for
* this request, the user attachment is considered destroyed upon return.
*/
NTSTATUS DetachUserRequestFunc(PDomain pDomain, PSD_IOCTL pSdIoctl)
{
MCSError *pMCSErr;
DetachUserRequestIoctl *pDUrq;
ASSERT(pSdIoctl->InputBufferLength == sizeof(DetachUserRequestIoctl));
ASSERT(pSdIoctl->OutputBufferLength == sizeof(MCSError));
pDUrq = (DetachUserRequestIoctl *)pSdIoctl->InputBuffer;
pMCSErr = (MCSError *)pSdIoctl->OutputBuffer;
ASSERT(pDUrq->Header.Type == MCS_DETACH_USER_REQUEST);
// Call the kernel-mode API.
*pMCSErr = MCSDetachUserRequest(pDUrq->Header.hUser);
pSdIoctl->BytesReturned = sizeof(MCSError);
// Always return STATUS_SUCCESS.
return STATUS_SUCCESS;
}
/*
* Channel join - ChannelWrite() request.
*/
NTSTATUS ChannelJoinRequestFunc(PDomain pDomain, PSD_IOCTL pSdIoctl)
{
ChannelJoinRequestIoctl *pCJrq;
ChannelJoinReturnIoctl *pCJrt;
ASSERT(pSdIoctl->InputBufferLength == sizeof(ChannelJoinRequestIoctl));
ASSERT(pSdIoctl->OutputBufferLength == sizeof(ChannelJoinReturnIoctl));
pCJrq = (ChannelJoinRequestIoctl *) pSdIoctl->InputBuffer;
pCJrt = (ChannelJoinReturnIoctl *) pSdIoctl->OutputBuffer;
ASSERT(pCJrq->Header.Type == MCS_CHANNEL_JOIN_REQUEST);
// Make the call to the kernel mode API.
pCJrt->MCSErr = MCSChannelJoinRequest(pCJrq->Header.hUser,
pCJrq->ChannelID, &pCJrt->hChannel, &pCJrt->bCompleted);
pCJrt->ChannelID = ((MCSChannel *)pCJrt->hChannel)->ID;
pSdIoctl->BytesReturned = sizeof(ChannelJoinReturnIoctl);
// Always return STATUS_SUCCESS.
return STATUS_SUCCESS;
}
/*
* Channel leave - ChannelWrite() request.
*/
NTSTATUS ChannelLeaveRequestFunc(PDomain pDomain, PSD_IOCTL pSdIoctl)
{
MCSError *pMCSErr;
ChannelLeaveRequestIoctl *pCLrq;
ASSERT(pSdIoctl->InputBufferLength == sizeof(ChannelLeaveRequestIoctl));
ASSERT(pSdIoctl->OutputBufferLength == sizeof(MCSError));
pCLrq = (ChannelLeaveRequestIoctl *)pSdIoctl->InputBuffer;
pMCSErr = (MCSError *)pSdIoctl->OutputBuffer;
ASSERT(pCLrq->Header.Type == MCS_CHANNEL_LEAVE_REQUEST);
*pMCSErr = MCSChannelLeaveRequest(pCLrq->Header.hUser, pCLrq->hChannel);
pSdIoctl->BytesReturned = sizeof(MCSError);
// Always return STATUS_SUCCESS.
return STATUS_SUCCESS;
}
/*
* Send data - handles both uniform and regular sends.
* Data is packed immediately after the SendDataRequestIoctl struct.
* No profixes or suffixes are needed.
*/
NTSTATUS SendDataRequestFunc(PDomain pDomain, PSD_IOCTL pSdIoctl)
{
POUTBUF pOutBuf;
MCSError *pMCSErr;
NTSTATUS Status;
UserAttachment *pUA;
SendDataRequestIoctl *pSDrq;
ASSERT(pSdIoctl->InputBufferLength >= sizeof(SendDataRequestIoctl));
ASSERT(pSdIoctl->OutputBufferLength == sizeof(MCSError));
pSDrq = (SendDataRequestIoctl *)pSdIoctl->InputBuffer;
pMCSErr = (MCSError *)pSdIoctl->OutputBuffer;
ASSERT(pSDrq->Header.Type == MCS_SEND_DATA_REQUEST ||
pSDrq->Header.Type == MCS_UNIFORM_SEND_DATA_REQUEST);
ASSERT(pSdIoctl->InputBufferLength == (sizeof(SendDataRequestIoctl) +
pSDrq->DataLength));
#if DBG
// Get pUA for tracing.
pUA = (UserAttachment *)pSDrq->Header.hUser;
#endif
// Allocate an OutBuf to emulate a kernel-mode caller.
Status = IcaBufferAlloc(pDomain->pContext, TRUE, TRUE,
(SendDataReqPrefixBytes + pSDrq->DataLength +
SendDataReqSuffixBytes), NULL, &pOutBuf);
if (Status != STATUS_SUCCESS) {
ErrOut(pUA->pDomain->pContext, "Could not allocate an OutBuf for a "
"send-data request sent from user mode");
return Status;
}
// Copy the user-mode memory to the kernel outbuf.
memcpy(pOutBuf->pBuffer + SendDataReqPrefixBytes,
&pSdIoctl->InputBuffer + sizeof(SendDataRequestIoctl),
pSDrq->DataLength);
// Set OutBuf params according to needs of API.
pOutBuf->ByteCount = pSDrq->DataLength;
pOutBuf->pBuffer += SendDataReqPrefixBytes;
// Call the kernel-mode API.
*pMCSErr = MCSSendDataRequest(pSDrq->Header.hUser, pSDrq->hChannel,
pSDrq->RequestType, 0, pSDrq->Priority, pSDrq->Segmentation,
pOutBuf);
pSdIoctl->BytesReturned = sizeof(MCSError);
// Always return STATUS_SUCCESS.
return STATUS_SUCCESS;
}
/*
* Connect provider response - ChannelWrite() request. Requires filler bytes
* in MCSConnectProviderResponseIoctl to make sure we use at least 54 bytes
* for the struct so we can reuse the OutBuf here. User data must start
* at (pSdIoctl->pBuffer + sizeof(MCSConnectProviderResponseIoctl)).
*/
NTSTATUS ConnectProviderResponseFunc(
PDomain pDomain,
PSD_IOCTL pSdIoctl)
{
POUTBUF pOutBuf;
NTSTATUS Status;
ConnectProviderResponseIoctl *pCPrs;
ASSERT(pSdIoctl->InputBufferLength ==
sizeof(ConnectProviderResponseIoctl));
pCPrs = (ConnectProviderResponseIoctl *)pSdIoctl->InputBuffer;
ASSERT(pCPrs->Header.Type == MCS_CONNECT_PROVIDER_RESPONSE);
// Verify that we are actually waiting for a CP response.
if (pDomain->State != State_ConnectProvIndPending) {
ErrOut(pDomain->pContext, "Connect-provider response call received, "
"we are in wrong state, ignoring");
return STATUS_INVALID_DOMAIN_STATE;
}
// Alloc OutBuf for sending PDU.
// This allocation is vital to the session and must succeed.
do {
Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE,
ConnectResponseHeaderSize + pCPrs->UserDataLength, NULL,
&pOutBuf);
if (Status != STATUS_SUCCESS)
ErrOut(pDomain->pContext, "Could not allocate an OutBuf for a "
"connect-response PDU, retrying");
} while (Status != STATUS_SUCCESS);
// Encode PDU header. Param 2, the called connect ID, does not need to be
// anything special because we do not allow extra sockets to be opened
// for other data priorities.
CreateConnectResponseHeader(pDomain->pContext, pCPrs->Result, 0,
&pDomain->DomParams, pCPrs->UserDataLength, pOutBuf->pBuffer,
&pOutBuf->ByteCount);
// Copy the user data after the header.
RtlCopyMemory(pOutBuf->pBuffer + pOutBuf->ByteCount, pCPrs->pUserData,
pCPrs->UserDataLength);
pOutBuf->ByteCount += pCPrs->UserDataLength;
// Send the new PDU OutBuf down to the TD for sending out.
//MCS FUTURE: Needs to change for multiple connections.
Status = SendOutBuf(pDomain, pOutBuf);
if (!NT_SUCCESS(Status)) {
ErrOut(pDomain->pContext, "Could not send connect-response PDU OutBuf "
"to TD");
// Ignore errors here -- this should only occur if stack is going down.
return Status;
}
// Transition state depending on Result.
if (pCPrs->Result == RESULT_SUCCESSFUL) {
pDomain->State = State_MCS_Connected;
}
else {
TraceOut(pDomain->pContext, "ConnectProviderRespFunc(): Node "
"controller returned error in response, destroying call "
"data");
pDomain->State = State_Disconnected;
// Detach any users that attached during domain setup.
DisconnectProvider(pDomain, TRUE, REASON_PROVIDER_INITIATED);
}
return STATUS_SUCCESS;
}
/*
* Disconnect provider - ChannelWrite() request.
* This handles both the case where a disconnect is performed on the local
* "connection" (i.e. pDPrq->hConn == NULL), and a specific remote
* connection (pDPrq->hConn != NULL)/
* MCS FUTURE: Change for multiple connections.
*/
NTSTATUS DisconnectProviderRequestFunc(
PDomain pDomain,
PSD_IOCTL pSdIoctl)
{
NTSTATUS Status;
DisconnectProviderRequestIoctl *pDPrq;
LONG refs;
TraceOut1(pDomain->pContext, "DisconnectProviderRequestFunc(): Entry, "
"pDomain=%X", pDomain);
ASSERT(pSdIoctl->InputBufferLength ==
sizeof(DisconnectProviderRequestIoctl));
pDPrq = (DisconnectProviderRequestIoctl *)pSdIoctl->InputBuffer;
ASSERT(pDPrq->Header.hUser == NULL);
ASSERT(pDPrq->Header.Type == MCS_DISCONNECT_PROVIDER_REQUEST);
// Send DPum PDU if we can still send data.
if ((pDomain->State == State_MCS_Connected) && pDomain->bCanSendData) {
POUTBUF pOutBuf;
// Alloc OutBuf for sending DPum PDU.
Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE, DPumPDUSize,
NULL, &pOutBuf);
if (Status != STATUS_SUCCESS) {
ErrOut(pDomain->pContext, "Could not allocate an OutBuf for a "
"DPum PDU, cannot send");
// We ignore problems sending the DPum PDU since we are going down
// anyway.
}
else {
SD_SYNCWRITE SdSyncWrite;
CreateDisconnectProviderUlt(pDPrq->Reason, pOutBuf->pBuffer);
pOutBuf->ByteCount = DPumPDUSize;
TraceOut(pDomain->pContext, "DisconnectProviderRequestFunc(): "
"Sending DPum PDU");
// Send the PDU to the transport.
// MCS FUTURE: Assuming only one transport and only one
// connection.
Status = SendOutBuf(pDomain, pOutBuf);
if (!NT_SUCCESS(Status))
// We ignore problems sending the DPum PDU since we are going
// down anyway.
WarnOut(pDomain->pContext, "Could not send DPum PDU OutBuf "
"downward");
// The call to IcaCallNextDriver unlocks our stack, allowing a WD_Close to
// go through. WD_Close can call MCSCleanup which will free pDomain,
// and NULL out pTSWd->hDomainKernel. Because pDomain may no longer be valid,
// it is not good enough to check pDomain->StatusDead here! To keep this
// fix localized, we created a pseudo-RefCount to protect the exact cases
// we saw this bug hit in stress. A bug will be opened for Longhorn to
// make this RefCount generic so that ALL calls to IcaWaitForSingleObject (etc)
// are protected.
PDomainAddRef(pDomain);
// Flush the transport driver. Note that this call can block and
// release the stack lock
Status = IcaCallNextDriver(pDomain->pContext, SD$SYNCWRITE,
&SdSyncWrite);
refs = PDomainRelease(pDomain);
if (0 == refs)
{
// We ignore problems since we are going down anyway.
Status = STATUS_SUCCESS;
goto DC_EXIT_POINT;
}
if (!NT_SUCCESS(Status))
// We ignore problems since we are going down anyway.
WarnOut(pDomain->pContext, "Could not sync transport after "
"DPum");
// If the client has not already responded with a FIN (while we
// were blocked on the synchronous write) wait until we see it or time
// out trying
if (pDomain->bCanSendData) {
pDomain->pBrokenEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
if (pDomain->pBrokenEvent) {
KeInitializeEvent(pDomain->pBrokenEvent, NotificationEvent, FALSE);
PDomainAddRef(pDomain);
IcaWaitForSingleObject(pDomain->pContext,
pDomain->pBrokenEvent,
5000);
refs = PDomainRelease(pDomain);
if (0 == refs)
{
// We ignore problems since we are going down anyway.
Status = STATUS_SUCCESS;
goto DC_EXIT_POINT;
}
ExFreePool(pDomain->pBrokenEvent);
pDomain->pBrokenEvent = NULL;
}
}
}
}
// Internal disconnection code.
DisconnectProvider(pDomain, (BOOLEAN)(pDPrq->hConn == NULL),
pDPrq->Reason);
Status = STATUS_SUCCESS;
// Different behavior for different connections.
if (pDPrq->hConn == NULL) {
// This call should only come in when the stack is going away.
// So, prevent further data sends to transport and further channel
// inputs to user mode.
pDomain->bCanSendData = FALSE;
pDomain->bChannelBound = FALSE;
// The domain is considered dead now. Domain struct cleanup will
// occur during stack driver cleanup at MCSCleanup().
// Status = IcaChannelInput(pDomain->pContext, Channel_Virtual,
// Virtual_T120ChannelNum, NULL, "F", 1);
}
DC_EXIT_POINT:
return Status;
}
NTSTATUS T120StartFunc(Domain *pDomain, PSD_IOCTL pSdIoctl)
{
NTSTATUS Status;
DisconnectProviderIndicationIoctl DPin;
pDomain->bT120StartReceived = TRUE;
// This is to handle a timing window where the stack has just come up
// but a DPum from a quickly-disconnected client has already arrived.
if (pDomain->bDPumReceivedNotInput) {
// We should have received a QUERY_VIRTUAL_BINDINGS ioctl by this time.
ASSERT(pDomain->bChannelBound);
// Fill out disconnect-provider indication for the node controller.
DPin.Header.hUser = NULL; // Node controller.
DPin.Header.Type = MCS_DISCONNECT_PROVIDER_INDICATION;
DPin.hConn = NULL;
// Reason is a 3-bit field starting at bit 1 of the 1st byte.
DPin.Reason = pDomain->DelayedDPumReason;
// Send the DPin to the node controller channel.
TraceOut(pDomain->pContext, "HandleDisconnProvUlt(): Sending "
"DISCONNECT_PROV_IND upward");
Status = IcaChannelInput(pDomain->pContext, Channel_Virtual,
Virtual_T120ChannelNum, NULL, (BYTE *)&DPin, sizeof(DPin));
if (!NT_SUCCESS(Status)) {
// We ignore the error -- if the stack is coming down, the link
// may have been broken, so this is not a major concern.
WarnOut1(pDomain->pContext, "T120StartFunc(): "
"Could not send DISCONN_PROV_IND to user mode, "
"status=%X, ignoring error", Status);
}
// In this case we have to ignore the fact that we may have a
// X.224 connect already pending.
return STATUS_SUCCESS;
}
pDomain->bCanSendData = TRUE;
// If an X.224 connect has already been processed, and we have bound the
// virtual channels, send the X.224 response.
if (pDomain->bChannelBound && pDomain->State == State_X224_Requesting) {
TraceOut(pDomain->pContext, "T120StartFunc(): Sending X.224 response");
Status = SendX224Confirm(pDomain);
// Ignore errors. Failure to send should occur only when the stack is
// going down.
}
else {
WarnOut(pDomain->pContext,
"T120StartFunc(): Domain state not State_X224_Requesting, "
"awaiting X.224 connect");
}
return STATUS_SUCCESS;
}