/****************************************************************************/ /* ascint.c */ /* */ /* Share Controller Internal functions. */ /* */ /* Copyright(c) Microsoft, PictureTel 1992-1997 */ /* Copyright(c) Microsoft 1997-2000 */ /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "ascint" #include #include #include extern "C" { #include } /****************************************************************************/ /* FUNCTION: SCPartyJoiningShare */ /* */ /* Called when a new party is joining the share. This is an internal */ /* function because it is the SC which calls all these functions. The */ /* processing done here relies on the capabilities - so it is in here as */ /* this is called after CPC_PartyJoiningShare. */ /* */ /* PARAMETERS: */ /* locPersonID - local person ID of remote person joining the share. */ /* oldShareSize - the number of the parties which were in the share (ie */ /* excludes the joining party). */ /* */ /* RETURNS: */ /* TRUE if the party can join the share. */ /* FALSE if the party can NOT join the share. */ /****************************************************************************/ BOOL RDPCALL SHCLASS SCPartyJoiningShare(LOCALPERSONID locPersonID, unsigned oldShareSize) { DC_BEGIN_FN("SCPartyJoiningShare"); DC_IGNORE_PARAMETER(locPersonID); if (oldShareSize != 0) { SCParseGeneralCaps(); // Initialize Flow Control. SCFlowInit(); } DC_END_FN(); return TRUE; } /****************************************************************************/ /* FUNCTION: SCPartyLeftShare() */ /* */ /* Called when a party has left the share. */ /* */ /* PARAMETERS: */ /* locPersonID - local person ID of remote person leaving the */ /* share. */ /* newShareSize - the number of the parties now in the share (ie excludes */ /* the leaving party). */ /****************************************************************************/ void RDPCALL SHCLASS SCPartyLeftShare(LOCALPERSONID locPersonID, unsigned newShareSize) { DC_BEGIN_FN("SCPartyLeftShare"); DC_IGNORE_PARAMETER(locPersonID); if (newShareSize != 0) SCParseGeneralCaps(); DC_END_FN(); } /****************************************************************************/ // SCParseGeneralCaps // // Enumerates the general capabilities and sets up needed variables. /****************************************************************************/ void RDPCALL SHCLASS SCParseGeneralCaps() { DC_BEGIN_FN("SCParseGeneralCaps"); // Set default local support for fast-path output. It must be shut down // during a shadow to guarantee the non-fast-path format is used // in the cross-server shadow pipe, even though that means a performance // hit on the wire to each client. Note that checking for shadowState // == SHADOW_NONE is not a sufficient check, there is a timing window // where it is likely not be be set yet, so we also force it off in // SC_AddPartyToShare(). if (m_pTSWd->shadowState == SHADOW_NONE) { scUseFastPathOutput = TRUE; } else { scUseFastPathOutput = FALSE; TRC_ALT((TB,"Forcing fast-path output to off in shadow")); } CPC_EnumerateCapabilities(TS_CAPSETTYPE_GENERAL, NULL, SCEnumGeneralCaps); // Set the package header reservation size based on the final results of // the fast-path output support. if (scUseFastPathOutput) { if (m_pTSWd->bCompress) { TRC_ALT((TB,"Fast-path output enabled with compression")); scUpdatePDUHeaderSpace = 4; scCompressionUsedValue = TS_OUTPUT_FASTPATH_COMPRESSION_USED; } else { TRC_ALT((TB,"Fast-path output enabled without compression")); scUpdatePDUHeaderSpace = 3; scCompressionUsedValue = 0; } } else { TRC_ALT((TB,"Fast-path output disabled")); scUpdatePDUHeaderSpace = sizeof(TS_SHAREDATAHEADER); } SCUpdateVCCaps(); DC_END_FN(); } /****************************************************************************/ /* FUNCTION: SCEnumGeneralCaps */ /* */ /* Used to determine the lowest version in the share. */ /* */ /* PARAMETERS: standard CPC callback parameters */ /****************************************************************************/ void RDPCALL SHCLASS SCEnumGeneralCaps( LOCALPERSONID locPersonID, UINT_PTR UserData, PTS_CAPABILITYHEADER pCapabilities ) { PTS_GENERAL_CAPABILITYSET pGeneralCaps = (PTS_GENERAL_CAPABILITYSET)pCapabilities; DC_BEGIN_FN("SCEnumGeneralCaps"); DC_IGNORE_PARAMETER(locPersonID); DC_IGNORE_PARAMETER(UserData); // Determine if we should support No BC header or not depending on the // client support level and the current support level scNoBitmapCompressionHdr = min(scNoBitmapCompressionHdr, pGeneralCaps->extraFlags & TS_EXTRA_NO_BITMAP_COMPRESSION_HDR); // Disable fast-path output if any client does not support it. if (!(pGeneralCaps->extraFlags & TS_FASTPATH_OUTPUT_SUPPORTED)) scUseFastPathOutput = FALSE; // Enable sending Long Credentials back to the client if it supports it if (pGeneralCaps->extraFlags & TS_LONG_CREDENTIALS_SUPPORTED) { scUseLongCredentials = TRUE; } else { scUseLongCredentials = FALSE; } // Determine if we should enable the arc cookie if ((pGeneralCaps->extraFlags & TS_AUTORECONNECT_COOKIE_SUPPORTED) && (FALSE == m_pTSWd->fPolicyDisablesArc)) { scUseAutoReconnect = TRUE; } else { scUseAutoReconnect = FALSE; } // Determine if we support the more secure checksum style if ((pGeneralCaps->extraFlags & TS_ENC_SECURE_CHECKSUM)) { SM_SetSafeChecksumMethod(scPSMHandle, TRUE); } else { SM_SetSafeChecksumMethod(scPSMHandle, FALSE); } DC_END_FN(); } /****************************************************************************/ /* FUNCTION: SCCallPartyJoiningShare */ /* */ /* Calls other components' XX_PartyJoiningShare() functions. Should be */ /* called with scNumberInShare set to the old call size. */ /* */ /* PARAMETERS: */ /* locPersonID - of party joining the call. */ /* sizeOfCaps - sizeof the capabilities parameter pCaps. */ /* pCaps - pointer to capabilities for the party. */ /* pAccepted - pointer to array of BOOLs which is filled in with the */ /* result of the respective components function. */ /* oldShareSize - the number to pass to the PJS functions. */ /* */ /* RETURNS: */ /* TRUE - all components accepted the party; pAccepted will be filled with */ /* TRUE. */ /* FALSE - a component rejected the party. Any components which accepted */ /* the party will have their pAccepted entry set to TRUE; all other */ /* entries will be FALSE. */ /****************************************************************************/ BOOL RDPCALL SHCLASS SCCallPartyJoiningShare( LOCALPERSONID locPersonID, unsigned sizeOfCaps, PVOID pCaps, PBOOL pAccepted, unsigned oldShareSize) { DC_BEGIN_FN("SCCallPartyJoiningShare"); /************************************************************************/ /* Set all of pAccepted to FALSE. */ /************************************************************************/ memset(pAccepted, 0, sizeof(BOOL) * SC_NUM_PARTY_JOINING_FCTS); /************************************************************************/ /* Call the functions in the correct order, giving up if any reject the */ /* party. */ /************************************************************************/ #define CALL_PJS(NUM, CALL) \ TRC_NRM((TB, "Call PJS # %d", NUM)); \ if (0 == (pAccepted[NUM] = CALL)) \ { \ TRC_NRM((TB, "%d PartyJoining failed", (unsigned)NUM)); \ return FALSE; \ } TRC_NRM((TB, "{%d}Call PJS functions", locPersonID)); /************************************************************************/ // Notes on the order of PartyJoiningShare calls: // 1. CPC must be called before everyone else (as everyone else needs // capabilites for the new party). // 2. UP must be called after SC because UP relies on the caps // negotiation in SC. /************************************************************************/ CALL_PJS(SC_CPC, CPC_PartyJoiningShare(locPersonID, oldShareSize, sizeOfCaps, pCaps)) CALL_PJS(SC_SC, SCPartyJoiningShare(locPersonID, oldShareSize)) CALL_PJS(SC_IM, IM_PartyJoiningShare (locPersonID, oldShareSize)) CALL_PJS(SC_CA, CA_PartyJoiningShare (locPersonID, oldShareSize)) CALL_PJS(SC_CM, CM_PartyJoiningShare (locPersonID, oldShareSize)) CALL_PJS(SC_OE, OE_PartyJoiningShare (locPersonID, oldShareSize)) CALL_PJS(SC_SSI, SSI_PartyJoiningShare(locPersonID, oldShareSize)) CALL_PJS(SC_USR, USR_PartyJoiningShare(locPersonID, oldShareSize)) CALL_PJS(SC_UP, UP_PartyJoiningShare(locPersonID, oldShareSize)) CALL_PJS(SC_SBC, SBC_PartyJoiningShare(locPersonID, oldShareSize)) TRC_DATA_NRM("PJS status", pAccepted, sizeof(BOOL) * SC_NUM_PARTY_JOINING_FCTS); DC_END_FN(); return TRUE; } /****************************************************************************/ /* FUNCTION: SCCallPartyLeftShare */ /* */ /* Calls other components' XX_PartyLeftShare() functions. should be called */ /* with scNumberInShare set to the new call size. */ /* */ /* PARAMETERS: */ /* locPersonID - of party who has left the call. */ /* pAccepted - pointer to array of BOOLs which is used to decide which */ /* components' functions to call. Any component with an entry set to */ /* TRUE will be called. */ /* newShareSize - the number to pass to the various PLS functions. */ /****************************************************************************/ void RDPCALL SHCLASS SCCallPartyLeftShare(LOCALPERSONID locPersonID, PBOOL pAccepted, unsigned newShareSize ) { DC_BEGIN_FN("SCCallPartyLeftShare"); /************************************************************************/ /* Call any components which have their pAccepted entry to TRUE. */ /************************************************************************/ #define CALL_PLS(A, B) \ { \ if (pAccepted[A]) \ { \ TRC_NRM((TB, "Call PLS # %d", A)); \ B(locPersonID, newShareSize); \ } \ } TRC_NRM((TB, "Call PLS functions")); /************************************************************************/ /* Notes on order of PartyLeftShare calls */ /* */ /* 1. CPC must be called first (so everyone else gets capabilities */ /* which exclude the party which has left). */ /************************************************************************/ CALL_PLS(SC_CPC, CPC_PartyLeftShare) CALL_PLS(SC_SC, SCPartyLeftShare) CALL_PLS(SC_CA, CA_PartyLeftShare ) CALL_PLS(SC_IM, IM_PartyLeftShare ) CALL_PLS(SC_OE, OE_PartyLeftShare ) CALL_PLS(SC_SBC, SBC_PartyLeftShare) CALL_PLS(SC_SSI, SSI_PartyLeftShare) CALL_PLS(SC_USR, USR_PartyLeftShare) CALL_PLS(SC_UP, UP_PartyLeftShare) TRC_NRM((TB, "Done PLS functions")); DC_END_FN(); } /****************************************************************************/ /* Name: SCInitiateSync */ /* */ /* Purpose: Initiate synchronization */ /* */ /* Params: bShadowSync - set to true of this sync is being requested for */ /* a shadowing session. */ /* */ /* Operation: Broadcast a sync packet on all priorities */ /* Call other components to synchronize, unless this is a shadow */ /* sync in which case the DD will already have synchronized */ /* itself prior to initiating this action. */ /****************************************************************************/ void RDPCALL SHCLASS SCInitiateSync(BOOLEAN bShadowSync) { PTS_SYNCHRONIZE_PDU pPkt; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SCInitiateSync"); SC_CHECK_STATE(SCE_INITIATESYNC); /************************************************************************/ // Allocate a Sync PDU // fWait is TRUE means that we will always wait for a buffer to be avail /************************************************************************/ status = SM_AllocBuffer(scPSMHandle, (PPVOID)(&pPkt), sizeof(TS_SYNCHRONIZE_PDU), TRUE, FALSE); if ( STATUS_SUCCESS == status ) { /********************************************************************/ // Build the Sync PDU /********************************************************************/ pPkt->shareDataHeader.shareControlHeader.totalLength = sizeof(TS_SYNCHRONIZE_PDU); pPkt->shareDataHeader.pduType2 = TS_PDUTYPE2_SYNCHRONIZE; pPkt->messageType = TS_SYNCMSGTYPE_SYNC; /********************************************************************/ // Send the Sync PDU (Broadcast, all parties, all priorities) /********************************************************************/ rc = SC_SendData((PTS_SHAREDATAHEADER)pPkt, sizeof(TS_SYNCHRONIZE_PDU), sizeof(TS_SYNCHRONIZE_PDU), 0, 0); if (rc) { TRC_NRM((TB, "Sent Sync OK")); /****************************************************************/ // Call all the XX_SyncNow() functions. /****************************************************************/ CA_SyncNow(); PM_SyncNow(); // added for shadowing UP_SyncNow(bShadowSync); } else { TRC_ERR((TB, "Failed to send Sync PDU")); } } else { TRC_ERR((TB, "Failed to alloc syncPDU")); } DC_EXIT_POINT: DC_END_FN(); } /* SCInitiateSync */ /****************************************************************************/ /* Name: SCConfirmActive */ /* */ /* Purpose: Handle incoming ConfirmActivePDU */ /* */ /* Params: netPersonID - ID of sender of ConfirmActivePDU */ /* pPkt - ConfirmActivePDU */ /****************************************************************************/ void RDPCALL SHCLASS SCConfirmActive( PTS_CONFIRM_ACTIVE_PDU pPkt, unsigned DataLength, NETPERSONID netPersonID) { LOCALPERSONID localPersonID = 0; unsigned localCapsSize; PTS_COMBINED_CAPABILITIES pLocalCaps; BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; BOOL rc = FALSE; BOOL callingPJS = FALSE; BOOL kickWDW = FALSE; unsigned errDetailCode = 0; WCHAR detailData[25]; unsigned len, detailDataLen; DC_BEGIN_FN("SCConfirmActive"); SC_CHECK_STATE(SCE_CONFIRM_ACTIVE); // First check we have enogh data for this packet. if (DataLength >= (sizeof(TS_CONFIRM_ACTIVE_PDU) - 1)) { if (DataLength >= (sizeof(TS_CONFIRM_ACTIVE_PDU) - 1 + pPkt->lengthSourceDescriptor + pPkt->lengthCombinedCapabilities)) { // Do some meaningful work here to predict the branches correctly. goto PDUOK; } else { TRC_ERR((TB,"Total PDU len %u too short for header and data len %u", DataLength, pPkt->lengthSourceDescriptor + pPkt->lengthCombinedCapabilities)); goto ShortPDU; } } else { TRC_ERR((TB,"Data length %u too short for ConfirmActivePDU header", DataLength)); goto ShortPDU; } PDUOK: kickWDW = FALSE; /************************************************************************/ /* Check it's the right ConfirmActivePDU */ /************************************************************************/ if (pPkt->shareID != scShareID) { TRC_ERR((TB, "Wrong Share ID, expect %x, got %x", scShareID, pPkt->shareID)); errDetailCode = Log_RDP_ConfirmActiveWrongShareID; len = swprintf(detailData, L"%x %x", scShareID, pPkt->shareID); detailDataLen = sizeof(*detailData) * len; DC_QUIT; } if (pPkt->originatorID != scUserID) { TRC_ERR((TB, "Wrong originator ID, expect %d, got %hd", scUserID, pPkt->originatorID)); errDetailCode = Log_RDP_ConfirmActiveWrongOriginator; len = swprintf(detailData, L"%x %hx", scUserID, pPkt->originatorID); detailDataLen = sizeof(*detailData) * len; DC_QUIT; } /************************************************************************/ /* We will receive a ConfirmActivePDU on all priorities. If we get */ /* here and we're already in a Share, this must be a second or */ /* subsequent one. Simply ignore it. Set rc = TRUE so that we don't */ /* send a DeactivateOtherPDU below. */ /************************************************************************/ if (scState == SCS_IN_SHARE) { TRC_ALT((TB, "Superfluous ConfirmActivePDU received")); rc = TRUE; DC_QUIT; } /************************************************************************/ /* If we get here, this is the first ConfirmActivePDU, and it's from */ /* the right Client. Set a flag which will cause us to kick WDW back */ /* into life at the end of this function */ /************************************************************************/ kickWDW = TRUE; /************************************************************************/ /* Reject this party if it will exceed the maximum number of parties */ /* allowed in a share. (Not required for RNS V1.0, but left in as it */ /* doesn't do any harm). */ /************************************************************************/ if (scNumberInShare == SC_DEF_MAX_PARTIES) { TRC_ERR((TB, "Reached max parties in share %d", SC_DEF_MAX_PARTIES)); DC_QUIT; } /************************************************************************/ /* If this is the first remote party in the share, call the */ /* XX_PartyJoiningShare() functions for the local party first. */ /************************************************************************/ callingPJS = TRUE; if (scNumberInShare == 0) { CPC_GetCombinedCapabilities(SC_LOCAL_PERSON_ID, &localCapsSize, &pLocalCaps); if (!SCCallPartyJoiningShare(SC_LOCAL_PERSON_ID, localCapsSize, pLocalCaps, acceptedArray, 0)) { /****************************************************************/ /* Some component rejected the local party */ /****************************************************************/ TRC_ERR((TB, "The local party should never be rejected")); DC_QUIT; } /********************************************************************/ /* There is now one party in the share (the local one). */ /********************************************************************/ scNumberInShare = 1; TRC_NRM((TB, "Added local person")); } /************************************************************************/ /* Calculate a localPersonID for the remote party and store their */ /* details in the party array. */ /************************************************************************/ for ( localPersonID = 1; localPersonID < SC_DEF_MAX_PARTIES; localPersonID++ ) { if (scPartyArray[localPersonID].netPersonID == 0) { /****************************************************************/ /* Found an empty slot. */ /****************************************************************/ TRC_NRM((TB, "Allocated local person ID %d", localPersonID)); break; } } /************************************************************************/ /* Even though scNumberInShare is checked against SC_DEF_MAX_PARTIES */ /* above, the loop above might still not find an empty slot. */ /************************************************************************/ if (SC_DEF_MAX_PARTIES <= localPersonID) { TRC_ABORT((TB, "Couldn't find room to store local person")); DC_QUIT; } /************************************************************************/ /* Store the new person's details */ /************************************************************************/ scPartyArray[localPersonID].netPersonID = netPersonID; // we know that we have at least lengthSourceDescriptor bytes in the buffer // we should copy not more than lengthSourceDescriptor or the destination // buffer size. strncpy(scPartyArray[localPersonID].name, (char *)(&(pPkt->data[0])), min(sizeof(scPartyArray[0].name)-sizeof(scPartyArray[0].name[0]), pPkt->lengthSourceDescriptor)); // zero terminate to make sure we don't overflow on subsequent processing. scPartyArray[localPersonID].name[sizeof(scPartyArray[0].name)/ sizeof(scPartyArray[0].name[0]) - 1] = 0; memset(scPartyArray[localPersonID].sync, 0, sizeof(scPartyArray[localPersonID].sync)); TRC_NRM((TB, "{%d} person name %s", (unsigned)localPersonID, scPartyArray[localPersonID].name)); /************************************************************************/ /* Call the XX_PartyJoiningShare() functions for the remote party. */ /************************************************************************/ if (!SCCallPartyJoiningShare(localPersonID, pPkt->lengthCombinedCapabilities, (PVOID)(&(pPkt->data[pPkt->lengthSourceDescriptor])), acceptedArray, scNumberInShare)) { /********************************************************************/ // Some component rejected the remote party. Force a disconnect // and log an event. /********************************************************************/ TRC_ERR((TB, "Remote party rejected")); errDetailCode = Log_RDP_BadCapabilities; detailDataLen = 0; DC_QUIT; } /************************************************************************/ /* The remote party is now in the share. */ /************************************************************************/ callingPJS = FALSE; rc = TRUE; scNumberInShare++; TRC_NRM((TB, "Number in share %d", (unsigned)scNumberInShare)); /************************************************************************/ /* Move onto the next state. */ /************************************************************************/ SC_SET_STATE(SCS_IN_SHARE); /************************************************************************/ /* Synchronise only for primary stacks. Shadow stacks will be sync'd */ /* by the DD right before output starts. */ /************************************************************************/ SCInitiateSync(m_pTSWd->StackClass == Stack_Shadow); DC_EXIT_POINT: if (!rc) { /********************************************************************/ /* Something went wrong. Tidy up */ /********************************************************************/ TRC_NRM((TB, "Something went wrong - %d people in Share", scNumberInShare)); /********************************************************************/ /* If we fail, tell WDW now, so it can clean up. If we succeed, FH */ /* tells WDW when font negotiation is complete. */ /********************************************************************/ if (kickWDW) { TRC_NRM((TB, "Kick WDW")); WDW_ShareCreated(m_pTSWd, FALSE); } if (callingPJS) { TRC_NRM((TB, "Failed in PJS functions")); /****************************************************************/ /* Notify components that remote party has left Share. Note */ /* that scNumberInShare is not updated if PJS functions fail. */ /****************************************************************/ if (scNumberInShare > 0) { /************************************************************/ /* We failed to add a remote party */ /************************************************************/ TRC_NRM((TB, "Failed to add remote party")); SCCallPartyLeftShare(localPersonID, acceptedArray, scNumberInShare ); /************************************************************/ /* Set acceptedArray ready to call PJS for local person */ /************************************************************/ memset(acceptedArray, TRUE, sizeof(BOOL) * SC_NUM_PARTY_JOINING_FCTS ); } if (scNumberInShare <= 1) { /************************************************************/ /* We failed to add one of */ /* - the local person (scNumberInShare == 0) */ /* - the first remote person (scNumberInShare == 1) */ /* Either way, we now need to remove the local person. */ /************************************************************/ TRC_NRM((TB, "Clean up local person")); SCCallPartyLeftShare(SC_LOCAL_PERSON_ID, acceptedArray, 0); scNumberInShare = 0; } } /********************************************************************/ /* Now we need to terminate the Client, via one of two means: */ /* - if it's a protocol error, simply disconnect the Client */ /* - if it's a resource error, try to end the Share. */ /********************************************************************/ if (errDetailCode != 0) { WDW_LogAndDisconnect(m_pTSWd, TRUE, errDetailCode, (BYTE *)detailData, detailDataLen); } TRC_NRM((TB, "Reject the new person")); SCDeactivateOther(netPersonID); /********************************************************************/ /* Finally, free the local person ID, if one was allocated */ /********************************************************************/ if ((localPersonID != 0) && (localPersonID != SC_DEF_MAX_PARTIES)) { TRC_NRM((TB, "Free local person ID")); scPartyArray[localPersonID].netPersonID = 0; } } DC_END_FN(); return; // Error handling ShortPDU: WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ConfirmActivePDUTooShort, (BYTE *)pPkt, DataLength); DC_END_FN(); } /* SC_ConfirmActivePDU */ /****************************************************************************/ /* Name: SCDeactivateOther */ /* */ /* Purpose: Deactivate another person */ /* */ /* Params: netPersonID - ID of person to be deactivated */ /****************************************************************************/ void RDPCALL SHCLASS SCDeactivateOther(NETPERSONID netPersonID) { unsigned pktLen; unsigned nameLen; PTS_DEACTIVATE_OTHER_PDU pPkt; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SCDeactivateOther"); /************************************************************************/ /* Allocate a buffer */ /************************************************************************/ pktLen = sizeof(TS_DEACTIVATE_OTHER_PDU) - 1; nameLen = strlen(scPartyArray[0].name); nameLen = (unsigned)DC_ROUND_UP_4(nameLen); pktLen += nameLen; // fWait is TRUE means that we will always wait for a buffer to be avail status = SM_AllocBuffer(scPSMHandle, (PPVOID)(&pPkt), pktLen, TRUE, FALSE); if ( STATUS_SUCCESS == status ) { /********************************************************************/ // Fill in the packet fields. /********************************************************************/ pPkt->shareControlHeader.totalLength = (UINT16)pktLen; pPkt->shareControlHeader.pduType = TS_PDUTYPE_DEACTIVATEOTHERPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16)scUserID; pPkt->shareID = scShareID; pPkt->deactivateID = (UINT16)netPersonID; memcpy(&(pPkt->sourceDescriptor[0]), scPartyArray[0].name, nameLen); // Send it. rc = SM_SendData(scPSMHandle, pPkt, pktLen, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (!rc) { TRC_ERR((TB, "Failed to send TS_DEACTIVATE_OTHER_PDU")); } } else { /********************************************************************/ /* Failed to allocate a buffer. This Bad News. Give up. */ /********************************************************************/ TRC_ERR((TB, "Failed to alloc %d bytes for TS_DEACTIVATE_OTHER_PDU", pktLen)); } DC_END_FN(); } /* SCDeactivateOther */ /****************************************************************************/ /* Name: SCEndShare */ /* */ /* Purpose: Clean up the local side of the share */ /* */ /* Operation: Call all PartyLeftShare functions for each party. */ /****************************************************************************/ void RDPCALL SHCLASS SCEndShare(void) { BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; int i; DC_BEGIN_FN("SCEndShare"); /************************************************************************/ /* If no-one has joined the Share yet, there's nothing to do */ /************************************************************************/ if (scNumberInShare == 0) { TRC_ALT((TB, "Ending Share before it was started")); DC_QUIT; } /************************************************************************/ /* Call PLS for all remote people. */ /************************************************************************/ memset(acceptedArray, TRUE, sizeof(acceptedArray)); for (i = SC_DEF_MAX_PARTIES - 1; i > 0; i--) { if (scPartyArray[i].netPersonID != 0) { TRC_NRM((TB, "Party %d left Share", i)); scNumberInShare--; SCCallPartyLeftShare(i, acceptedArray, scNumberInShare); memset(&(scPartyArray[i]), 0, sizeof(*scPartyArray)); } } /************************************************************************/ /* Now call PLS functions for the local person. Don't clear */ /* scPartyArray for the local person, as the info is still valid. */ /************************************************************************/ scNumberInShare--; TRC_ASSERT((scNumberInShare == 0), (TB, "Still %d people in the Share", scNumberInShare)); TRC_NRM((TB, "Local party left Share")); SCCallPartyLeftShare(0, acceptedArray, scNumberInShare); DC_EXIT_POINT: /************************************************************************/ /* Return to the inititalized state */ /************************************************************************/ SC_SET_STATE(SCS_INITED); DC_END_FN(); } /* SCEndShare */ /****************************************************************************/ /* Name: SCSynchronizePDU */ /* */ /* Purpose: Handle incoming Synchronize PDUs */ /* */ /* Params: netPersonID - user ID of the sender */ /* pPkt - SynchronizePDU */ /****************************************************************************/ void RDPCALL SHCLASS SCSynchronizePDU(NETPERSONID netPersonID, UINT32 priority, PTS_SYNCHRONIZE_PDU pPkt) { LOCALPERSONID localID; DC_BEGIN_FN("SCSynchronizePDU"); localID = SC_NetworkIDToLocalID(netPersonID); TRC_NRM((TB, "SynchronizePDU person [%d] {%d}, priority %d", netPersonID, localID, priority)); scPartyArray[localID].sync[priority] = TRUE; DC_END_FN(); } /* SCSynchronizePDU */ /****************************************************************************/ /* Name: SCReceivedControlPacket */ /* */ /* Purpose: Handle incoming control packets */ /* */ /* Params: netPersonID - ID of the sender */ /* priority - priority on which the packet was sent */ /* pPkt - the packet */ /****************************************************************************/ void RDPCALL SHCLASS SCReceivedControlPacket( NETPERSONID netPersonID, UINT32 priority, void *pPkt, unsigned DataLength) { unsigned pduType; LOCALPERSONID locPersonID; BOOL pduOK = FALSE; BOOL status; DC_BEGIN_FN("SCReceivedControlPacket"); // We have enough data to read the flow control marker since the marker // is overlaid over the SHARECONTROLHEADER.totalLength. We checked earlier // for having enough data to read the share ctrl hdr. /************************************************************************/ /* First, check for Flow Control packets */ /************************************************************************/ if (((PTS_FLOW_PDU)pPkt)->flowMarker != TS_FLOW_MARKER) { /********************************************************************/ /* Check for control packets */ /********************************************************************/ // SC_CHECK_STATE(SCE_CONTROLPACKET); pduOK = TRUE; pduType = ((PTS_SHARECONTROLHEADER)pPkt)->pduType & TS_MASK_PDUTYPE; switch (pduType) { case TS_PDUTYPE_CONFIRMACTIVEPDU: TRC_ALT((TB, "%s Stack: ConfirmActivePDU", m_pTSWd->StackClass == Stack_Primary ? "Primary" : (m_pTSWd->StackClass == Stack_Passthru ? "Passthru" : "Shadow"))); SCConfirmActive((PTS_CONFIRM_ACTIVE_PDU)pPkt, DataLength, netPersonID); break; case TS_PDUTYPE_CLIENTRANDOMPDU: TRC_ALT((TB, "ClientRandomPDU")); status = SC_SaveClientRandom((PTS_CLIENT_RANDOM_PDU) pPkt, DataLength); if (status != TRUE) { TRC_ERR((TB, "Error in SC_SaveClientRandom, data length = %u", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_InputPDUBadLength, (BYTE *) pPkt, DataLength); } break; default: { /************************************************************/ /* At the moment, we don't expect or process any other */ /* control packets */ /************************************************************/ TRC_ERR((TB, "Unexpected packet type %d", pduType)); TRC_DATA_NRM("Packet", pPkt, ((PTS_SHARECONTROLHEADER)&pPkt)->totalLength); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_UnknownPDUType, (BYTE *) &pduType, sizeof(pduType)); } break; } } else { /********************************************************************/ /* For the purposes of state checking, treat Flow Control packets */ /* as data packets. */ /********************************************************************/ SC_CHECK_STATE(SCE_DATAPACKET); pduOK = TRUE; // Make sure we have enough data to access the TS_FLOW_PDU fields. if (DataLength >= sizeof(TS_FLOW_PDU)) { pduType = ((PTS_FLOW_PDU)pPkt)->pduType; locPersonID = SC_NetworkIDToLocalID(netPersonID); } else { TRC_ERR((TB,"Data length %u too short for FlowPDU", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_FlowPDUTooShort, (BYTE *)pPkt, DataLength); DC_QUIT; } switch (pduType) { case TS_PDUTYPE_FLOWTESTPDU: { TRC_NRM((TB, "[%d] {%d} Flow Test PDU on priority %d", netPersonID, locPersonID, priority)); SCFlowTestPDU(locPersonID, (PTS_FLOW_PDU)pPkt, priority); } break; default: { TRC_ERR((TB, "[%d] {%d} Unknown Flow PDU %d on priority %d", netPersonID, locPersonID, pduType, priority)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_UnknownFlowPDU, (BYTE *) &pduType, sizeof(pduType)); } break; } } DC_EXIT_POINT: if (!pduOK) { /********************************************************************/ /* Out-of-sequence control PDU - log and disconnect */ /********************************************************************/ WCHAR detailData[(sizeof(pduType)*2) + (sizeof(scState)*2) + 2]; TRC_ERR((TB, "Out-of-sequence control PDU %hx, state %d", pduType, scState)); swprintf(detailData, L"%hx %x", pduType, scState); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ControlPDUSequence, (BYTE *)detailData, sizeof(detailData)); } DC_END_FN(); } /* SCReceivedControlPacket */ /****************************************************************************/ /* Name: SCFlowTestPDU */ /* */ /* Purpose: Handle incoming Flow Test PDUs */ /* */ /* Params: locPersonID - id of the sender */ /* pPkt - the Flow Test PDU */ /****************************************************************************/ void RDPCALL SHCLASS SCFlowTestPDU( LOCALPERSONID locPersonID, PTS_FLOW_PDU pPkt, UINT32 priority) { PTS_FLOW_PDU pRsp; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SCFlowTestPDU"); // Build and send a flow response PDU. // fWait is TRUE means that we will always wait for a buffer to be avail status = SM_AllocBuffer(scPSMHandle, (PPVOID)(&pRsp), sizeof(*pRsp), TRUE, FALSE); if ( STATUS_SUCCESS == status ) { pRsp->flowMarker = TS_FLOW_MARKER; pRsp->pduType = TS_PDUTYPE_FLOWRESPONSEPDU; pRsp->flowIdentifier = pPkt->flowIdentifier; pRsp->flowNumber = pPkt->flowNumber; pRsp->pduSource = pPkt->pduSource; rc = SM_SendData(scPSMHandle, pRsp, sizeof(*pRsp), priority, scPartyArray[locPersonID].netPersonID, FALSE, RNS_SEC_ENCRYPT, FALSE); if (!rc) { TRC_ERR((TB, "Failed to send Flow Response PDU")); } } else { TRC_ERR((TB, "Failed to alloc Flow Response PDU")); } DC_END_FN(); } /* SCFlowTestPDU */ // // SCUpdateVCCaps update VirtualChannel capabilities based on // remote person's caps. // void RDPCALL SHCLASS SCUpdateVCCaps() { DC_BEGIN_FN("SCUpdateVCCaps"); PTS_VIRTUALCHANNEL_CAPABILITYSET pVcCaps = NULL; // //Determine if the client supports VC compression //What we're determining here is that the client supports //the server sending it compressed VC data. The capability in the //other direction, i.e can the server send the client VC data //is a capability the server exposes to the client and it may choose //to then send compressed VC data to the server // pVcCaps = (PTS_VIRTUALCHANNEL_CAPABILITYSET)CPCGetCapabilities( SC_REMOTE_PERSON_ID, TS_CAPSETTYPE_VIRTUALCHANNEL); if(pVcCaps && (pVcCaps->vccaps1 & TS_VCCAPS_COMPRESSION_64K)) { m_pTSWd->bClientSupportsVCCompression = TRUE; TRC_NRM((TB, "Client supports VC compression")); } else { m_pTSWd->bClientSupportsVCCompression = FALSE; TRC_NRM((TB, "Client doesn't support VC compression")); } DC_END_FN(); }