/****************************************************************************/ // mcsint.cpp // // MCS internal portable functions. // // Copyright (C) 1997-1999 Microsoft Corporation /****************************************************************************/ #include extern "C" { #define TRC_FILE "amcsint" #define TRC_GROUP TRC_GROUP_NETWORK #include } #include "autil.h" #include "mcs.h" #include "cd.h" #include "xt.h" #include "nc.h" #include "nl.h" /****************************************************************************/ /* Name: MCSSendConnectInitial */ /* */ /* Purpose: This function generates and sends a MCS connect-initial PDU. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSendConnectInitial(ULONG_PTR unused) { XT_BUFHND bufHandle; PDCUINT8 pData = NULL; DCUINT pduLength; DCUINT dataLength; DCBOOL intRC; MCS_PDU_CONNECTINITIAL ciPDU = MCS_DATA_CONNECTINITIAL; DC_BEGIN_FN("MCSSendConnectInitial"); DC_IGNORE_PARAMETER(unused); /************************************************************************/ /* Calculate the size of the data to send. The pdu length is the size */ /* of the Connect-Initial header plus the user data. The data length */ /* is the length transmitted in the length field of the PDU, which */ /* doesn't include the PDU type (2 bytes) or the length field (3 */ /* bytes). Thus we need to subtract 5 bytes. */ /************************************************************************/ pduLength = sizeof(ciPDU) + _MCS.userDataLength; dataLength = pduLength - 5; TRC_NRM((TB, _T("CI total length:%u (data:%u) (hc:%u user-data:%u)"), pduLength, dataLength, sizeof(ciPDU), _MCS.userDataLength)); /************************************************************************/ /* Assume that the total CI length is less than the maximum MCS send */ /* packet size. */ /************************************************************************/ TRC_ASSERT((dataLength <= MCS_MAX_SNDPKT_LENGTH), (TB, _T("Datalength out of range: %u"), dataLength)); TRC_ASSERT((_MCS.pReceivedPacket != NULL), (TB, _T("Null rcv packet buffer"))); /************************************************************************/ /* Update the MCS CI header with the data size. */ /************************************************************************/ ciPDU.length = MCSLocalToWire16((DCUINT16)dataLength); /************************************************************************/ /* Update the MCS user-data octet string length. */ /************************************************************************/ ciPDU.udLength = MCSLocalToWire16((DCUINT16)_MCS.userDataLength); /************************************************************************/ /* Get a private buffer from XT. */ /************************************************************************/ intRC = _pXt->XT_GetPrivateBuffer(pduLength, &pData, &bufHandle); if (!intRC) { /********************************************************************/ /* We've failed to get a private buffer. This ONLY happens when TD */ /* has disconnected while the layers above are still trying to */ /* connect. Since TD has now disconnected and is refusing to give */ /* us a buffer we might as well just give up trying to get a */ /* buffer. */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get a private buffer - just quit"))); DC_QUIT; } /************************************************************************/ /* Now fill in the buffer that we've just got. */ /************************************************************************/ DC_MEMCPY(pData, &ciPDU, sizeof(ciPDU)); DC_MEMCPY((pData + sizeof(ciPDU)), _MCS.pReceivedPacket, _MCS.userDataLength); /************************************************************************/ /* Trace out the PDU. */ /************************************************************************/ TRC_DATA_NRM("Connect-Initial PDU", pData, pduLength); /************************************************************************/ /* Send the buffer. If everything has worked OK, we should receive a */ /* Connect-Response PDU shortly. */ /************************************************************************/ _pXt->XT_SendBuffer(pData, pduLength, bufHandle); DC_EXIT_POINT: DC_END_FN(); } /* MCSSendConnectInitial */ /****************************************************************************/ /* Name: MCSSendErectDomainRequest */ /* */ /* Purpose: Generates and sends an Erect-Domain-Request (EDrq) PDU. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSendErectDomainRequest(ULONG_PTR unused) { PDCUINT8 pData = NULL; XT_BUFHND bufHandle; DCBOOL intRC; MCS_PDU_ERECTDOMAINREQUEST edrPDU = MCS_DATA_ERECTDOMAINREQUEST; DC_BEGIN_FN("MCSSendErectDomainRequest"); DC_IGNORE_PARAMETER(unused); /************************************************************************/ /* Get a internal send buffer from XT. */ /************************************************************************/ intRC = _pXt->XT_GetPrivateBuffer(sizeof(edrPDU), &pData, &bufHandle); if (!intRC) { /********************************************************************/ /* We've failed to get a private buffer. This ONLY happens when TD */ /* has disconnected while the layers above are still trying to */ /* connect. Since TD has now disconnected and is refusing to give */ /* us a buffer we might as well just give up trying to get a */ /* buffer. */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get a private buffer - just quit"))); DC_QUIT; } /************************************************************************/ /* Now fill in the buffer with the AUR PDU. */ /************************************************************************/ DC_MEMCPY(pData, &edrPDU, sizeof(edrPDU)); TRC_DATA_NRM("EDR PDU:", &edrPDU, sizeof(edrPDU)); /************************************************************************/ /* Now send the buffer. */ /************************************************************************/ TRC_NRM((TB, _T("Sending EDR PDU..."))); _pXt->XT_SendBuffer(pData, sizeof(edrPDU), bufHandle); DC_EXIT_POINT: DC_END_FN(); } /* MCSSendErectDomainRequest */ /****************************************************************************/ /* Name: MCSSendAttachUserRequest */ /* */ /* Purpose: Generates and sends an MCS Attach-User-Request (AUrq) PDU. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSendAttachUserRequest(ULONG_PTR unused) { PDCUINT8 pData = NULL; XT_BUFHND bufHandle; DCBOOL intRC; MCS_PDU_ATTACHUSERREQUEST aurPDU = MCS_DATA_ATTACHUSERREQUEST; DC_BEGIN_FN("MCSSendAttachUserRequest"); DC_IGNORE_PARAMETER(unused); /************************************************************************/ /* Get a internal send buffer from XT. */ /************************************************************************/ intRC = _pXt->XT_GetPrivateBuffer(sizeof(aurPDU), &pData, &bufHandle); if (!intRC) { /********************************************************************/ /* We've failed to get a private buffer. This ONLY happens when TD */ /* has disconnected while the layers above are still trying to */ /* connect. Since TD has now disconnected and is refusing to give */ /* us a buffer we might as well just give up trying to get a */ /* buffer. */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get a private buffer - just quit"))); DC_QUIT; } /************************************************************************/ /* Now fill in the buffer with the AUR PDU. */ /************************************************************************/ DC_MEMCPY(pData, &aurPDU, sizeof(aurPDU)); TRC_DATA_NRM("AUR PDU:", &aurPDU, sizeof(aurPDU)); /************************************************************************/ /* Now send the buffer. */ /************************************************************************/ TRC_NRM((TB, _T("Sending AUR PDU..."))); _pXt->XT_SendBuffer(pData, sizeof(aurPDU), bufHandle); DC_EXIT_POINT: DC_END_FN(); return; } /* MCSSendAttachUserRequest */ /****************************************************************************/ /* Name: MCSSendChannelJoinRequest */ /* */ /* Purpose: Generates and sends a Channel-Join-Request (CJrq) PDU. */ /* */ /* Params: IN channelID - the channel ID to join. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSendChannelJoinRequest(PDCVOID pData, DCUINT dataLen) { PDCUINT8 pBuffer = NULL; XT_BUFHND bufHandle; DCBOOL intRC; MCS_PDU_CHANNELJOINREQUEST cjrPDU = MCS_DATA_CHANNELJOINREQUEST; PMCS_DECOUPLEINFO pDecoupleInfo = (PMCS_DECOUPLEINFO)pData; DC_BEGIN_FN("MCSSendChannelJoinRequest"); DC_IGNORE_PARAMETER(dataLen); TRC_NRM((TB, _T("Join channel:%#x for user:%#x"), pDecoupleInfo->channel, pDecoupleInfo->userID)); /************************************************************************/ /* Assert that the hiword of the channel is 0. */ /************************************************************************/ TRC_ASSERT((0 == HIWORD((DCUINT32)pDecoupleInfo->channel)), (TB, _T("Hi-word of channel is non-zero"))); /************************************************************************/ /* Assert that the hiword of the user-id is 0. */ /************************************************************************/ TRC_ASSERT((0 == HIWORD((DCUINT32)pDecoupleInfo->userID)), (TB, _T("Hi-word of userID is non-zero"))); /************************************************************************/ /* Add the channel and user ids. */ /************************************************************************/ cjrPDU.initiator = MCSLocalUserIDToWireUserID((DCUINT16)pDecoupleInfo->userID); cjrPDU.channelID = MCSLocalToWire16((DCUINT16)pDecoupleInfo->channel); /************************************************************************/ /* Get a internal send buffer from XT. */ /************************************************************************/ intRC = _pXt->XT_GetPrivateBuffer(sizeof(cjrPDU), &pBuffer, &bufHandle); if (!intRC) { /********************************************************************/ /* We've failed to get a private buffer. This ONLY happens when TD */ /* has disconnected while the layers above are still trying to */ /* connect. Since TD has now disconnected and is refusing to give */ /* us a buffer we might as well just give up trying to get a */ /* buffer. */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get a private buffer - just quit"))); DC_QUIT; } /************************************************************************/ /* Now fill in the buffer with the CJR PDU. */ /************************************************************************/ DC_MEMCPY(pBuffer, &cjrPDU, sizeof(cjrPDU)); TRC_DATA_NRM("CJR PDU:", &cjrPDU, sizeof(cjrPDU)); /************************************************************************/ /* Now send the buffer. */ /************************************************************************/ TRC_NRM((TB, _T("Sending Channel-Join-Request PDU..."))); _pXt->XT_SendBuffer(pBuffer, sizeof(cjrPDU), bufHandle); DC_EXIT_POINT: DC_END_FN(); } /* MCSSendChannelJoinRequest */ /****************************************************************************/ /* Name: MCSSendDisconnectProviderUltimatum */ /* */ /* Purpose: Generates and sends a Disconnect-Provider-Ultimatum (DPum) */ /* PDU with the reason code set to rn-user-requested. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSendDisconnectProviderUltimatum(ULONG_PTR unused) { PDCUINT8 pData = NULL; XT_BUFHND bufHandle; DCBOOL intRC; MCS_PDU_DISCONNECTPROVIDERUM dpumPDU = MCS_DATA_DISCONNECTPROVIDERUM; DC_BEGIN_FN("MCSSendDisconnectProviderUltimatum"); DC_IGNORE_PARAMETER(unused); /************************************************************************/ /* Get a internal send buffer from XT. */ /************************************************************************/ intRC = _pXt->XT_GetPrivateBuffer(sizeof(dpumPDU), &pData, &bufHandle); if (!intRC) { /********************************************************************/ /* We've failed to get a private buffer. This ONLY happens when TD */ /* has disconnected while the layers above are still trying to */ /* connect. Since TD has now disconnected and is refusing to give */ /* us a buffer we might as well just give up trying to get a */ /* buffer. */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get a private buffer - just quit"))); DC_QUIT; } /************************************************************************/ /* Now fill in the buffer with the DPum PDU. */ /************************************************************************/ DC_MEMCPY(pData, &dpumPDU, sizeof(dpumPDU)); TRC_DATA_NRM("DPUM PDU:", &dpumPDU, sizeof(dpumPDU)); /************************************************************************/ /* Now send the buffer. */ /************************************************************************/ TRC_NRM((TB, _T("Sending Disconnect-Provider-Ultimatum PDU..."))); _pXt->XT_SendBuffer(pData, sizeof(dpumPDU), bufHandle); DC_EXIT_POINT: /************************************************************************/ /* We don't get any feedback for this PDU (i.e. there is no */ /* Disconnect-Provider-Confirm PDU), so we need to decouple back to */ /* the receiver thread and get it to begin disconnecting the layers */ /* below. */ /************************************************************************/ TRC_NRM((TB, _T("Decouple to receiver thrd and call MCSContinueDisconnect"))); _pCd->CD_DecoupleSimpleNotification(CD_RCV_COMPONENT, this, CD_NOTIFICATION_FUNC(CMCS,MCSContinueDisconnect), (ULONG_PTR) 0); DC_END_FN(); } /* MCSSendDisconnectProviderUltimatum */ /****************************************************************************/ /* Name: MCSContinueDisconnect */ /* */ /* Purpose: Continue the disconnect processing on the receiver thread */ /* after having sent a MCS DPum on the sender thread. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSContinueDisconnect(ULONG_PTR unused) { DC_BEGIN_FN("MCSContinueDisconnect"); DC_IGNORE_PARAMETER(unused); /************************************************************************/ /* Just call XT_Disconnect. */ /************************************************************************/ TRC_NRM((TB, _T("Disconnect lower layers - call XT_Disconnect"))); _pXt->XT_Disconnect(); DC_END_FN(); } /* MCSContinueDisconnect */ /****************************************************************************/ /* Name: MCSGetSDRHeaderLength */ /* */ /* Purpose: This function calculates the length of a Send-Data-Request */ /* PDU header based on the length of the data passed in. */ /* */ /* Returns: The length of the SDR header required for dataLength bytes */ /* of data. */ /* */ /* Params: IN dataLength - the length of the data to base the header */ /* calculation on. */ /****************************************************************************/ DCUINT DCINTERNAL CMCS::MCSGetSDRHeaderLength(DCUINT dataLength) { DCUINT headerLength; DC_BEGIN_FN("MCSGetSDRHeaderLength"); /************************************************************************/ /* Check that we're being asked to send less than the maximum amount */ /* of data. */ /************************************************************************/ TRC_ASSERT((dataLength < MCS_MAX_SNDPKT_LENGTH), (TB, _T("Too much data to send:%u"), dataLength)); /************************************************************************/ /* Calculate the maximum size of a MCS data header. This is comprised */ /* of a constant length part (which contains pkt type, user-id etc) and */ /* a variable size field which encodes the length of the user-data */ /* according to the PER encoding rules. */ /* */ /* First get the length of the constant part. */ /************************************************************************/ headerLength = sizeof(MCS_PDU_SENDDATAREQUEST); /************************************************************************/ /* Now use the length of the data to calculate how many bytes are */ /* required to encode it. */ /************************************************************************/ if (dataLength < 128) { /********************************************************************/ /* We need only one byte to encode the length of the data. */ /********************************************************************/ headerLength += 1; } else { /********************************************************************/ /* We need two bytes to encode the length of the data. */ /********************************************************************/ headerLength += 2; } TRC_DBG((TB, _T("Returning header length of:%u for data length:%u"), headerLength, dataLength)); DC_END_FN(); return(headerLength); } /* MCSGetSDRHeaderLength */ /****************************************************************************/ /* Name: MCSRecvToHdrBuf */ /* */ /* Purpose: Receives data into the header buffer. */ /* */ /* Returns: TRUE if the receive bytes needed count is zero, FALSE */ /* otherwise. */ /****************************************************************************/ DCBOOL DCINTERNAL CMCS::MCSRecvToHdrBuf(DCVOID) { DCUINT bytesRecv; DCBOOL rc; DC_BEGIN_FN("MCSRecvToHdrBuf"); TRC_ASSERT((NULL != _MCS.pHdrBuf), (TB, _T("No MCS header buffer!"))); /************************************************************************/ /* Make sure that we're expected to receive some data. */ /************************************************************************/ TRC_ASSERT((_MCS.hdrBytesNeeded != 0), (TB, _T("No data to receive"))); /************************************************************************/ /* Reallocate a larger buffer for the header if the current one is too */ /* small to contain the incoming data. */ /************************************************************************/ if( _MCS.hdrBufLen < _MCS.hdrBytesRead + _MCS.hdrBytesNeeded ) { PDCUINT8 pNewHdrBuf; pNewHdrBuf = (PDCUINT8)UT_Malloc( _pUt, _MCS.hdrBytesRead + _MCS.hdrBytesNeeded ); if( NULL == pNewHdrBuf ) { TRC_ABORT((TB, _T("Cannot allocate memory to receive MCS header (%u)"), _MCS.hdrBytesNeeded + _MCS.hdrBytesRead)); return( FALSE ); } DC_MEMCPY( pNewHdrBuf, _MCS.pHdrBuf, _MCS.hdrBytesRead ); UT_Free( _pUt, _MCS.pHdrBuf ); _MCS.pHdrBuf = pNewHdrBuf; _MCS.hdrBufLen = _MCS.hdrBytesRead + _MCS.hdrBytesNeeded; } /************************************************************************/ /* Get some data into the header buffer. */ /************************************************************************/ bytesRecv = _pXt->XT_Recv(_MCS.pHdrBuf + _MCS.hdrBytesRead, _MCS.hdrBytesNeeded); TRC_DBG((TB, _T("Received %u of %u needed bytes"), bytesRecv, _MCS.hdrBytesNeeded)); /************************************************************************/ /* Update the receive byte counts. */ /************************************************************************/ _MCS.hdrBytesNeeded -= bytesRecv; _MCS.hdrBytesRead += bytesRecv; /************************************************************************/ /* Determine if we've got all the bytes that we need. */ /************************************************************************/ if (0 == _MCS.hdrBytesNeeded) { /********************************************************************/ /* We've got all that we were asked for - return TRUE. */ /********************************************************************/ TRC_DBG((TB, _T("Got all the bytes needed"))); rc = TRUE; } else { /********************************************************************/ /* We need to wait for some more bytes. */ /********************************************************************/ TRC_NRM((TB, _T("Wait for %u more bytes"), _MCS.hdrBytesNeeded)); rc = FALSE; } DC_END_FN(); return(rc); } /* MCSRecvToHdrBuf */ /****************************************************************************/ /* Name: MCSGetPERInfo */ /* */ /* Purpose: This function identifies PER PDUs based on the first byte in */ /* the header buffer and also calculates how many additional */ /* header bytes are needed for a complete PDU. */ /* */ /* Params: OUT pType - the PDU type. */ /* OUT pSize - the number of additional header bytes needed. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSGetPERInfo(PDCUINT pType, PDCUINT pSize) { DC_BEGIN_FN("MCSGetPERInfo"); TRC_ASSERT((NULL != pType), (TB, _T("pType is NULL"))); TRC_ASSERT((NULL != pSize), (TB, _T("pSize is NULL"))); /************************************************************************/ /* Trace out the header buffer. */ /************************************************************************/ TRC_DATA_DBG("Header buffer contains", _MCS.pHdrBuf, _MCS.hdrBytesRead); /************************************************************************/ /* This a PER encoded PDU. The six most-significant bits of the first */ /* byte contain the PDU type - mask out the remainder. */ /************************************************************************/ *pType = _MCS.pHdrBuf[0] & MCS_PDUTYPEMASK; /************************************************************************/ /* Check for PDU types that we don't expect to receive. If we get any */ /* of the following then we disconnect as the server is asking us to do */ /* things that we can't do - and which will require a response from us. */ /************************************************************************/ if ((MCS_TYPE_ATTACHUSERREQUEST == *pType) || (MCS_TYPE_DETACHUSERREQUEST == *pType) || (MCS_TYPE_CHANNELJOINREQUEST == *pType) || (MCS_TYPE_SENDDATAREQUEST == *pType)) { TRC_ABORT((TB, _T("Unexpected MCS PDU type:%#x"), *pType)); MCSSetReasonAndDisconnect(NL_ERR_MCSUNEXPECTEDPDU); DC_QUIT; } /************************************************************************/ /* Now calculate the number of bytes that we still need for the */ /* different PDU types. This is the size of the PDU minus the number */ /* of bytes that we've read so far. */ /************************************************************************/ switch (*pType) { case MCS_TYPE_SENDDATAINDICATION: { *pSize = sizeof(MCS_PDU_SENDDATAINDICATION) - _MCS.hdrBytesRead; TRC_DBG((TB, _T("MCS_PDU_SENDDATAINDICATION (%#x) read:%u need:%u"), *pType, _MCS.hdrBytesRead, *pSize)); } break; case MCS_TYPE_ATTACHUSERCONFIRM: { /****************************************************************/ /* The user-id is optional, so determine if it is present. */ /****************************************************************/ if (_MCS.pHdrBuf[0] & MCS_AUC_OPTIONALUSERIDMASK) { /************************************************************/ /* The user-id is present. */ /************************************************************/ TRC_NRM((TB, _T("Optional user-id is present in AUC"))); *pSize = sizeof(MCS_PDU_ATTACHUSERCONFIRMFULL) - _MCS.hdrBytesRead; } else { /************************************************************/ /* The user-id is NOT present. */ /************************************************************/ TRC_NRM((TB, _T("Optional user-id is NOT present in AUC"))); *pSize = sizeof(MCS_PDU_ATTACHUSERCONFIRMCOMMON) - _MCS.hdrBytesRead; } TRC_NRM((TB, _T("MCS_PDU_ATTACHUSERCONFIRM (%#x) read:%u need:%u"), *pType, _MCS.hdrBytesRead, *pSize)); } break; case MCS_TYPE_DETACHUSERINDICATION: { *pSize = sizeof(MCS_PDU_DETACHUSERINDICATION) - _MCS.hdrBytesRead; TRC_NRM((TB, _T("MCS_PDU_DETACHUSERINDICATION (%#x) read:%u need:%u"), *pType, _MCS.hdrBytesRead, *pSize)); } break; case MCS_TYPE_CHANNELJOINCONFIRM: { /****************************************************************/ /* The channel-id is optional, so determine if it is present. */ /****************************************************************/ if (_MCS.pHdrBuf[0] & MCS_CJC_OPTIONALCHANNELIDMASK) { /************************************************************/ /* The channel-id is present. */ /************************************************************/ TRC_NRM((TB, _T("Optional channel-id is present in CJC"))); *pSize = sizeof(MCS_PDU_CHANNELJOINCONFIRMFULL) - _MCS.hdrBytesRead; } else { /************************************************************/ /* The channel-id is NOT present. */ /************************************************************/ TRC_NRM((TB, _T("Optional channel-id is NOT present in CJC"))); *pSize = sizeof(MCS_PDU_CHANNELJOINCONFIRMCOMMON) - _MCS.hdrBytesRead; } TRC_NRM((TB, _T("MCS_PDU_CHANNELJOINCONFIRM (%#x) read:%u need:%u"), *pType, _MCS.hdrBytesRead, *pSize)); } break; case MCS_TYPE_DISCONNECTPROVIDERUM: { *pSize = sizeof(MCS_PDU_DISCONNECTPROVIDERUM) - _MCS.hdrBytesRead; TRC_NRM((TB, _T("MCS_PDU_DISCONNECTPROVIDERUM (%#x) read:%u need:%u"), *pType, _MCS.hdrBytesRead, *pSize)); } break; default: { /****************************************************************/ /* This is an unexpected MCS PDU - disconnect, as something has */ /* gone horribly wrong! */ /****************************************************************/ TRC_ABORT((TB, _T("Unexpected MCS PDU type:%#x"), *pType)); MCSSetReasonAndDisconnect(NL_ERR_MCSUNEXPECTEDPDU); *pSize = 0; DC_QUIT; } break; } DC_EXIT_POINT: DC_END_FN(); } /* MCSGetPERInfo */ /****************************************************************************/ /* Name: MCSHandleControlPkt */ /* */ /* Purpose: This function handles a MCS control packet which is located */ /* in the header buffer. After identifying the PDU type it */ /* calls a NC callback. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSHandleControlPkt(DCVOID) { DCUINT pduType; DCUINT pduSize; DC_BEGIN_FN("MCSHandleControlPkt"); /************************************************************************/ /* Trace out the header buffer contents. */ /************************************************************************/ TRC_DATA_NRM("Header bytes read:", _MCS.pHdrBuf, _MCS.hdrBytesRead); /************************************************************************/ /* Work out the PDU type. */ /************************************************************************/ if (MCS_BER_CONNECT_PREFIX == _MCS.pHdrBuf[0]) { /********************************************************************/ /* This is a BER encoded PDU. The next byte is the type. */ /********************************************************************/ pduType = _MCS.pHdrBuf[1]; } else { /********************************************************************/ /* This is a PER encoded PDU. Get the PDU type. */ /********************************************************************/ MCSGetPERInfo(&pduType, &pduSize); } /************************************************************************/ /* Now switch on the PDU type. */ /************************************************************************/ TRC_NRM((TB, _T("PDU type:%#x"), pduType)); switch (pduType) { case MCS_TYPE_CONNECTRESPONSE: { TRC_NRM((TB, _T("Connect response PDU received"))); MCSHandleCRPDU(); } break; case MCS_TYPE_ATTACHUSERCONFIRM: { PMCS_PDU_ATTACHUSERCONFIRMCOMMON pAUCCommon; PMCS_PDU_ATTACHUSERCONFIRMFULL pAUCFull; DCUINT result; DCUINT16 userID; TRC_NRM((TB, _T("MCS Attach-User-Confirm PDU received"))); /****************************************************************/ /* Determine if this PDU includes the optional user-id as well. */ /****************************************************************/ if (!(_MCS.pHdrBuf[0] & MCS_AUC_OPTIONALUSERIDMASK)) { TRC_ABORT((TB, _T("Optional user-id NOT present in AUC"))); MCSSetReasonAndDisconnect(NL_ERR_MCSNOUSERIDINAUC); DC_QUIT; } /****************************************************************/ /* Cast the header buffer in the form of the common part of */ /* this PDU. */ /****************************************************************/ pAUCCommon = (PMCS_PDU_ATTACHUSERCONFIRMCOMMON)_MCS.pHdrBuf; /****************************************************************/ /* Pull out the result code from the PDU and translate it. */ /****************************************************************/ result = MCSGetResult(pAUCCommon->typeResult, MCS_AUC_RESULTCODEOFFSET); /****************************************************************/ /* Retrieve the user-id as well. */ /****************************************************************/ pAUCFull = (PMCS_PDU_ATTACHUSERCONFIRMFULL)_MCS.pHdrBuf; userID = MCSWireUserIDToLocalUserID(pAUCFull->userID); TRC_NRM((TB, _T("Calling NC_OnMCSAUC - result:%u userID:%#x"), result, userID)); _pNc->NC_OnMCSAttachUserConfirm(result, userID); } break; case MCS_TYPE_CHANNELJOINCONFIRM: { PMCS_PDU_CHANNELJOINCONFIRMCOMMON pCJCCommon; PMCS_PDU_CHANNELJOINCONFIRMFULL pCJCFull; DCUINT16 channelID; DCUINT result; TRC_NRM((TB, _T("MCS Channel-Join-Confirm PDU received"))); /****************************************************************/ /* Determine if this PDU includes the optional channel-id as */ /* well. */ /****************************************************************/ if (!(_MCS.pHdrBuf[0] & MCS_CJC_OPTIONALCHANNELIDMASK)) { TRC_ABORT((TB, _T("Optional channel-id NOT present in CJC"))); MCSSetReasonAndDisconnect(NL_ERR_MCSNOCHANNELIDINCJC); DC_QUIT; } /****************************************************************/ /* Cast the header buffer in the form of this PDU. */ /****************************************************************/ pCJCCommon = (PMCS_PDU_CHANNELJOINCONFIRMCOMMON)_MCS.pHdrBuf; /****************************************************************/ /* Pull out the result code from the PDU and translate it. */ /****************************************************************/ result = MCSGetResult(pCJCCommon->typeResult, MCS_CJC_RESULTCODEOFFSET); /****************************************************************/ /* Retrieve the channel id. */ /****************************************************************/ pCJCFull = (PMCS_PDU_CHANNELJOINCONFIRMFULL)_MCS.pHdrBuf; channelID = MCSWireToLocal16(pCJCFull->channelID); TRC_NRM((TB, _T("Calling NC_OnMCSCJC - result:%u channelID:%#x"), result, channelID)); _pNc->NC_OnMCSChannelJoinConfirm(result, channelID); } break; case MCS_TYPE_DETACHUSERINDICATION: { /****************************************************************/ /* The following code is only compiled in if normal level */ /* tracing is enabled - otherwise we just ignore detach-user */ /* indications. The server will send us a disconnect-provider */ /* when it wants us to detach. */ /****************************************************************/ #ifdef TRC_ENABLE_NRM DCUINT16 userID; PMCS_PDU_DETACHUSERINDICATION pDUI; /****************************************************************/ /* Cast the header buffer in the form of this PDU. */ /****************************************************************/ pDUI = (PMCS_PDU_DETACHUSERINDICATION)_MCS.pHdrBuf; /****************************************************************/ /* Dig out the MCS user id and issue the NC callback. */ /****************************************************************/ userID = MCSWireUserIDToLocalUserID(pDUI->userID); TRC_NRM((TB, _T("MCS Detach-User-Indication PDU recv'd - userID:%#x"), userID)); #endif /* TRC_ENABLE_NRM */ } break; case MCS_TYPE_DISCONNECTPROVIDERUM: { PMCS_PDU_DISCONNECTPROVIDERUM pDPum; DCUINT reason; TRC_NRM((TB, _T("Disconnect Provider Ultimatum received"))); /****************************************************************/ /* Cast the header buffer in the form of this PDU. */ /****************************************************************/ pDPum = (PMCS_PDU_DISCONNECTPROVIDERUM)_MCS.pHdrBuf; /****************************************************************/ /* Pull out the reason code from the PDU. */ /****************************************************************/ reason = MCSGetReason(pDPum->typeReason, MCS_DPUM_REASONCODEOFFSET); TRC_ASSERT((reason <= MCS_REASON_CHANNEL_PURGED), (TB, _T("Unexpected MCS reason code:%u"), reason)); /****************************************************************/ /* Switch on the reason code. */ /****************************************************************/ switch (reason) { case MCS_REASON_PROVIDER_INITIATED: { /********************************************************/ /* The server has disconnected us. */ /********************************************************/ TRC_NRM((TB, _T("DPum with reason MCS_REASON_PROVIDER_INITIATED"))); _MCS.disconnectReason = NL_DISCONNECT_REMOTE_BY_SERVER; } break; case MCS_REASON_USER_REQUESTED: { /********************************************************/ /* We initiated the disconnection and the server */ /* concurred. */ /********************************************************/ TRC_NRM((TB, _T("DPum with reason MCS_REASON_USER_REQUESTED"))); _MCS.disconnectReason = NL_DISCONNECT_REMOTE_BY_USER; } break; default: { /********************************************************/ /* This is an unrecognized reason code. */ /********************************************************/ TRC_ABORT((TB, _T("Unexpected MCS reason code:%u"), reason)); _MCS.disconnectReason = NL_MAKE_DISCONNECT_ERR(NL_ERR_MCSBADMCSREASON); } break; } /****************************************************************/ /* Getting a DPum means we should disconnect so call XT to */ /* disconnect the lower layers. */ /****************************************************************/ _pXt->XT_Disconnect(); } break; default: { TRC_ABORT((TB, _T("Unrecognised PDU type:%#x"), pduType)); } break; } DC_EXIT_POINT: DC_END_FN(); return; } /* MCSHandleControlPkt */ /****************************************************************************/ /* Name: MCSHandleCRPDU */ /* */ /* Purpose: Handles a MCS Connect-Response PDU. This function splits out */ /* the MCS result code and user-data from the PDU and issues a */ /* callback to NC with these values. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSHandleCRPDU(DCVOID) { BOOL fBadFields = FALSE; PDCUINT8 pPDU; DCUINT length, nBERBytes; DCUINT i; DCUINT result = MCS_RESULT_UNSPECIFIED_FAILURE; DC_BEGIN_FN("MCSHandleCRPDU"); TRC_NRM((TB, _T("MCS Connect-Response PDU received"))); TRC_DATA_NRM("Connect-Response data:", _MCS.pHdrBuf, _MCS.hdrBytesRead); /************************************************************************/ /* Set our local pointer to the start of the PDU. */ /************************************************************************/ pPDU = _MCS.pHdrBuf; /************************************************************************/ /* Skip the PDU type fields. */ /************************************************************************/ pPDU += 2; /************************************************************************/ /* Skip the length field. Note that we add one to the result of */ /* MCSGetBERLengthSize as this function returns the number of */ /* additional bytes that are needed to encode the length. */ /* We know that it's safe to skip these first few bytes since we */ /* already verified that they were received in MCS_RCVST_BERHEADER */ /* and MCS_RCVST_BERLENGTH. */ /************************************************************************/ pPDU += MCSGetBERLengthSize(*pPDU); TRC_NRM((TB, _T("Skipped type and length %p->%p"), _MCS.pHdrBuf, pPDU)); /************************************************************************/ /* Now loop through the PDU fields. We are only interested in two of */ /* the fields - the result and the user data. */ /************************************************************************/ for (i = 0; i < MCS_CRPDU_NUMFIELDS; i++) { /********************************************************************/ /* We need one byte for the BER encoded fieldtype. Also, we need at */ /* least one byte to get the NUMBER of bytes in the BER length (1 */ /* or 2 bytes). */ /********************************************************************/ if ((pPDU + 2) > (_MCS.pHdrBuf + _MCS.hdrBytesRead)) { fBadFields = TRUE; DC_QUIT; } /********************************************************************/ /* The first item in a BER encoded field is the type. We're not */ /* interested in this so just skip it. */ /********************************************************************/ pPDU++; /********************************************************************/ /* The next byte has the NUMBER of bytes for the length. */ /********************************************************************/ nBERBytes = MCSGetBERLengthSize(*pPDU); /********************************************************************/ /* The number of bytes had better be 1-3. Also, make sure we have */ /* at least this many bytes left! */ /********************************************************************/ if (nBERBytes > 3 || ((pPDU + nBERBytes) > (_MCS.pHdrBuf + _MCS.hdrBytesRead))) { fBadFields = TRUE; DC_QUIT; } /********************************************************************/ /* The second item is the length. Calculate it and the number of */ /* bytes that the length was encoded in. Note that we add one to */ /* the result of MCSGetBERNumOfLengthBytes as this function returns */ /* the number of additional bytes that are needed to encode the */ /* length. */ /********************************************************************/ length = MCSGetBERLength(pPDU); pPDU += nBERBytes; TRC_NRM((TB, _T("Field %u has length:%u (pPDU:%p)"), i, length, pPDU)); /********************************************************************/ /* Of course, we had better have enough space for the actual len. */ /********************************************************************/ if ((pPDU + length) > (_MCS.pHdrBuf + _MCS.hdrBytesRead)) { fBadFields = TRUE; DC_QUIT; } /********************************************************************/ /* The third item is the actual data - switch on the field number */ /* to determine if this a piece of data that we're interested in. */ /********************************************************************/ switch (i) { case MCS_CRPDU_RESULTOFFSET: { /************************************************************/ /* This is the MCS result field - dig it out and store it. */ /************************************************************/ TRC_ASSERT((MCS_CR_RESULTLEN == length), (TB, _T("Bad CR result length expect:%u got:%u"), MCS_CR_RESULTLEN, length)); result = *pPDU; TRC_NRM((TB, _T("Connect-Response result code:%u"), result)); /************************************************************/ /* If the rc is good then we need to send a MCS */ /* Erect-Domain-Request to the server. */ /************************************************************/ if (MCS_RESULT_SUCCESSFUL == result) { TRC_NRM((TB, _T("Generating EDR PDU"))); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CMCS,MCSSendErectDomainRequest), (ULONG_PTR) 0); } } break; case MCS_CRPDU_USERDATAOFFSET: { /************************************************************/ /* This is the user-data, so issue the NC callback. */ /************************************************************/ TRC_NRM((TB, _T("Call NC_OnMCSCPC - rc:%u pUserData:%p len:%u"), result, pPDU, length)); _pNc->NC_OnMCSConnected(result, pPDU, length); } break; default: { /************************************************************/ /* This is a field that we're not interested in so just */ /* skip it. */ /************************************************************/ TRC_NRM((TB, _T("Offset %u - skip %u bytes of data"), i, length)); } break; } /********************************************************************/ /* Skip the data field. */ /********************************************************************/ pPDU += length; } DC_EXIT_POINT: if (fBadFields) { TRC_ABORT((TB, _T("Bad CR PDU fields"))); MCSSetReasonAndDisconnect(NL_ERR_MCSBADCRFIELDS); } DC_END_FN(); } /* MCSHandleCRPDU */ // This check will be sure that MCS.dataBytesNeeded is not greater than // XT.dataBytesLeft. This should be use ONLY when the MCS needs to read all // it's data from the XT and is not allowed any network reads. In these cases, // without enough data in XT, the client goes into an endless loop // BUG 647947 #define CHECK_VALID_MCS_DATABYTESNEEDED( mcsInst, xtInst, hr ) \ TRC_DBG(( TB, _T("_MCS.dataBytesNeeded = %d"), (mcsInst).dataBytesNeeded )); \ if ((xtInst).dataBytesLeft < (mcsInst).dataBytesNeeded) { \ TRC_ABORT((TB, _T("Bad _MCS.dataBytesNeeded:%u _XT.dataBytesLeft=%u"), \ (mcsInst).dataBytesNeeded, (xtInst).dataBytesLeft )); \ MCSSetReasonAndDisconnect(NL_ERR_MCSINVALIDPACKETFORMAT); \ (hr) = E_ABORT; \ DC_QUIT; \ } /****************************************************************************/ /* Name: MCSRecvData */ /* */ /* Purpose: This is the main data receiver function. It reads data from */ /* the user-data section of a MCS SDin PDU and places it in the */ /* receiver buffer. */ /* */ /* Returns: *pfFinishedData is TRUE if the data section of a MCS PDU was */ /* completely processed and FALSE if not. */ /****************************************************************************/ HRESULT DCINTERNAL CMCS::MCSRecvData(BOOL *pfFinishedData) { BOOL fFinishedData = FALSE; HRESULT hrc = S_OK; PMCS_PDU_SENDDATAINDICATION pSDI; DCUINT16 senderID; DCUINT16 channelID; DCUINT fragCount; DC_BEGIN_FN("MCSRecvData"); /************************************************************************/ /* Switch on the data state. */ /************************************************************************/ switch (_MCS.dataState) { case MCS_DATAST_SIZE1: { /****************************************************************/ /* Try to receive the data into the header buffer. */ /****************************************************************/ if (MCSRecvToHdrBuf()) { /************************************************************/ /* Trace out the contents of the header buffer. */ /************************************************************/ TRC_DATA_DBG("Header buf contains:", _MCS.pHdrBuf, _MCS.hdrBytesRead); /************************************************************/ /* Cast the contents of the header buffer. */ /************************************************************/ pSDI = (PMCS_PDU_SENDDATAINDICATION)_MCS.pHdrBuf; /************************************************************/ /* Check to determine if the header begin segmentation flag */ /* is set. */ /* If the flag is set, the count of bytes read should be 0. */ /* If it is NOT set, the count of bytes should be non-zero. */ /************************************************************/ if (((pSDI->priSeg & MCS_SDI_BEGINSEGMASK) && (0 != _MCS.dataBytesRead)) || (!(pSDI->priSeg & MCS_SDI_BEGINSEGMASK) && (0 == _MCS.dataBytesRead))) { TRC_ABORT((TB, _T("Segmentation flag does not match data bytes read (%u)"), _MCS.dataBytesRead)); MCSSetReasonAndDisconnect(NL_ERR_MCSINVALIDPACKETFORMAT); hrc = E_ABORT; DC_QUIT; } /************************************************************/ /* Update the state variable. */ /************************************************************/ TRC_DBG((TB, _T("State: DATA_SIZE1->DATA_SIZE2"))); _MCS.dataState = MCS_DATAST_SIZE2; } } break; case MCS_DATAST_SIZE2: { /****************************************************************/ /* Now try to receive the first of the data size bytes into the */ /* size buffer. Since the size may be completely contained */ /* within a single byte we only want to read one byte at this */ /* time. */ /****************************************************************/ if (0 != _pXt->XT_Recv(&(_MCS.pSizeBuf[0]), 1)) { /************************************************************/ /* Trace out the contents of the size buffer. */ /************************************************************/ TRC_DATA_DBG("Size buf contains:", _MCS.pSizeBuf, 2); if (_MCS.pSizeBuf[0] & 0x80) { /********************************************************/ /* The MSB of the first byte is set. We now need to */ /* look at the second bit to discover if the following */ /* data is fragmented. */ /********************************************************/ if (_MCS.pSizeBuf[0] & 0x40) { /****************************************************/ /* Bits 1-6 now contain a number between 1 and 4 */ /* which when multiplied by 16K gives the length */ /* of the following fragment. We expect a */ /* maximum packet size of 32K, so the most that */ /* this value should be is 2. */ /****************************************************/ fragCount = _MCS.pSizeBuf[0] & 0x3F; if (fragCount > 2) { TRC_ABORT((TB, _T("Bad fragCount:%u"), fragCount)); MCSSetReasonAndDisconnect(NL_ERR_MCSINVALIDPACKETFORMAT); hrc = E_ABORT; DC_QUIT; } TRC_DBG((TB, _T("Fragmentation count is %u"), fragCount)); /****************************************************/ /* Now work out the number of bytes to read and */ /* change state. */ /****************************************************/ _MCS.dataBytesNeeded = fragCount * 16384; CHECK_VALID_MCS_DATABYTESNEEDED( _MCS, _pXt->_XT, hrc ); _MCS.dataState = MCS_DATAST_READFRAG; /************************************************************/ /* There is a retail check for size in MCS_RecvToDataBuf, */ /* but this helps us debug it before that point. */ /************************************************************/ TRC_ASSERT((_MCS.dataBytesNeeded < 65535), (TB,_T("Data recv size %u too large"), _MCS.dataBytesNeeded)); TRC_DBG((TB, _T("Data bytes needed is now %u"), _MCS.dataBytesNeeded)); TRC_DBG((TB, _T("State: DATA_SIZE2->DATA_READSEG"))); } else { /****************************************************/ /* This section is not fragmented, and contains */ /* between 128 bytes and 16K of data. We need to */ /* read another byte before we can work out how */ /* much data we need. Change state. */ /****************************************************/ _MCS.dataState = MCS_DATAST_SIZE3; TRC_DBG((TB, _T("State: DATA_SIZE2->DATA_SIZE3"))); } } else { /********************************************************/ /* The MSB of the first byte is not set, so this */ /* section contains less than 128 bytes of data. This */ /* means we can just set the count of bytes needed to */ /* the size of this byte and update the state to */ /* reading remainder. */ /********************************************************/ _MCS.dataBytesNeeded = _MCS.pSizeBuf[0]; CHECK_VALID_MCS_DATABYTESNEEDED( _MCS, _pXt->_XT, hrc ); _MCS.dataState = MCS_DATAST_READREMAINDER; /************************************************************/ /* There is a retail check for size in MCS_RecvToDataBuf, */ /* but this helps us debug it before that point. */ /************************************************************/ TRC_ASSERT((_MCS.dataBytesNeeded < 65535), (TB,_T("Data recv size %u too large"), _MCS.dataBytesNeeded)); TRC_DBG((TB, _T("Read %u bytes"), _MCS.dataBytesNeeded)); TRC_DBG((TB, _T("State: DATA_SIZE2->DATA_READREMAINDER"))); } } } break; case MCS_DATAST_SIZE3: { /****************************************************************/ /* The length field is 2 bytes long (i.e. the data size lies */ /* somewhere between 128 bytes and 16K) so try to read the */ /* second byte. Just call XT_Recv directly to get the single */ /* byte. */ /****************************************************************/ if (0 != _pXt->XT_Recv(&(_MCS.pSizeBuf[1]), 1)) { /************************************************************/ /* Trace out the contents of the size buffer. */ /************************************************************/ TRC_DATA_DBG("Size buf contains:", _MCS.pSizeBuf, 2); /************************************************************/ /* We can now work out how much data to receive, so do it */ /* now. */ /************************************************************/ _MCS.dataBytesNeeded = _MCS.pSizeBuf[1] + ((_MCS.pSizeBuf[0] & 0x3F) << 8); CHECK_VALID_MCS_DATABYTESNEEDED( _MCS, _pXt->_XT, hrc ); _MCS.dataState = MCS_DATAST_READREMAINDER; /************************************************************/ /* There is a retail check for size in MCS_RecvToDataBuf, */ /* but this helps us debug it before that point. */ /************************************************************/ TRC_ASSERT((_MCS.dataBytesNeeded < 65535), (TB,_T("Data recv size %u too large"), _MCS.dataBytesNeeded)); TRC_DBG((TB, _T("State: DATA_SIZE3->DATA_READREMAINDER"))); } } break; case MCS_DATAST_READFRAG: { MCS_RecvToDataBuf(hrc, _pXt, this); if (!SUCCEEDED(hrc)) { MCSSetReasonAndDisconnect(NL_ERR_MCSINVALIDPACKETFORMAT); DC_QUIT; } if (S_OK == hrc) { fFinishedData = TRUE; /************************************************************/ /* We've read this fragment completely so change state. */ /************************************************************/ _MCS.dataState = MCS_DATAST_SIZE2; TRC_DBG((TB, _T("State: DATA_READFRAG->DATA_SIZE2"))); } } break; case MCS_DATAST_READREMAINDER: { MCS_RecvToDataBuf(hrc, _pXt, this); if (!SUCCEEDED(hrc)) { MCSSetReasonAndDisconnect(NL_ERR_MCSINVALIDPACKETFORMAT); DC_QUIT; } if (S_OK == hrc) { /************************************************************/ /* We've completely read the data part of a MCS data */ /* packet, so return TRUE. */ /************************************************************/ fFinishedData = TRUE; /************************************************************/ /* Cast the contents of the header buffer. */ /************************************************************/ pSDI = (PMCS_PDU_SENDDATAINDICATION)_MCS.pHdrBuf; /************************************************************/ /* Decide if we should issue a callback to the layer above */ /* with the packet - we do this if this is the last */ /* segment (i.e. the end segmentation flag is set). */ /************************************************************/ if (pSDI->priSeg & MCS_SDI_ENDSEGMASK) { /********************************************************/ /* Dig out the sender id. */ /********************************************************/ senderID = MCSWireUserIDToLocalUserID(pSDI->userID); channelID = MCSWireToLocal16(pSDI->channelID); /********************************************************/ /* Update the performance counter. */ /********************************************************/ PRF_INC_COUNTER(PERF_PKTS_RECV); /********************************************************/ /* The flag is set, so issue the callback. */ /********************************************************/ TRC_DBG((TB, _T("Calling PRcb (senderID:%#x, channelID:%#x, size:%u)"), senderID, channelID, _MCS.dataBytesRead)); TRC_ASSERT((_MCS.pReceivedPacket != NULL), (TB, _T("Null rcv packet buffer"))); /********************************************************/ /* If this function fails, we bail out of the rest of */ /* the packet (outside of this function). */ /********************************************************/ hrc = _pNl->_NL.callbacks.onPacketReceived(_pSl, _MCS.pReceivedPacket, _MCS.dataBytesRead, 0, channelID, 0); /********************************************************/ /* Reset the count of bytes read. */ /********************************************************/ _MCS.dataBytesRead = 0; } /************************************************************/ /* Finally update the state. */ /************************************************************/ _MCS.dataState = MCS_DATAST_SIZE1; TRC_DBG((TB, _T("State: DATA_READREMAINDER->DATA_SIZE1"))); } } break; default: { TRC_ABORT((TB, _T("Unknown data state:%u"), _MCS.dataState)); } break; } DC_EXIT_POINT: *pfFinishedData = fFinishedData; DC_END_FN(); return(hrc); } /* MCSRecvData */ /****************************************************************************/ /* Name: MCSSetReasonAndDisconnect */ /* */ /* Purpose: This function is called when MCS detects that an error has */ /* occurred while processing a PDU. It sets the reason code for */ /* the disconnection which is used to over-ride the value that */ /* comes back in the OnDisconnected callback from XT. After */ /* setting this variable it then calls XT_Disconnect to begin */ /* the disconnection process. */ /* */ /* Params: IN reason - the reason code for the disconnection. */ /****************************************************************************/ DCVOID DCINTERNAL CMCS::MCSSetReasonAndDisconnect(DCUINT reason) { DC_BEGIN_FN("MCSSetReasonAndDisconnect"); /************************************************************************/ /* Set the disconnect error code. This will be used to over-ride the */ /* error value in the OnDisconnected callback from XT before we pass it */ /* to NC. */ /************************************************************************/ TRC_ASSERT((0 == _MCS.disconnectReason), (TB, _T("Disconnect reason has already been set!"))); _MCS.disconnectReason = NL_MAKE_DISCONNECT_ERR(reason); /************************************************************************/ /* Attempt to disconnect. */ /************************************************************************/ TRC_NRM((TB, _T("Set reason code to %#x so now call XT_Disconnect..."), _MCS.disconnectReason)); _pXt->XT_Disconnect(); DC_END_FN(); } /* MCSSetReasonAndDisconnect */