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