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
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;
|
|
}
|
|
|