ICAAPI!IcaStackConnectionAccept到RDPWD!SendX224Confirm----termsrv源代码分析

ICAAPI!IcaStackConnectionAccept到RDPWD!SendX224Confirm

1: kd> kc

00 RDPWD!SendOutBuf

01 RDPWD!SendX224Confirm

02 RDPWD!T120StartFunc

03 RDPWD!MCSIcaT120Request

04 RDPWD!WD_Ioctl

05 termdd!_IcaCallSd

06 termdd!_IcaCallStack

07 termdd!IcaDeviceControlStack

08 termdd!IcaDeviceControl

09 termdd!IcaDispatch

0a nt!IofCallDriver

0b nt!IopSynchronousServiceTail

0c nt!IopXxxControlFile

0d nt!NtDeviceIoControlFile

0e nt!_KiSystemService

0f SharedUserData!SystemCallStub

10 ntdll!NtDeviceIoControlFile

11 ICAAPI!IcaIoControl

12 ICAAPI!_IcaStackIoControlWorker

13 ICAAPI!IcaStackIoControl

14 rdpwsx!MCSCreateDomain

15 rdpwsx!GCCConferenceInit

16 rdpwsx!TSrvBindStack

17 rdpwsx!TSrvAllocInfo

18 rdpwsx!TSrvStackConnect

19 rdpwsx!WsxIcaStackIoControl

1a termsrv!WsxStackIoControl

1b ICAAPI!_IcaStackIoControl

1c ICAAPI!_IcaStackWaitForIca

1d ICAAPI!IcaStackConnectionAccept

1e termsrv!TransferConnectionToIdleWinStation

1f termsrv!WinStationTransferThread

20 kernel32!BaseThreadStart

第一部分:

NTSTATUS

IcaStackConnectionAccept( IN HANDLE hIca,

IN HANDLE pContext,

IN PWINSTATIONNAME pWinStationName,

IN PWINSTATIONCONFIG2 pWinStationConfig,

IN PVOID pEndpoint,

IN ULONG EndpointLength,

IN PICA_STACK_STATE_HEADER pStackState,

IN ULONG BufferLength,

IN PICA_TRACE pTrace )

{

/*

* Send the config data to stack driver

*/

_IcaStackIoControl( pStack,

IOCTL_ICA_STACK_SET_CONFIG,

&ConfigData,

sizeof(ICA_STACK_CONFIG_DATA),

NULL,

0,

NULL);

/*

* Wait for ICA Detect string from client

*/

Status = _IcaStackWaitForIca( pContext,

pWinStationConfig,

&fStackModified );

if ( !NT_SUCCESS(Status) ) {

goto badaccept;

}

第二部分:

NTSTATUS

_IcaStackWaitForIca( IN PSTACK pStack,

IN OUT PWINSTATIONCONFIG2 pWinStationConfig,

OUT BOOLEAN * pfStackModified )

{

ICA_STACK_CONFIG IcaStackConfig;

PPDCONFIG pPdConfig;

NTSTATUS Status;

ULONG cbReturned;

ULONG i;

ASSERTLOCK( &pStack->CritSec );

/*

* Initialize flag

*/

*pfStackModified = FALSE;

/*

* Wait for ICA Detect string from client

*/

Status = _IcaStackIoControl( pStack,

IOCTL_ICA_STACK_WAIT_FOR_ICA,

NULL,

0,

&IcaStackConfig,

sizeof(IcaStackConfig),

&cbReturned );

if ( !NT_SUCCESS(Status) ) {

goto baddetect;

}

第三部分:

chenghao@chenghaodeiMac termsrv % grep "TSrvStackConnect" -nr ./

.//rdpwsx/rdpex/tsrvcon.h:44:EXTERN_C NTSTATUS TSrvStackConnect(IN HANDLE hIca,

Binary file .//rdpwsx/rdpex/obj/i386/tsrvwsx.obj matches

Binary file .//rdpwsx/rdpex/obj/i386/tsrvcon.obj matches

Binary file .//rdpwsx/rdpex/obj/i386/rdpwsx.pdb matches

.//rdpwsx/rdpex/tsrvcon.c:161:// TSrvStackConnect()

.//rdpwsx/rdpex/tsrvcon.c:177:TSrvStackConnect(IN HANDLE hIca,

.//rdpwsx/rdpex/tsrvcon.c:184: "TShrSRV: TSrvStackConnect entry\n"));

.//rdpwsx/rdpex/tsrvcon.c:206: "TShrSRV: TSrvStackConnect exit - 0x%x\n", ntStatus));

.//rdpwsx/rdpex/tsrvwsx.c:1098: ntStatus = TSrvStackConnect(hIca, hStack, &pTSrvInfo);

NTSTATUS

TSrvStackConnect(IN HANDLE hIca,

IN HANDLE hStack,

OUT PTSRVINFO *ppTSrvInfo)

{

NTSTATUS ntStatus;

TRACE((DEBUG_TSHRSRV_FLOW,

"TShrSRV: TSrvStackConnect entry\n"));

ntStatus = STATUS_UNSUCCESSFUL;

if (TSrvIsReady(TRUE))

{

TS_ASSERT(hStack);

// Allocate a TSrvInfo object which will be used to

// track this connection instance

ntStatus = TSrvAllocInfo(ppTSrvInfo, hIca, hStack);

if (NT_SUCCESS(ntStatus))

{

TS_ASSERT(*ppTSrvInfo);

ntStatus = TSrvDoConnect(*ppTSrvInfo);

}

}

TRACE((DEBUG_TSHRSRV_FLOW,

"TShrSRV: TSrvStackConnect exit - 0x%x\n", ntStatus));

return (ntStatus);

}

第四部分:

chenghao@chenghaodeiMac termsrv % grep "TSrvAllocInfo" -nr ./

Binary file .//rdpwsx/rdpex/obj/i386/tsrvinfo.obj matches

Binary file .//rdpwsx/rdpex/obj/i386/tsrvcon.obj matches

Binary file .//rdpwsx/rdpex/obj/i386/rdpwsx.pdb matches

.//rdpwsx/rdpex/tsrvinfo.h:105:EXTERN_C NTSTATUS TSrvAllocInfo(OUT PTSRVINFO *ppTSrvInfo, HANDLE hIca, HANDLE hStack);

.//rdpwsx/rdpex/_tsrvinfo.h:40:PTSRVINFO TSrvAllocInfoNew(void);

.//rdpwsx/rdpex/tsrvcon.c:195: ntStatus = TSrvAllocInfo(ppTSrvInfo, hIca, hStack);

.//rdpwsx/rdpex/tsrvcon.c:265: pTSrvInfo = TSrvAllocInfoNew();

.//rdpwsx/rdpex/tsrvinfo.c:149:// TSrvAllocInfoNew()

.//rdpwsx/rdpex/tsrvinfo.c:163:TSrvAllocInfoNew(void)

.//rdpwsx/rdpex/tsrvinfo.c:168: "TShrSRV: TSrvAllocInfoNew entry\n"));

.//rdpwsx/rdpex/tsrvinfo.c:226: "TShrSRV: TSrvAllocInfoNew exit - %p\n", pTSrvInfo));

.//rdpwsx/rdpex/tsrvinfo.c:234:// TSrvAllocInfo()

.//rdpwsx/rdpex/tsrvinfo.c:249:TSrvAllocInfo(OUT PTSRVINFO *ppTSrvInfo,

.//rdpwsx/rdpex/tsrvinfo.c:257: "TShrSRV: TSrvAllocInfo entry\n"));

.//rdpwsx/rdpex/tsrvinfo.c:263: pTSrvInfo = TSrvAllocInfoNew();

.//rdpwsx/rdpex/tsrvinfo.c:295: "TShrSRV: TSrvAllocInfo exit - 0x%x\n", ntStatus));

NTSTATUS

TSrvAllocInfo(OUT PTSRVINFO *ppTSrvInfo,

IN HANDLE hIca,

IN HANDLE hStack)

{

NTSTATUS ntStatus;

PTSRVINFO pTSrvInfo;

TRACE((DEBUG_TSHRSRV_FLOW,

"TShrSRV: TSrvAllocInfo entry\n"));

ntStatus = STATUS_NO_MEMORY;

// Try allocating a TSRVINFO object

pTSrvInfo = TSrvAllocInfoNew();

// If we managed to get a TSRVINFO object, perform

// default base initialization

if (pTSrvInfo)

{

pTSrvInfo->hDomain = NULL;

pTSrvInfo->hIca = hIca;

pTSrvInfo->hStack = hStack;

pTSrvInfo->fDisconnect = FALSE;

pTSrvInfo->fuConfState = TSRV_CONF_PENDING;

pTSrvInfo->ulReason = 0;

pTSrvInfo->ntStatus = STATUS_SUCCESS;

pTSrvInfo->bSecurityEnabled = FALSE;

pTSrvInfo->SecurityInfo.CertType = CERT_TYPE_INVALID;

// Base init complete - now bind the Ica stack

ntStatus = TSrvBindStack(pTSrvInfo);

第五部分:

chenghao@chenghaodeiMac termsrv % grep "TSrvBindStack" -nr ./

Binary file .//rdpwsx/rdpex/obj/i386/tsrvinfo.obj matches

Binary file .//rdpwsx/rdpex/obj/i386/rdpwsx.pdb matches

Binary file .//rdpwsx/rdpex/obj/i386/tsrvcom.obj matches

.//rdpwsx/rdpex/tsrvcom.h:35:EXTERN_C NTSTATUS TSrvBindStack(PTSRVINFO pTSrvInfo);

.//rdpwsx/rdpex/tsrvcom.c:893:// TSrvBindStack()

.//rdpwsx/rdpex/tsrvcom.c:905:TSrvBindStack(IN PTSRVINFO pTSrvInfo)

.//rdpwsx/rdpex/tsrvcom.c:911: "TShrSRV: TSrvBindStack entry\n"));

.//rdpwsx/rdpex/tsrvcom.c:941: "TShrSRV: TSrvBindStack exit - 0x%x\n", ntStatus));

.//rdpwsx/rdpex/tsrvinfo.c:282: ntStatus = TSrvBindStack(pTSrvInfo);

//

//*************************************************************

NTSTATUS

TSrvBindStack(IN PTSRVINFO pTSrvInfo)

{

NTSTATUS ntStatus;

GCCError GCCrc;

TRACE((DEBUG_TSHRSRV_FLOW,

"TShrSRV: TSrvBindStack entry\n"));

TS_ASSERT(pTSrvInfo);

TS_ASSERT(pTSrvInfo->hStack);

TRACE((DEBUG_TSHRSRV_NORMAL, "TShrSRV: Binding Ica stack\n"));

GCCrc = GCCConferenceInit(pTSrvInfo->hIca,

pTSrvInfo->hStack,

pTSrvInfo,

&pTSrvInfo->hDomain);

if (GCCrc == GCC_NO_ERROR)

{

ntStatus = STATUS_SUCCESS;

第六部分:

chenghao@chenghaodeiMac termsrv % grep "GCCConferenceInit" -nr ./|grep -v "inary"

.//inc/tgcc.h:724:GCCConferenceInit(

.//rdpwsx/gcc/tgcc.c:247:// GCCConferenceInit()

.//rdpwsx/gcc/tgcc.c:261:GCCConferenceInit(HANDLE hIca,

.//rdpwsx/gcc/tgcc.c:270: "GCC: GCCConferenceInit entry\n"));

.//rdpwsx/gcc/tgcc.c:297: "GCC: GCCConferenceInit exit - 0x%x\n",

.//rdpwsx/rdpex/tsrvcom.c:918: GCCrc = GCCConferenceInit(pTSrvInfo->hIca,

chenghao@chenghaodeiMac termsrv %

GCCError

APIENTRY

GCCConferenceInit(HANDLE hIca,

HANDLE hStack,

PVOID pvContext,

DomainHandle *phDomain)

{

MCSError mcsError;

GCCError gccError;

TRACE((DEBUG_GCC_DBFLOW,

"GCC: GCCConferenceInit entry\n"));

if (gccIsInitialized(&gccError))

{

TS_ASSERT(hIca);

TS_ASSERT(hStack);

TS_ASSERT(pvContext);

TS_ASSERT(phDomain);

TRACE((DEBUG_GCC_DBDEBUG,

"GCC: Calling MCSCreateDomain - hIca 0x%x, hStack 0x%x, "

"pvContext 0x%x, phDomain 0x%x\n",

hIca, hStack, pvContext, phDomain));

mcsError = MCSCreateDomain(hIca, hStack, pvContext, phDomain);

第七部分:

chenghao@chenghaodeiMac termsrv % grep "MCSCreateDomain" -nr ./|grep -v "inary"

.//rdpwsx/mcsmux/mcsapi.c:516:MCSError APIENTRY MCSCreateDomain(

.//rdpwsx/mcsmux/mcsapi.c:691: // originally incremented for GCC in MCSCreateDomain().

.//rdpwsx/gcc/tgcc.c:280: "GCC: Calling MCSCreateDomain - hIca 0x%x, hStack 0x%x, "

.//rdpwsx/gcc/tgcc.c:284: mcsError = MCSCreateDomain(hIca, hStack, pvContext, phDomain);

.//rdpwsx/gcc/tgcc.c:287: "MCSCreateDomain");

.//rdpwsx/gcc/tgcc.c:290: "GCC: MCSCreateDomain: domain 0x%x\n",

.//common/inc/mcs.h:35:MCSError APIENTRY MCSCreateDomain(

/*

* MUX-only non-MCS primitive function to allow ICA code to inject a new entry

* into the MUX-internal domain/stack database.

*/

MCSError APIENTRY MCSCreateDomain(

HANDLE hIca,

HANDLE hIcaStack,

void *pContext,

DomainHandle *phDomain)

{

NTSTATUS status;

Domain *pDomain;

NTSTATUS Status;

IoctlHeader StartIoctl;

CheckInitialized("CreateDomain()");

// Init the receiver's data to NULL to ensure a fault if they skip the

// error code.

*phDomain = NULL;

pDomain = Malloc(sizeof(Domain));

if (pDomain != NULL) {

// Create and enter the locking critical section.

status = RtlInitializeCriticalSection(&pDomain->csLock);

if (status == STATUS_SUCCESS) {

EnterCriticalSection(&pDomain->csLock);

#if MCS_Future

pDomain->SelLen = 0;

#endif

pDomain->hIca = hIca;

pDomain->hIcaStack = hIcaStack;

pDomain->NCUserDefined = pContext;

pDomain->State = Dom_Unconnected;

pDomain->Overlapped.hEvent = NULL;

pDomain->Overlapped.Offset = pDomain->Overlapped.OffsetHigh = 0;

pDomain->RefCount = 0;

// Take a reference count for the caller of this function (GCC).

MCSReferenceDomain(pDomain);

// Open T.120 ICA virtual channel.

Status = IcaChannelOpen(hIca, Channel_Virtual, Virtual_T120,

&pDomain->hIcaT120Channel);

if (!NT_SUCCESS(Status)) {

ErrOutIca(hIca, "CreateDomain: Error opening virtual channel");

goto PostInitCS;

}

// Add the hIcaT120 Channel to the handles associated with the

// main I/O completion port. We use pDomain as the completion key

// so we can find it during callback processing.

if (CreateIoCompletionPort(pDomain->hIcaT120Channel, g_hIoPort,

(ULONG_PTR)pDomain, MaxIoPortThreads) == NULL) {

ErrOutIca(hIca, "CreateDomain(): Could not add ICA channel to "

"I/O completion port");

goto PostChannel;

}

// Tell kernel mode to start the MCS I/O.

StartIoctl.hUser = NULL;

StartIoctl.Type = MCS_T120_START;

Status = IcaStackIoControl(hIcaStack, IOCTL_T120_REQUEST,

&StartIoctl, sizeof(StartIoctl), NULL, 0, NULL);

if (!NT_SUCCESS(Status)) {

ErrOutIca(hIca, "Could not start kernel T120 I/O");

goto PostChannel;

}

第八部分:

chenghao@chenghaodeiMac termsrv % grep "MCSIcaT120Request" -nr ./|grep -v "inary"

.//inc/mcskernl.h:49:NTSTATUS MCSIcaT120Request(DomainHandle, PSD_IOCTL);

.//drivers/rdp/pdmcs/icaiface.c:330:NTSTATUS MCSIcaT120Request(DomainHandle hDomain, PSD_IOCTL pSdIoctl)

.//drivers/rdp/rdpwd/nwdwcpp.cpp:1331: status = MCSIcaT120Request(pTSWd->hDomainKernel, pSdIoctl);

/****************************************************************************/

// WD_Ioctl

//

// Query/Set configuration information for the WD.

/****************************************************************************/

NTSTATUS WD_Ioctl(PTSHARE_WD pTSWd, PSD_IOCTL pSdIoctl)

{

/********************************************************************/

/* T.120 request from user mode - pass it on */

/********************************************************************/

case IOCTL_T120_REQUEST:

{

status = MCSIcaT120Request(pTSWd->hDomainKernel, pSdIoctl);

}

break;

第九部分:

/*

* 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);

}

/*

* 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,

};

第十部分:

chenghao@chenghaodeiMac termsrv % grep "T120StartFunc" -nr ./|grep -v "inary"

.//drivers/rdp/pdmcs/mcsioctl.c:29:NTSTATUS T120StartFunc(PDomain, PSD_IOCTL);

.//drivers/rdp/pdmcs/mcsioctl.c:63: T120StartFunc,

.//drivers/rdp/pdmcs/mcsioctl.c:581:NTSTATUS T120StartFunc(Domain *pDomain, PSD_IOCTL pSdIoctl)

.//drivers/rdp/pdmcs/mcsioctl.c:610: WarnOut1(pDomain->pContext, "T120StartFunc(): "

.//drivers/rdp/pdmcs/mcsioctl.c:625: TraceOut(pDomain->pContext, "T120StartFunc(): Sending X.224 response");

.//drivers/rdp/pdmcs/mcsioctl.c:632: "T120StartFunc(): Domain state not State_X224_Requesting, "

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;

}

第十一部分:

chenghao@chenghaodeiMac termsrv % grep "SendX224Confirm" -nr ./|grep -v "inary"

.//drivers/rdp/pdmcs/mcsimpl.h:457:NTSTATUS SendX224Confirm(Domain *);

.//drivers/rdp/pdmcs/decode.c:122:NTSTATUS SendX224Confirm(Domain *pDomain)

.//drivers/rdp/pdmcs/decode.c:298: Status = SendX224Confirm(pDomain);

.//drivers/rdp/pdmcs/mcsioctl.c:626: Status = SendX224Confirm(pDomain);

.//drivers/rdp/pdmcs/icaiface.c:319: Status = SendX224Confirm(pDomain);

/*

* Utility function to send an X.224 connection response. Used by

* DecodeWireData() and when a T120_START is sent indicating the

* stack is up.

*/

NTSTATUS SendX224Confirm(Domain *pDomain)

{

POUTBUF pOutBuf;

NTSTATUS Status;

pDomain->State = State_X224_Connected;

// This PDU send is vital to the connection and must succeed.

// Keep retrying the allocation until it succeeds.

do {

// Allow the call to wait for a buffer.

Status = IcaBufferAlloc(pDomain->pContext, TRUE, FALSE,

X224_ConnectionConPacketSize, NULL, &pOutBuf);

if (Status != STATUS_SUCCESS) // NT_SUCCESS() does not fail STATUS_TIMEOUT

ErrOut(pDomain->pContext,

"Could not alloc X.224 connect-confirm OutBuf, retrying");

} while (Status != STATUS_SUCCESS);

// Use a bogus source port number for the confirm. This is

// not used by either side.

CreateX224ConnectionConfirmPacket(pOutBuf->pBuffer,

pDomain->X224SourcePort, 0x1234);

pOutBuf->ByteCount = X224_ConnectionConPacketSize;

Status = SendOutBuf(pDomain, pOutBuf);

if (!NT_SUCCESS(Status)) {

ErrOut(pDomain->pContext,

"Unable to send X.224 connection-confirm");