/****************************************************************************/ // ascapi.c // // Share Controller API Functions. // // Copyright (C) Microsoft Corp., PictureTel 1992-1997 // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "ascapi" #include extern "C" { #include #include #include } #include /****************************************************************************/ // SC_Init // Initializes the Share Controller. // // PARAMETERS: // pSMHandle - Handle to pass to SM calls // // RETURNS: FALSE on failure. /****************************************************************************/ BOOL RDPCALL SHCLASS SC_Init(PVOID pSMHandle) { BOOL rc; unsigned MaxPDUSize; DC_BEGIN_FN("SC_Init"); // Don't check the state - there's no static data init in the C++ App // Serving build so this will see complete garbage in scState and fail. #define DC_INIT_DATA #include #undef DC_INIT_DATA // Register with SM. rc = SM_Register(pSMHandle, &MaxPDUSize, &scUserID); if (rc) { // Save the User ID. scPartyArray[0].netPersonID = scUserID; scPSMHandle = pSMHandle; TRC_NRM((TB, "Local user id [%d]", scUserID)); // Set the user name. strcpy(scPartyArray[0].name, "RDP"); // Get the usable space (and hence the allocation request size) for // our 8K and 16K OutBufs. sc8KOutBufUsableSpace = IcaBufferGetUsableSpace(OUTBUF_8K_ALLOC_SIZE) - OUTBUF_HEADER_OVERHEAD; sc16KOutBufUsableSpace = IcaBufferGetUsableSpace( OUTBUF_16K_ALLOC_SIZE) - OUTBUF_HEADER_OVERHEAD; // Move onto the next state. SC_SET_STATE(SCS_INITED) } else { TRC_ERR((TB, "Failed to register with SM")); } DC_END_FN(); return rc; } /****************************************************************************/ // SC_Update // Update the Share Controller after shadow. // /****************************************************************************/ void RDPCALL SHCLASS SC_Update() { DC_BEGIN_FN("SC_Update"); scNoBitmapCompressionHdr = TS_EXTRA_NO_BITMAP_COMPRESSION_HDR; DC_END_FN(); } /****************************************************************************/ /* SC_Term() */ /* */ /* Terminates the Share Controller. */ /****************************************************************************/ void RDPCALL SHCLASS SC_Term(void) { DC_BEGIN_FN("SC_Term"); SC_CHECK_STATE(SCE_TERM); // Reset state. SC_SET_STATE(SCS_STARTED); DC_EXIT_POINT: DC_END_FN(); } /****************************************************************************/ /* SC_CreateShare() */ /* */ /* Creates a share for the current session. */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_CreateShare(void) { BOOL rc = FALSE; unsigned pktLen; unsigned nameLen; unsigned capsSize; UINT32 sessionId; PTS_COMBINED_CAPABILITIES caps; PTS_DEMAND_ACTIVE_PDU pPkt = NULL; PTS_BITMAP_CAPABILITYSET pBitmapCaps; NTSTATUS status; DC_BEGIN_FN("SC_CreateShare"); SC_CHECK_STATE(SCE_CREATE_SHARE); /************************************************************************/ /* For console sessions, there's no client, so not much point in */ /* sending a demand active PDU. We also have to set up caps ourselves. */ /************************************************************************/ if (m_pTSWd->StackClass == Stack_Console) { LOCALPERSONID localPersonID = 0; unsigned localCapsSize; PTS_COMBINED_CAPABILITIES pLocalCaps; BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; BOOL callingPJS = FALSE; TRC_NRM((TB, "SC_CreateShare called for console stack")); /********************************************************************/ /* Move onto the next state. */ /********************************************************************/ SC_SET_STATE(SCS_IN_SHARE); /********************************************************************/ /* carry on as if a confirm active has been received */ /********************************************************************/ 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; } } /********************************************************************/ /* If we don't find an empty slot, we can't keep running because */ /* we write past the end of the scPartyArray below. */ /********************************************************************/ 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 = 42; strncpy(scPartyArray[localPersonID].name, "Console", sizeof(scPartyArray[0].name) ); memset(scPartyArray[localPersonID].sync, 0, sizeof(scPartyArray[localPersonID].sync)); TRC_NRM((TB, "{%d} person name %s", (unsigned)localPersonID, scPartyArray[localPersonID].name)); /********************************************************************/ /* We need to set up client caps ourselves, since there's no actual */ /* client to do it. We set up a maximal set that will get */ /* negotiated down when someone shadows us. */ /********************************************************************/ typedef struct tagCC_COMBINED_CAPABILITIES { UINT16 numberCapabilities; #ifdef DRAW_GDIPLUS #ifdef DRAW_NINEGRID #define CC_COMBINED_CAPS_NUMBER_CAPABILITIES 18 #else #define CC_COMBINED_CAPS_NUMBER_CAPABILITIES 17 #endif #else // DRAW_GDIPLUS #ifdef DRAW_NINEGRID #define CC_COMBINED_CAPS_NUMBER_CAPABILITIES 17 #else #define CC_COMBINED_CAPS_NUMBER_CAPABILITIES 16 #endif #endif // DRAW_GDIPLUS UINT16 pad2octets; TS_GENERAL_CAPABILITYSET generalCapabilitySet; TS_BITMAP_CAPABILITYSET bitmapCapabilitySet; TS_ORDER_CAPABILITYSET orderCapabilitySet; TS_BITMAPCACHE_CAPABILITYSET bitmapCacheCaps; TS_COLORTABLECACHE_CAPABILITYSET colorTableCacheCapabilitySet; TS_WINDOWACTIVATION_CAPABILITYSET windowActivationCapabilitySet; TS_CONTROL_CAPABILITYSET controlCapabilitySet; TS_POINTER_CAPABILITYSET pointerCapabilitySet; TS_SHARE_CAPABILITYSET shareCapabilitySet; TS_INPUT_CAPABILITYSET inputCapabilitySet; TS_SOUND_CAPABILITYSET soundCapabilitySet; TS_FONT_CAPABILITYSET fontCapabilitySet; TS_GLYPHCACHE_CAPABILITYSET glyphCacheCapabilitySet; TS_BRUSH_CAPABILITYSET brushCapabilitySet; TS_OFFSCREEN_CAPABILITYSET offscreenCapabilitySet; TS_VIRTUALCHANNEL_CAPABILITYSET virtualchannelCapabilitySet; #ifdef DRAW_NINEGRID TS_DRAW_NINEGRID_CAPABILITYSET drawNineGridCapabilitySet; #endif #ifdef DRAW_GDIPLUS TS_DRAW_GDIPLUS_CAPABILITYSET drawGdiplusCapabilitySet; #endif } CC_COMBINED_CAPABILITIES; // ARG! Why isn't this const!!! CC_COMBINED_CAPABILITIES caps = { CC_COMBINED_CAPS_NUMBER_CAPABILITIES, /* Number of capabilities */ 0, /* padding */ /****************************************************************/ /* General caps */ /****************************************************************/ { TS_CAPSETTYPE_GENERAL, /* capabilitySetType */ sizeof(TS_GENERAL_CAPABILITYSET), /* lengthCapability */ TS_OSMAJORTYPE_WINDOWS, /* OSMajorType */ TS_OSMINORTYPE_WINDOWS_NT, /* OSMinorType */ TS_CAPS_PROTOCOLVERSION, /* protocolVersion */ 0, /* pad1 */ 0, /* generalCompressionTypes */ TS_EXTRA_NO_BITMAP_COMPRESSION_HDR | TS_FASTPATH_OUTPUT_SUPPORTED | TS_LONG_CREDENTIALS_SUPPORTED | TS_AUTORECONNECT_COOKIE_SUPPORTED | TS_ENC_SECURE_CHECKSUM,/*recv safer checksums */ FALSE, /* updateCapabilityFlag */ FALSE, /* remoteUnshareFlag */ 0, /* generalCompressionLevel */ 0, /* refreshRectSupport */ 0 /* suppressOutputSupport */ }, /****************************************************************/ /* Bitmap caps */ /****************************************************************/ { TS_CAPSETTYPE_BITMAP, /* capabilitySetType */ sizeof(TS_BITMAP_CAPABILITYSET), /* lengthCapability */ 8, /* Set in CC */ /* preferredBitsPerPixel */ TRUE, /* receive1BitPerPixel */ TRUE, /* receive4BitsPerPixel */ TRUE, /* receive8BitsPerPixel */ 1024, /* Set in CC */ /* desktopWidth */ 768, /* Set in CC */ /* desktopHeight */ 0, /* pad2 */ FALSE, /* desktopResizeFlag */ 1, /* bitmapCompressionFlag */ 0, /* highColorFlags */ 0, /* pad1 */ TRUE, /* multipleRectangleSupport*/ 0 /* pad2 */ }, /****************************************************************/ /* Order Caps */ /****************************************************************/ { TS_CAPSETTYPE_ORDER, /* capabilitySetType */ sizeof(TS_ORDER_CAPABILITYSET), /* lengthCapability */ {'\0','\0','\0','\0','\0','\0','\0','\0', '\0','\0','\0','\0','\0','\0','\0','\0'}, /* terminalDescriptor */ 0, /* pad1 */ 1, /* desktopSaveXGranularity */ 20, /* desktopSaveYGranularity */ 0, /* pad2 */ 1, /* maximumOrderLevel */ 0, /* numberFonts */ TS_ORDERFLAGS_ZEROBOUNDSDELTASSUPPORT | /* orderFlags */ TS_ORDERFLAGS_NEGOTIATEORDERSUPPORT | TS_ORDERFLAGS_COLORINDEXSUPPORT, { /********************************************************/ /* Order Support flags. */ /* */ /* The array index corresponds to the TS_NEG_xxx_INDEX */ /* value indicated (from at128.h) The values marked */ /* with an x in the first column are overwritten at run */ /* time by UH before CC sends the combined */ /* capabilities. */ /********************************************************/ 1, /* 0 TS_NEG_DSTBLT_INDEX destinationBltSupport */ 1, /* 1 TS_NEG_PATBLT_INDEX patternBltSupport */ 1, /* x 2 TS_NEG_SCRBLT_INDEX screenBltSupport */ 1, /* 3 TS_NEG_MEMBLT_INDEX memoryBltSupport */ 1, /* 4 TS_NEG_MEM3BLT_INDEX memoryThreeWayBltSupport */ 0, /* x 5 TS_NEG_ATEXTOUT_INDEX textASupport */ 0, /* x 6 TS_NEG_AEXTTEXTOUT_INDEX extendedTextASupport */ #ifdef DRAW_NINEGRID 1, /* 7 TS_NEG_RECTANGLE_INDEX rectangleSupport */ #else 0, #endif 1, /* 8 TS_NEG_LINETO_INDEX lineSupport */ #ifdef DRAW_NINEGRID 1, /* 9 TS_NEG_FASTFRAME_INDEX frameSupport */ #else 0, #endif 0, /* 10 TS_NEG_OPAQUERECT_INDEX opaqueRectangleSupport */ 1, /* x11 TS_NEG_SAVEBITMAP_INDEX desktopSaveSupport */ 0, /* x12 TS_NEG_WTEXTOUT_INDEX textWSupport */ 1, /* 13 TS_NEG_MEMBLT_R2_INDEX Reserved entry */ 1, /* 14 TS_NEG_MEM3BLT_R2_INDEX Reserved entry */ 1, /* 15 TS_NEG_MULTIDSTBLT_INDEX multi DstBlt support */ 1, /* 16 TS_NEG_MULTIPATBLT_INDEX multi PatBlt support */ 1, /* 17 TS_NEG_MULTISCRBLT_INDEX multi ScrBlt support */ 1, /* 18 TS_NEG_MULTIOPAQUERECT_INDEX multi OpaqueRect support */ 1, /* 19 TS_NEG_FAST_INDEX */ 1, /* 20 TS_NEG_POLYGON_SC_INDEX */ 1, /* 21 TS_NEG_POLYGON_CB_INDEX */ 1, /* 22 TS_NEG_POLYLINE_INDEX polyLineSupport */ 0, /* 23 MS reserved entry 2 */ 1, /* 24 TS_NEG_FAST_GLYPH_INDEX */ 1, /* 25 TS_NEG_ELLIPSE_SC_INDEX */ 1, /* 26 TS_NEG_ELLIPSE_CB_INDEX */ 0, /* 27 MS reserved entry 6 */ 0, /* x28 TS_NEG_WEXTTEXTOUT_INDEX extendedTextWSupport */ 0, /* x29 TS_NEG_WLONGTEXTOUT_INDEX longTextWSupport */ 0, /* x30 TS_NEG_WLONGEXTTEXTOUT_INDEX longExtendedTextWSupport */ 0, /* 31 DCL reserved entry 3 */ }, (TS_TEXT_AND_MASK)|(TS_TEXT_OR_MASK), /* textFlags */ 0, /* pad2 */ 0, /* pad4 */ 480 * 480, /* desktopSaveSize */ 0, /* pad2 */ 0, /* pad2 */ 0, /* textANSICodePage */ 0 /* pad2 */ }, /****************************************************************/ /* BitmapCache Caps Note that this same space is used for rev1 */ /* and rev2, we declare as rev1 because it is the larger of the */ /* two. We will cast to rev2 if we get a server advertisement */ /* that it supports rev2 (via */ /* TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT). */ /****************************************************************/ { TS_CAPSETTYPE_BITMAPCACHE_REV2, /* capabilitySetType */ sizeof(TS_BITMAPCACHE_CAPABILITYSET), /* lengthCapability */ 0, /* pad DWORDs 1 */ 0, /* pad DWORDs 2 */ 0, /* pad DWORDs 3 */ 0, /* pad DWORDs 4 */ 0, /* pad DWORDs 5 */ 0, /* pad DWORDs 6 */ 0, 0, /* Cache1 */ 0, 0, /* Cache2 */ 0, 0, /* Cache3 */ }, /****************************************************************/ /* ColorTableCache Caps */ /****************************************************************/ { TS_CAPSETTYPE_COLORCACHE, /* capabilitySetType */ sizeof(TS_COLORTABLECACHE_CAPABILITYSET), /* lengthCapability */ 6, /* colortableCacheSize */ 0 /* notpartOfTSharePad */ }, /****************************************************************/ /* WindowActivation Caps */ /****************************************************************/ { TS_CAPSETTYPE_ACTIVATION, /* capabilitySetType */ sizeof(TS_WINDOWACTIVATION_CAPABILITYSET), /* lengthCapability */ FALSE, /* helpKeyFlag */ FALSE, /* helpKeyIndexFlag */ FALSE, /* helpExtendedKeyFlag */ FALSE /* windowManagerKeyFlag */ }, /****************************************************************/ /* Control Caps */ /****************************************************************/ { TS_CAPSETTYPE_CONTROL, /* capabilitySetType */ sizeof(TS_CONTROL_CAPABILITYSET), /* lengthCapability */ 0, /* controlFlags */ FALSE, /* remoteDetachFlag */ TS_CONTROLPRIORITY_NEVER, /* controlInterest */ TS_CONTROLPRIORITY_NEVER /* detachInterest */ }, /****************************************************************/ /* Pointer Caps */ /****************************************************************/ { TS_CAPSETTYPE_POINTER, /* capabilitySetType */ sizeof(TS_POINTER_CAPABILITYSET), /* lengthCapability */ TRUE, /* colorPointerFlag */ 20, /* colorPointerCacheSize */ 21 /* pointerCacheSize */ }, /****************************************************************/ /* Share Caps */ /****************************************************************/ { TS_CAPSETTYPE_SHARE, /* capabilitySetType */ sizeof(TS_SHARE_CAPABILITYSET), /* lengthCapability */ 0, /* nodeId */ 0 /* padding */ }, /****************************************************************/ /* Input Caps */ /****************************************************************/ { TS_CAPSETTYPE_INPUT, sizeof(TS_INPUT_CAPABILITYSET), /* lengthCapability */ TS_INPUT_FLAG_SCANCODES | TS_INPUT_FLAG_MOUSEX, /* inputFlags */ TS_INPUT_FLAG_FASTPATH_INPUT2, /* padding */ 0 /* keyboard layout */ }, /****************************************************************/ /* Sound */ /****************************************************************/ { TS_CAPSETTYPE_SOUND, sizeof(TS_SOUND_CAPABILITYSET), /* lengthCapability */ TS_SOUND_FLAG_BEEPS, /* soundFlags */ 0, /* padding */ }, /****************************************************************/ /* Font */ /****************************************************************/ { TS_CAPSETTYPE_FONT, sizeof(TS_FONT_CAPABILITYSET), /* lengthCapability */ TS_FONTSUPPORT_FONTLIST, /* fontSupportFlags */ 0, /* padding */ }, /****************************************************************/ /* GlyphCache Caps */ /****************************************************************/ { TS_CAPSETTYPE_GLYPHCACHE, /* capabilitySetType */ sizeof(TS_GLYPHCACHE_CAPABILITYSET), /* lengthCapability */ { /* GlyphCache */ { 254, 4 }, { 254, 4 }, { 254, 8 }, { 254, 8 }, { 254, 16 }, { 254, 32 }, { 254, 64 }, { 254, 128 }, { 254, 256 }, { 254, 2048 } }, { 256, 256 }, /* FragCache */ 2, /* GlyphSupportLevel */ }, /****************************************************************/ /* Brush Caps */ /****************************************************************/ { TS_CAPSETTYPE_BRUSH, /* capabilitySetType */ sizeof(TS_BRUSH_CAPABILITYSET), /* lengthCapability */ 1, /* TS_BRUSH_COLOR8x8 */ /* brushSupportLevel */ }, // Enable this capability when GDI supports Device Bitmaps in mirroring // display drivers. /************************************************************************/ /* Offscreen Caps */ /************************************************************************/ { TS_CAPSETTYPE_OFFSCREENCACHE, /* capabilitySetType */ sizeof(TS_OFFSCREEN_CAPABILITYSET), /* lengthCapability */ TS_OFFSCREEN_SUPPORTED, /* offscreenSupportLevel */ TS_OFFSCREEN_CACHE_SIZE_SERVER_DEFAULT, /* offscreenCacheSize */ TS_OFFSCREEN_CACHE_ENTRIES_DEFAULT, /* offscreenCacheEntries */ }, /************************************************************************/ /* Virtual Channel Caps */ /************************************************************************/ { TS_CAPSETTYPE_VIRTUALCHANNEL, /* capabilitySetType */ sizeof(TS_VIRTUALCHANNEL_CAPABILITYSET), /* lengthCapability */ // // What this particular cap means is that the client understands // virtual channels compressed from the server at 64K. // // The client recevies what compression cap the server supports // from the client and compresses appropriately // TS_VCCAPS_COMPRESSION_64K,//TS_VCCAPS_DEFAULT?/* vc support flags */ #ifdef DRAW_NINEGRID }, { TS_CAPSETTYPE_DRAWNINEGRIDCACHE, // capabilitySetType sizeof(TS_DRAW_NINEGRID_CAPABILITYSET), // lengthCapability TS_DRAW_NINEGRID_SUPPORTED_REV2, // drawNineGridSupportLevel TS_DRAW_NINEGRID_CACHE_SIZE_DEFAULT, // drawNineGridCacheSize TS_DRAW_NINEGRID_CACHE_ENTRIES_DEFAULT, // drawNineGridCacheEntries #endif #ifdef DRAW_GDIPLUS }, { TS_CAPSETTYPE_DRAWGDIPLUS, // capabilitySetType sizeof(TS_DRAW_GDIPLUS_CAPABILITYSET), // lengthCapability TS_DRAW_GDIPLUS_SUPPORTED, // drawEscapeSupportLevel 0xFFFFFFFF, // TSGdiplusVersion TS_DRAW_GDIPLUS_CACHE_LEVEL_ONE, // drawGdiplusCacheLevel TS_GDIP_GRAPHICS_CACHE_ENTRIES_DEFAULT, TS_GDIP_BRUSH_CACHE_ENTRIES_DEFAULT, TS_GDIP_PEN_CACHE_ENTRIES_DEFAULT, TS_GDIP_IMAGE_CACHE_ENTRIES_DEFAULT, TS_GDIP_IMAGEATTRIBUTES_CACHE_ENTRIES_DEFAULT, TS_GDIP_GRAPHICS_CACHE_CHUNK_SIZE_DEFAULT, TS_GDIP_BRUSH_CACHE_CHUNK_SIZE_DEFAULT, TS_GDIP_PEN_CACHE_CHUNK_SIZE_DEFAULT, TS_GDIP_IMAGEATTRIBUTES_CACHE_CHUNK_SIZE_DEFAULT, TS_GDIP_IMAGE_CACHE_CHUNK_SIZE_DEFAULT, // Chunk size to store image cache TS_GDIP_IMAGE_CACHE_TOTAL_SIZE_DEFAULT, // Total size of image cache in number of chunks TS_GDIP_IMAGE_CACHE_MAX_SIZE_DEFAULT, // Maximun size of image to cache, in number of chunks #endif } }; /********************************************************************/ /* set up bitmap cache caps */ /********************************************************************/ { TS_BITMAPCACHE_CAPABILITYSET_REV2 *pRev2Caps; // Rev2 caps. pRev2Caps = (TS_BITMAPCACHE_CAPABILITYSET_REV2 *)&caps.bitmapCacheCaps; TRC_ALT((TB,"Preparing REV2 caps for server\n")); pRev2Caps->capabilitySetType = TS_CAPSETTYPE_BITMAPCACHE_REV2; pRev2Caps->NumCellCaches = 3; pRev2Caps->bPersistentKeysExpected = FALSE; pRev2Caps->bAllowCacheWaitingList = FALSE; pRev2Caps->CellCacheInfo[0].bSendBitmapKeys = FALSE; pRev2Caps->CellCacheInfo[0].NumEntries = 600; pRev2Caps->CellCacheInfo[1].bSendBitmapKeys = FALSE; pRev2Caps->CellCacheInfo[1].NumEntries = 300; pRev2Caps->CellCacheInfo[2].bSendBitmapKeys = FALSE; pRev2Caps->CellCacheInfo[2].NumEntries = 300; pRev2Caps->CellCacheInfo[3].bSendBitmapKeys = 0; pRev2Caps->CellCacheInfo[3].NumEntries = 0; pRev2Caps->CellCacheInfo[4].bSendBitmapKeys = 0; pRev2Caps->CellCacheInfo[4].NumEntries = 0; } /********************************************************************/ /* and screen size */ /********************************************************************/ { PTS_BITMAP_CAPABILITYSET pBmpCaps; pBmpCaps = (TS_BITMAP_CAPABILITYSET *)&caps.bitmapCapabilitySet; pBmpCaps->desktopWidth = (TSUINT16)(m_pTSWd->desktopWidth); pBmpCaps->desktopHeight = (TSUINT16)(m_pTSWd->desktopHeight); #ifdef DC_HICOLOR pBmpCaps->preferredBitsPerPixel = (TSUINT16)(m_pTSWd->desktopBpp); #endif } if (!SCCallPartyJoiningShare(localPersonID, sizeof(caps), &caps, acceptedArray, scNumberInShare)) { /****************************************************************/ /* Some component rejected the remote party */ /****************************************************************/ TRC_ERR((TB, "Remote party rejected")); DC_QUIT; } /********************************************************************/ /* The remote party is now in the share. */ /********************************************************************/ callingPJS = FALSE; rc = TRUE; scNumberInShare++; TRC_NRM((TB, "Number in share %d", (unsigned)scNumberInShare)); /********************************************************************/ /* Synchronise only for primary stacks. Shadow stacks will be */ /* sync'd by the DD right before output starts. */ /********************************************************************/ SCInitiateSync(m_pTSWd->StackClass == Stack_Shadow); /********************************************************************/ /* don't wait for a response - there isn't a client out there. */ /* Just wake up the WD now */ /********************************************************************/ TRC_NRM((TB, "Wake up WDW")); WDW_ShareCreated(m_pTSWd, TRUE); DC_QUIT; } /************************************************************************/ /* Get the combined capabilities */ /************************************************************************/ CPC_GetCombinedCapabilities(SC_LOCAL_PERSON_ID, &capsSize, &caps); /************************************************************************/ /* If we support dynamic client resizing, then we need to update the */ /* desktop width and height in the caps passed out on the demand active */ /* PDU to notify the client of the change */ /************************************************************************/ pBitmapCaps = (PTS_BITMAP_CAPABILITYSET) WDW_GetCapSet( m_pTSWd, TS_CAPSETTYPE_BITMAP, caps, capsSize); if (pBitmapCaps) { if (pBitmapCaps->desktopResizeFlag == TS_CAPSFLAG_SUPPORTED) { TRC_ALT((TB, "Update client desktop size")); pBitmapCaps->desktopHeight = (TSUINT16)(m_pTSWd->desktopHeight); pBitmapCaps->desktopWidth = (TSUINT16)(m_pTSWd->desktopWidth); } #ifdef DC_HICOLOR /********************************************************************/ /* For high color, update the color depth too */ /********************************************************************/ pBitmapCaps->preferredBitsPerPixel = (TSUINT16)(m_pTSWd->desktopBpp); #endif } /************************************************************************/ /* Get the sessionId from the WD structure */ /************************************************************************/ sessionId = m_pTSWd->sessionId; /************************************************************************/ /* Calculate the size of the various bits of the TS_DEMAND_ACTIVE_PDU */ /************************************************************************/ pktLen = sizeof(TS_DEMAND_ACTIVE_PDU) - 1 + sizeof(UINT32); nameLen = strlen(scPartyArray[0].name); nameLen = (unsigned)DC_ROUND_UP_4(nameLen); pktLen += nameLen; pktLen += capsSize; /************************************************************************/ // Get a buffer - this should not fail, so abort if it does // 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 ) { // Calculate a new shareID. scGeneration++; scShareID = scUserID | (((UINT32)(scGeneration & 0xFFFF)) << 16); // Fill in the packet fields. pPkt->shareControlHeader.totalLength = (UINT16)pktLen; pPkt->shareControlHeader.pduType = TS_PDUTYPE_DEMANDACTIVEPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16)scUserID; pPkt->shareID = scShareID; pPkt->lengthSourceDescriptor = (UINT16)nameLen; pPkt->lengthCombinedCapabilities = (UINT16)capsSize; memcpy(&(pPkt->data[0]), scPartyArray[0].name, nameLen); memcpy(&(pPkt->data[nameLen]), caps, capsSize); memcpy(&(pPkt->data[nameLen+capsSize]), &sessionId, sizeof(sessionId)); // Send it. rc = SM_SendData(scPSMHandle, pPkt, pktLen, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (rc) { TRC_ALT((TB, "%s Stack sent TS_DEMAND_ACTIVE_PDU", m_pTSWd->StackClass == Stack_Primary ? "Primary" : (m_pTSWd->StackClass == Stack_Shadow ? "Shadow" : "PassThru"))); } else { TRC_ERR((TB, "Failed to send TS_DEMAND_ACTIVE_PDU")); } } else { TRC_ERR((TB, "Failed to alloc %d bytes for TS_DEMAND_ACTIVE_PDU", pktLen)); rc = FALSE; DC_QUIT; } /************************************************************************/ /* Change SC state. */ /************************************************************************/ SC_SET_STATE(SCS_SHARE_STARTING) DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* SC_SendServerCert */ /* */ /* Sends the target server's random + certificate to a client server for */ /* use in shadowing. */ /****************************************************************************/ NTSTATUS RDPCALL SHCLASS SC_SendServerCert(PSHADOWCERT pCert, ULONG ulLength) { PTS_SERVER_CERTIFICATE_PDU pPkt; ULONG ulPktSize; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SC_SendServerCert"); ulPktSize = sizeof(TS_SERVER_CERTIFICATE_PDU) - 1 + pCert->shadowRandomLen + pCert->shadowCertLen; TRC_ERR((TB, "handle: %p, &pPkt: %p, size: %ld", m_pTSWd->pSmInfo, &pPkt, ulPktSize)); status = SM_AllocBuffer(m_pTSWd->pSmInfo, (PPVOID) &pPkt, ulPktSize, TRUE, FALSE); if ( STATUS_SUCCESS == status ) { // Fill in the packet fields. pPkt->shareControlHeader.totalLength = (UINT16) ulPktSize; pPkt->shareControlHeader.pduType = TS_PDUTYPE_SERVERCERTIFICATEPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16) scUserID; pPkt->encryptionMethod = pCert->encryptionMethod; pPkt->encryptionLevel = pCert->encryptionLevel; pPkt->shadowRandomLen = pCert->shadowRandomLen; pPkt->shadowCertLen = pCert->shadowCertLen; // Copy over the random + cert if (pPkt->encryptionLevel != 0) { memcpy(pPkt->data, pCert->data, pCert->shadowRandomLen + pCert->shadowCertLen); } // Send ServerCertificatePDU rc = SM_SendData(m_pTSWd->pSmInfo, pPkt, ulPktSize, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (rc) { status = STATUS_SUCCESS; TRC_ALT((TB, "Sent TS_SERVER_CERTIFICATE_PDU: %ld", ulPktSize)); } else { status = STATUS_UNEXPECTED_IO_ERROR; TRC_ERR((TB, "Failed to send TS_SERVER_CERTIFICATE_PDU")); } } else { status = STATUS_NO_MEMORY; TRC_ERR((TB, "Failed to alloc %d bytes for TS_SERVER_CERTIFICATE_PDU", ulPktSize)); } DC_END_FN(); return status; } /****************************************************************************/ /* SC_SaveServerCert */ /* */ /* Save the server certificate + random for subsequent validation by rdpwsx.*/ /* A NULL pPkt indicates we should save an empty certificate. */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_SaveServerCert(PTS_SERVER_CERTIFICATE_PDU pPkt, ULONG ulLength) { PSHADOWCERT pCert; ULONG ulCertLen; DC_BEGIN_FN("SC_SaveServerCert"); // Save off the data and signal rdpwsx that we got it. We will actually // perform the validation in user mode. if (pPkt != NULL) { ulCertLen = pPkt->shadowRandomLen + pPkt->shadowCertLen; pCert = (PSHADOWCERT) COM_Malloc(sizeof(SHADOWCERT) + ulCertLen - 1); if (pCert != NULL) { pCert->encryptionMethod = pPkt->encryptionMethod; pCert->encryptionLevel = pPkt->encryptionLevel; pCert->shadowRandomLen = pPkt->shadowRandomLen; pCert->shadowCertLen = pPkt->shadowCertLen; // If the encryption level is non-zero, then we should have a server random // and certificate following the initial header if (pCert->encryptionLevel != 0) { memcpy(pCert->data, pPkt->data, ulCertLen); } TRC_ALT((TB, "Received certificate[%ld], level=%ld, method=%lx, random[%ld]", pCert->shadowCertLen, pCert->encryptionLevel, pCert->encryptionMethod, pCert->shadowRandomLen)); // Update SM parameters with negotiated values SM_SetEncryptionParams(m_pTSWd->pSmInfo, pCert->encryptionLevel, pCert->encryptionMethod); } else { TRC_ERR((TB, "Could not allocate space for server cert: %ld!", ulCertLen)); } } // Else, the target server did not send back a certificate (B3) else { pCert = (PSHADOWCERT) COM_Malloc(sizeof(SHADOWCERT)); if (pCert != NULL) { memset(pCert, 0, sizeof(SHADOWCERT)); } else { TRC_ERR((TB, "Could not allocate space for server cert: %ld!", sizeof(SHADOWCERT))); } } // wake up the rdpwsx thread which is waiting on this information m_pTSWd->pShadowCert = pCert; KeSetEvent(m_pTSWd->pSecEvent, 0, FALSE); DC_END_FN(); return TRUE; } /* SC_SaveServerCert */ /****************************************************************************/ /* SC_SendClientRandom */ /* */ /* Send the encrypted client random to the shadow target server. */ /****************************************************************************/ NTSTATUS RDPCALL SHCLASS SC_SendClientRandom(PBYTE pClientRandom, ULONG ulLength) { PTS_CLIENT_RANDOM_PDU pPkt; ULONG ulPktSize; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SC_SendClientRandom"); ulPktSize = sizeof(TS_CLIENT_RANDOM_PDU) - 1 + ulLength; status = NM_AllocBuffer(m_pTSWd->pNMInfo, (PPVOID) &pPkt, ulPktSize, TRUE); if (STATUS_SUCCESS == status) { // Fill in the packet fields. pPkt->shareControlHeader.totalLength = (UINT16) ulPktSize; pPkt->shareControlHeader.pduType = TS_PDUTYPE_CLIENTRANDOMPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16)scUserID; pPkt->clientRandomLen = ulLength; // Copy over the random TRC_ALT((TB, "PDUType: %lx, random length: %ld, pktSize: %ld", pPkt->shareControlHeader.pduType, ulLength, ulPktSize)); memcpy(pPkt->data, pClientRandom, ulLength); TRC_DATA_DBG("snd random: ", pClientRandom, ulLength); rc = NM_SendData(m_pTSWd->pNMInfo, (PBYTE) pPkt, ulPktSize, TS_HIGHPRIORITY, 0, FALSE); if (rc) { status = STATUS_SUCCESS; TRC_ALT((TB, "Sent TS_CLIENT_RANDOM_PDU: %ld", ulPktSize)); } else { status = STATUS_UNEXPECTED_IO_ERROR; TRC_ERR((TB, "Failed to send TS_CLIENT_RANDOM_PDU")); } } else { status = STATUS_NO_MEMORY; TRC_ERR((TB, "Failed to alloc %d bytes for TS_CLIENT_RANDOM_PDU", ulPktSize)); } DC_END_FN(); return status; } /****************************************************************************/ /* SC_SaveClientRandom */ /* */ /* Save the encrypted client random for subsequent use by rdpwsx. */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_SaveClientRandom(PTS_CLIENT_RANDOM_PDU pPkt, ULONG ulLength) { PCLIENTRANDOM pClientRandom; BOOL rc = FALSE; DC_BEGIN_FN("SC_SaveClientRandom"); //Validate data length if ((ulLength < sizeof(TS_CLIENT_RANDOM_PDU)) || (ulLength + sizeof(TSUINT8) - sizeof(TS_CLIENT_RANDOM_PDU)) < pPkt->clientRandomLen) { TRC_ERR((TB, "Bad client random length: %ld", pPkt->clientRandomLen)); return FALSE; } // The largest possible client random size is 512, // defined in SendClientRandom() in tsrvsec.c if (pPkt->clientRandomLen > CLIENT_RANDOM_MAX_SIZE) { TRC_ERR((TB, "Client random length is too large: %ld", pPkt->clientRandomLen)); return FALSE; } // Save off the data and signal rdpwsx that we got it. We will actually // perform the decryption in user mode. pClientRandom = (PCLIENTRANDOM) COM_Malloc(sizeof(CLIENTRANDOM) - 1 + pPkt->clientRandomLen); if (pClientRandom != NULL) { pClientRandom->clientRandomLen = pPkt->clientRandomLen; memcpy(pClientRandom->data, pPkt->data, pPkt->clientRandomLen); TRC_ALT((TB, "Received encrypted client random: @%p, len=%ld", pClientRandom, pPkt->clientRandomLen)); TRC_DATA_DBG("sav random: ", pClientRandom->data, pClientRandom->clientRandomLen); } else { TRC_ERR((TB, "Could not allocate space for client random: %ld!", pPkt->clientRandomLen)); } // Free pShadowRandom in case it was allocated before if (NULL != m_pTSWd->pShadowRandom) { COM_Free(m_pTSWd->pShadowRandom); m_pTSWd->pShadowRandom = NULL; } // wake up the termsrv thread which is waiting on this information m_pTSWd->pShadowRandom = pClientRandom; KeSetEvent (m_pTSWd->pSecEvent, 0, FALSE); DC_END_FN(); return TRUE; } /****************************************************************************/ /* SC_GetSecurityData */ /* */ /* Wait for either a server certificate or a client random and return the */ /* data to rdpwsx. */ /****************************************************************************/ NTSTATUS RDPCALL SHCLASS SC_GetSecurityData(PSD_IOCTL pSdIoctl) { PSECURITYTIMEOUT pSecurityTimeout = (PSECURITYTIMEOUT) pSdIoctl->InputBuffer; ULONG ulBytesNeeded = 0; NTSTATUS status; DC_BEGIN_FN("SC_GetSecurityData"); // Wait for the connected indication from SC (if necessary) if (((pSdIoctl->IoControlCode == IOCTL_TSHARE_GET_CERT_DATA) && (pSdIoctl->OutputBufferLength == 0)) || (pSdIoctl->IoControlCode == IOCTL_TSHARE_GET_CLIENT_RANDOM)) { TRC_ALT((TB, "About to wait for %s data", pSdIoctl->IoControlCode == IOCTL_TSHARE_GET_CERT_DATA ? "Server Certificate" : "Client Random")); if (pSdIoctl->InputBufferLength == sizeof(SECURITYTIMEOUT)) { status = WDW_WaitForConnectionEvent(m_pTSWd, m_pTSWd->pSecEvent, pSecurityTimeout->ulTimeout); } else { status = STATUS_INVALID_PARAMETER; TRC_ERR((TB, "Bogus timeout value structure: Length [%ld] != Expected [%ld]", pSdIoctl->InputBufferLength, sizeof(SECURITYTIMEOUT))); DC_QUIT; } TRC_ALT((TB, "Back from wait for security data")); if (status != STATUS_SUCCESS) { TRC_ERR((TB, "Error waiting for security data: %lx, msec=%ld", status, pSecurityTimeout->ulTimeout)); if (!NT_ERROR(status)) { status = STATUS_IO_TIMEOUT; } DC_QUIT; } } // Server certificate + random if (pSdIoctl->IoControlCode == IOCTL_TSHARE_GET_CERT_DATA) { if (m_pTSWd->pShadowCert != NULL) { ULONG ulCertLength = m_pTSWd->pShadowCert->shadowCertLen + m_pTSWd->pShadowCert->shadowRandomLen; ulBytesNeeded = sizeof(SHADOWCERT) - 1 + ulCertLength; // Return the length so rdpwsx can alloc the right amount of memory. if ((pSdIoctl->OutputBuffer == NULL) || (pSdIoctl->OutputBufferLength < ulBytesNeeded)) { TRC_ALT((TB, "Cert[%ld] + Rand[%ld] buffer too small: %ld < %ld", m_pTSWd->pShadowCert->shadowCertLen, m_pTSWd->pShadowCert->shadowRandomLen, pSdIoctl->OutputBufferLength, ulBytesNeeded)); status = STATUS_BUFFER_TOO_SMALL; } // else, return the data to rdpwsx else { PSHADOWCERT pShadowCertOut = (PSHADOWCERT) pSdIoctl->OutputBuffer; PSHADOWCERT pShadowCertIn = m_pTSWd->pShadowCert; pShadowCertOut->encryptionMethod = pShadowCertIn->encryptionMethod; pShadowCertOut->encryptionLevel = pShadowCertIn->encryptionLevel; pShadowCertOut->shadowRandomLen = pShadowCertIn->shadowRandomLen; pShadowCertOut->shadowCertLen = pShadowCertIn->shadowCertLen; memcpy(pShadowCertOut->data, pShadowCertIn->data, ulCertLength); // Free up the temporary buffer COM_Free(m_pTSWd->pShadowCert); m_pTSWd->pShadowCert = NULL; status = STATUS_SUCCESS; } } // We were unable to save the certificate! else { TRC_ERR((TB, "Saved certificate not found!")); status = STATUS_NO_MEMORY; } } // Encrypted client random else if (pSdIoctl->IoControlCode == IOCTL_TSHARE_GET_CLIENT_RANDOM) { if (m_pTSWd->pShadowRandom != NULL) { ulBytesNeeded = m_pTSWd->pShadowRandom->clientRandomLen; // Return the length so rdpwsx can alloc the right amount of memory. if ((pSdIoctl->OutputBuffer == NULL) || (pSdIoctl->OutputBufferLength < ulBytesNeeded)) { status = STATUS_BUFFER_TOO_SMALL; TRC_ALT((TB, "Client random buffer too small: %ld < %ld", pSdIoctl->OutputBufferLength, ulBytesNeeded)); } // else, return the data to rdpwsx else { PCLIENTRANDOM pRandomIn = m_pTSWd->pShadowRandom; PBYTE pRandomOut = (PBYTE) pSdIoctl->OutputBuffer; TRC_ALT((TB, "Received client random: @%p, len=%ld", pRandomIn, ulBytesNeeded)); memcpy(pRandomOut, pRandomIn->data, ulBytesNeeded); TRC_DATA_DBG("rcv random: ", pRandomOut, ulBytesNeeded); // Free up the temporary buffer COM_Free(m_pTSWd->pShadowRandom); m_pTSWd->pShadowRandom = NULL; status = STATUS_SUCCESS; } } // We were unable to save the encrypted random! else { TRC_ERR((TB, "Saved encrypted random not found!")); status = STATUS_NO_MEMORY; } } else { TRC_ERR((TB, "Unrecognized ioctl: %lx", pSdIoctl->IoControlCode)); status = STATUS_INVALID_PARAMETER; } DC_EXIT_POINT: pSdIoctl->BytesReturned = ulBytesNeeded; DC_END_FN(); return status; } /****************************************************************************/ /* Name: SC_ShadowSyncShares */ /* */ /* See ascapi.h */ /****************************************************************************/ #ifdef DC_HICOLOR BOOL RDPCALL SHCLASS SC_ShadowSyncShares(PTS_COMBINED_CAPABILITIES pCaps, ULONG capsLen) #else BOOL RDPCALL SHCLASS SC_ShadowSyncShares(void) #endif { BOOL rc = TRUE; ShareClass *dcShare = (ShareClass *)m_pTSWd->dcShare; DC_BEGIN_FN("SC_SyncShare"); TRC_ASSERT((dcShare != NULL), (TB, "NULL Share Class")); #ifdef DC_HICOLOR /************************************************************************/ /* if we're the primary or console, update the caps with those supplied */ /* for the shadower. It can only "lower" the capabilities, so there's */ /* no problem with setting up caps the target can't cope with */ /************************************************************************/ if ((m_pTSWd->StackClass == Stack_Primary) || (m_pTSWd->StackClass == Stack_Console)) { BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; TRC_ALT((TB, "Update caps for shadower")); // Free the memory if (cpcRemoteCombinedCaps[SC_SHADOW_PERSON_ID - 1] != NULL) { COM_Free((PVOID)cpcRemoteCombinedCaps[SC_SHADOW_PERSON_ID - 1]); cpcRemoteCombinedCaps[SC_SHADOW_PERSON_ID - 1] = NULL; } SCCallPartyJoiningShare(SC_SHADOW_PERSON_ID, capsLen, pCaps, acceptedArray, scNumberInShare); /********************************************************************/ /* Now update the DD caps via the shared mem */ /********************************************************************/ DCS_TriggerUpdateShmCallback(); } #endif SCInitiateSync(TRUE); TRC_ALT((TB, "Synchronized Shares")); DC_END_FN(); return(rc); } /* SC_ShadowSyncShares */ /****************************************************************************/ /* Name: SC_EndShare */ /* */ /* Ends a share */ /****************************************************************************/ void RDPCALL SHCLASS SC_EndShare(BOOLEAN bForce) { PTS_DEACTIVATE_ALL_PDU pPkt; NTSTATUS status; BOOL rc; DC_BEGIN_FN("SC_EndShare"); // Due to the way a shadow hotkey terminates a session, we sometimes need // to force sending of a deactivate all PDU. This is done explicitly // instead of changing the state table so we don't effect normal connect // processing. if (!bForce) { SC_CHECK_STATE(SCE_END_SHARE); } else { TRC_ALT((TB, "Forcing deactivate all PDU")); } /************************************************************************/ // Get a buffer - this should not fail, so abort if it does // fWait is TRUE means that we will always wait for a buffer to be avail /************************************************************************/ status = SM_AllocBuffer(scPSMHandle, (PPVOID)(&pPkt), sizeof(TS_DEACTIVATE_ALL_PDU), TRUE, FALSE); if ( STATUS_SUCCESS == status ) { // Fill in the packet fields. pPkt->shareControlHeader.totalLength = sizeof(TS_DEACTIVATE_ALL_PDU); pPkt->shareControlHeader.pduType = TS_PDUTYPE_DEACTIVATEALLPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16)scUserID; pPkt->shareID = scShareID; pPkt->lengthSourceDescriptor = 1; pPkt->sourceDescriptor[0] = 0; // Send DeactivateAllPDU. rc = SM_SendData(scPSMHandle, pPkt, sizeof(TS_DEACTIVATE_ALL_PDU), TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (rc) { TRC_ALT((TB, "Sent DeactivateAllPDU")); SCEndShare(); } else { TRC_ERR((TB, "Failed to send TS_DEACTIVATE_ALL_PDU")); } } else { TRC_ERR((TB, "Failed to alloc %d bytes for TS_DEACTIVATE_ALL_PDU", sizeof(PTS_DEACTIVATE_ALL_PDU))); } DC_EXIT_POINT: DC_END_FN(); } /****************************************************************************/ // SC_OnDisconnected // // Handles disconnection notification. /****************************************************************************/ void RDPCALL SHCLASS SC_OnDisconnected(UINT32 userID) { DC_BEGIN_FN("SC_OnDisconnected"); if (scNumberInShare != 0) { SC_CHECK_STATE(SCE_DETACH_USER); TRC_NRM((TB, "User %u detached", userID)); // Do the real work... SCEndShare(); } else { TRC_NRM((TB, "Share already ended: nothing more to do")); } DC_EXIT_POINT: DC_END_FN(); } /* SC_OnDisconnected */ /****************************************************************************/ /* Name: SC_OnDataReceived */ /* */ /* Purpose: Callback from SM for data receive path. */ /* */ /* Params: netPersonID - MCS UserID of the data sender */ /* priority - MCS data priority the data was sent on */ /* pPkt - pointer to start of packet */ /****************************************************************************/ void RDPCALL ShareClass::SC_OnDataReceived( PBYTE pPkt, NETPERSONID netPersonID, unsigned DataLength, UINT32 priority) { UINT16 pduType, pduType2; LOCALPERSONID localID; BOOL pduOK = FALSE; DC_BEGIN_FN("SC_OnDataReceived"); TRC_NRM((TB, "Data Received")); if (DataLength >= sizeof(TS_SHARECONTROLHEADER)) { pduType = (((PTS_SHARECONTROLHEADER)pPkt)->pduType) & TS_MASK_PDUTYPE; TRC_NRM((TB, "[%u]SC packet type %u", netPersonID, pduType)); } else { TRC_ERR((TB,"Data len %u too small for share ctrl header", DataLength)); goto ShortPDU; } if (pduType == TS_PDUTYPE_DATAPDU) { /********************************************************************/ /* Data PDU. This is critical path so decode inline. */ /********************************************************************/ if (DataLength >= sizeof(TS_SHAREDATAHEADER)) { pduType2 = ((PTS_SHAREDATAHEADER)pPkt)->pduType2; SC_CHECK_STATE(SCE_DATAPACKET); pduOK = TRUE; } else { TRC_ERR((TB,"Data len %u too small for share data header", DataLength)); goto ShortPDU; } #ifdef DC_DEBUG { /****************************************************************/ /* Ok, this is ugly. I'm trying to trace the PDU name without */ /* - searching a table */ /* - implementing a sparse table */ /* - a huge if ... else if switch. */ /* If you don't like it or don't understand it, ask MF. Before */ /* you get too het up, bear in mind the #ifdef DC_DEBUG above. */ /****************************************************************/ unsigned pduIndex = (pduType2 == TS_PDUTYPE2_UPDATE) ? 1 : (pduType2 == TS_PDUTYPE2_FONT) ? 2 : (pduType2 == TS_PDUTYPE2_CONTROL) ? 3 : ((pduType2 >= TS_PDUTYPE2_WINDOWACTIVATION) && (pduType2 <= TS_PDUTYPE2_BITMAPCACHE_ERROR_PDU)) ? pduType2 - 19 : 0; TRC_NRM((TB, "DataPDU type %s (%u)", scPktName[pduIndex], pduType2)); } #endif /********************************************************************/ /* First check for synchronize packets */ /********************************************************************/ if (pduType2 != TS_PDUTYPE2_SYNCHRONIZE) { /****************************************************************/ /* Now check that this priority has been synchronized */ /****************************************************************/ localID = SC_NetworkIDToLocalID(netPersonID); if (!scPartyArray[localID].sync[priority]) { TRC_ALT((TB, "[%d] {%d} Discarding packet on unsynched priority %d", netPersonID, localID, priority)); DC_QUIT; } /****************************************************************/ /* All is well - pass the packet to its destination */ /****************************************************************/ switch (pduType2) { case TS_PDUTYPE2_INPUT: // Note that changes to this path should be examined // in light of fast-path input (IM_DecodeFastPathInput). IM_PlaybackEvents((PTS_INPUT_PDU)pPkt, DataLength); break; case TS_PDUTYPE2_CONTROL: CA_ReceivedPacket((PTS_CONTROL_PDU)pPkt, DataLength, localID); break; case TS_PDUTYPE2_FONTLIST: USR_ProcessRemoteFonts((PTS_FONT_LIST_PDU)pPkt, DataLength, localID); break; case TS_PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST: // Persistent bitmap cache key list PDU. SBC_HandlePersistentCacheList( (TS_BITMAPCACHE_PERSISTENT_LIST *)pPkt, DataLength, localID); break; case TS_PDUTYPE2_BITMAPCACHE_ERROR_PDU: // For future support of bitmap error PDU SBC_HandleBitmapCacheErrorPDU( (TS_BITMAPCACHE_ERROR_PDU *)pPkt, DataLength, localID); break; case TS_PDUTYPE2_OFFSCRCACHE_ERROR_PDU: // offscreen cache error PDU SBC_HandleOffscrCacheErrorPDU( (TS_OFFSCRCACHE_ERROR_PDU *)pPkt, DataLength, localID); break; #ifdef DRAW_NINEGRID case TS_PDUTYPE2_DRAWNINEGRID_ERROR_PDU: // drawninegrid error PDU SBC_HandleDrawNineGridErrorPDU( (TS_DRAWNINEGRID_ERROR_PDU *)pPkt, DataLength, localID); break; #endif #ifdef DRAW_GDIPLUS case TS_PDUTYPE2_DRAWGDIPLUS_ERROR_PDU: SBC_HandleDrawGdiplusErrorPDU( (TS_DRAWGDIPLUS_ERROR_PDU *)pPkt, DataLength, localID); break; #endif case TS_PDUTYPE2_REFRESH_RECT: WDW_InvalidateRect(m_pTSWd, (PTS_REFRESH_RECT_PDU)pPkt, DataLength); break; case TS_PDUTYPE2_SUPPRESS_OUTPUT: UP_ReceivedPacket((PTS_SUPPRESS_OUTPUT_PDU)pPkt, DataLength, localID); break; case TS_PDUTYPE2_SHUTDOWN_REQUEST: DCS_ReceivedShutdownRequestPDU((PTS_SHAREDATAHEADER)pPkt, DataLength, localID); break; default: /********************************************************/ /* Unknown pduType2 */ /********************************************************/ TRC_ERR((TB, "Unknown data packet %d", pduType2)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_UnknownPDUType2, (BYTE *)&pduType2, sizeof(pduType2)); break; } } else { TRC_NRM((TB, "Synchronize PDU")); SCSynchronizePDU(netPersonID, priority,(PTS_SYNCHRONIZE_PDU)pPkt); } } else { /********************************************************************/ /* Control PDU. Not critical so throw to handler. */ /********************************************************************/ TRC_DBG((TB, "Control PDU")); SCReceivedControlPacket(netPersonID, priority, (PVOID)pPkt, DataLength); pduOK = TRUE; } DC_EXIT_POINT: if (!pduOK) { /********************************************************************/ /* An out-of-sequence packet has been received. It's possible we */ /* might receive an input PDU just after we brought the share down, */ /* so don't kick off the client for that. */ /********************************************************************/ TRC_ERR((TB, "Out-of-sequence packet %hx/%hd received in state %d", pduType, pduType2, scState)); if ((pduType == TS_PDUTYPE_DATAPDU) && (pduType2 == TS_PDUTYPE2_INPUT)) { TRC_ERR((TB, "Not kicking client off: it was only an input PDU")); } else { /****************************************************************/ /* Disconnect the client. */ /****************************************************************/ wchar_t detailData[(sizeof(pduType) * 2) + (sizeof(pduType2) * 2) + (sizeof(scState) * 2) + 3]; TRC_ERR((TB, "Kicking client off")); swprintf(detailData, L"%hx %hx %x", pduType, pduType2, scState); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_DataPDUSequence, (BYTE *)&detailData, sizeof(detailData)); } } DC_END_FN(); return; // Error handling. ShortPDU: WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShareDataTooShort, pPkt, DataLength); DC_END_FN(); } /* SC_OnDataReceived */ /****************************************************************************/ /* Name: SC_OnShadowDataReceived */ /* */ /* Purpose: Callback from SM for shadow data receive path. Main purpose */ /* is to scan for the shadow hotkey being pressed. */ /* */ /* Params: netPersonID - MCS UserID of the data sender */ /* priority - MCS data priority the data was sent on */ /* pPkt - pointer to start of packet */ /****************************************************************************/ void RDPCALL ShareClass::SC_OnShadowDataReceived( PBYTE pPkt, NETPERSONID netPersonID, unsigned DataLength, UINT32 priority) { UINT16 pduType, pduType2; LOCALPERSONID localID; BOOLEAN bShadowData = TRUE; NTSTATUS status; DC_BEGIN_FN("SC_OnShadowDataReceived"); TRC_NRM((TB, "Shadow data Received")); // If this is the primary client stack, then process the data and figure out // whether or not the PDU should be passed on to the target. Else this is // a passthru stack and we should forward PDUs regardless. // Note IM_DecodeFastPathInput() performs this logic for fast-path input. if (m_pTSWd->StackClass == Stack_Primary) { // check that we have enough data before we deref the pduType. if (sizeof(TS_SHARECONTROLHEADER) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_SHARECONTROLHEADER %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } pduType = (((PTS_SHARECONTROLHEADER)pPkt)->pduType) & TS_MASK_PDUTYPE; TRC_NRM((TB, "[%u]SC packet type %u", netPersonID, pduType)); if (pduType == TS_PDUTYPE_DATAPDU) { /********************************************************************/ /* Data PDU. This is critical path so decode inline. */ /********************************************************************/ if (sizeof(TS_SHAREDATAHEADER) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_SHAREDATAHEADER %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } pduType2 = ((PTS_SHAREDATAHEADER)pPkt)->pduType2; #ifdef DC_DEBUG { /****************************************************************/ /* Ok, this is ugly. I'm trying to trace the PDU name without */ /* - searching a table */ /* - implementing a sparse table */ /* - a huge if ... else if switch. */ /* If you don't like it or don't understand it, ask MF. Before */ /* you get too het up, bear in mind the #ifdef DC_DEBUG above. */ /****************************************************************/ unsigned pduIndex = (pduType2 == TS_PDUTYPE2_UPDATE) ? 1 : (pduType2 == TS_PDUTYPE2_FONT) ? 2 : (pduType2 == TS_PDUTYPE2_CONTROL) ? 3 : ((pduType2 >= TS_PDUTYPE2_WINDOWACTIVATION) && (pduType2 <= TS_PDUTYPE2_BITMAPCACHE_ERROR_PDU)) ? pduType2 - 19 : 0; if (pduIndex != (TS_PDUTYPE2_INPUT - 19)) { TRC_NRM((TB, "Shadow DataPDU type %s (%d)", scPktName[pduIndex], pduType2)); } } #endif /********************************************************************/ /* First check for synchronize packets */ /********************************************************************/ if (pduType2 != TS_PDUTYPE2_SYNCHRONIZE) { /****************************************************************/ /* Now check that this priority has been synchronized */ /****************************************************************/ localID = SC_NetworkIDToLocalID(netPersonID); if (!scPartyArray[localID].sync[priority]) { TRC_ALT((TB, "[%d] {%d} Discarding packet on unsynched priority %d", netPersonID, localID, priority)); DC_QUIT; } /****************************************************************/ /* All is well - pass the packet to its destination */ /****************************************************************/ switch (pduType2) { case TS_PDUTYPE2_INPUT: { // Note that changes to this path should be examined // in light of fast-path input (IM_DecodeFastPathInput). IM_PlaybackEvents((PTS_INPUT_PDU)pPkt, DataLength); } break; case TS_PDUTYPE2_SUPPRESS_OUTPUT: { /********************************************************/ /* A SuppressOutputPDU. Don't process it as it would */ /* disable output for the shadow target as well! */ /********************************************************/ TRC_ALT((TB, "Not forwarding TS_PDUTYPE2_SUPPRESS_OUTPUT")); bShadowData = FALSE; } break; case TS_PDUTYPE2_SHUTDOWN_REQUEST: { /********************************************************/ /* A ShutdownRequestPDU. Process locally only. It */ /* should apply to the shadow client and not the target.*/ /********************************************************/ // this does not actually use the pPkt member and we // have at lest TS_SHAREDATAHEADER left DCS_ReceivedShutdownRequestPDU((PTS_SHAREDATAHEADER)pPkt, DataLength, localID); TRC_ALT((TB, "Not forwarding TS_PDUTYPE2_SHUTDOWN_REQUEST")); bShadowData = FALSE; } break; case TS_PDUTYPE2_FONTLIST: { if (sizeof(TS_FONT_LIST_PDU) - sizeof(TS_FONT_ATTRIBUTE) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_FONT_LIST_PDU %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } PTS_FONT_LIST_PDU pFontListPDU = (PTS_FONT_LIST_PDU) pPkt; /********************************************************/ /* NT5 server doesn't do anything with a font packet so */ /* send the smallest possible packet accross the pipe. */ /********************************************************/ DataLength = sizeof(TS_FONT_LIST_PDU) - sizeof(TS_FONT_ATTRIBUTE); pFontListPDU->shareDataHeader.shareControlHeader.totalLength = (UINT16)DataLength; pFontListPDU->shareDataHeader.generalCompressedType = 0; pFontListPDU->shareDataHeader.generalCompressedLength = 0; pFontListPDU->numberFonts = 0; pFontListPDU->totalNumFonts = 0; pFontListPDU->entrySize = sizeof(TS_FONT_ATTRIBUTE); } break; // Forward all other PDUs until we learn otherwise! default: break; } } else { // TODO: Why are we processing this? TRC_ALT((TB, "Shadow Synchronize PDU")); SCSynchronizePDU(netPersonID, priority,(PTS_SYNCHRONIZE_PDU)pPkt); } } else { /********************************************************************/ /* Need to watch for confirm actives so we can determine how to */ /* terminate the shadow session. */ /********************************************************************/ if (sizeof(TS_FLOW_PDU) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_SHAREDATAHEADER %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } if (((PTS_FLOW_PDU)pPkt)->flowMarker != TS_FLOW_MARKER) { // here we already checked that we have enough buffer // for a TS_SHARECONTROLHEADER pduType = ((PTS_SHARECONTROLHEADER)pPkt)->pduType & TS_MASK_PDUTYPE; switch (pduType) { case TS_PDUTYPE_CONFIRMACTIVEPDU: TRC_ALT((TB, "Shadow Client ConfirmActivePDU - shadow active!")); m_pTSWd->bInShadowShare = TRUE; break; default: break; } } } } else if (m_pTSWd->StackClass == Stack_Passthru) { // If we see a demand active and no server certificate has been received // then wake up rdpwsx. // check that we have enough data before we deref the flow marker if (sizeof(TS_FLOW_PDU) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_FLOW_PDU %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } if (((PTS_FLOW_PDU)pPkt)->flowMarker != TS_FLOW_MARKER) { // Check that we have enough data before we deref the // TS_SHARECONTROLHEADER marker. // As of today we know exaclty that we have enough buffer // size sizeof(TS_FLOW_PDU) is grater then sizeof TS_SHARECONTROLHEADER. if (sizeof(TS_SHARECONTROLHEADER) > DataLength) { TRC_ERR((TB,"The PDU is not long enough to contain the TS_SHARECONTROLHEADER %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_ShadowDataTooShort, (PBYTE)pPkt, DataLength); DC_QUIT; } pduType = ((PTS_SHARECONTROLHEADER)pPkt)->pduType & TS_MASK_PDUTYPE; switch (pduType) { case TS_PDUTYPE_DEMANDACTIVEPDU: TRC_ALT((TB, "Passthru stack - demand active!")); SC_SaveServerCert(NULL, 0); m_pTSWd->bInShadowShare = TRUE; break; case TS_PDUTYPE_SERVERCERTIFICATEPDU: // check that we have enough data before we pass the // TS_SERVER_CERTIFICATE_PDU marker. if (sizeof(TS_SERVER_CERTIFICATE_PDU) > DataLength) { TRC_ERR((TB, "The PDU is not long enough to contain the TS_SERVER_CERTIFICATE_PDU %d", DataLength)); WDW_LogAndDisconnect(m_pTSWd, TRUE, Log_RDP_BadServerCertificateData, (PBYTE)pPkt, DataLength); DC_QUIT; } TRC_ALT((TB, "ServerCertificatePDU")); SC_SaveServerCert((PTS_SERVER_CERTIFICATE_PDU) pPkt, DataLength); bShadowData = FALSE; break; default: break; } } } // Forward PDU to shadow if it's OK. // Note IM_DecodeFastPathInput() performs this logic for fast-path input. if (bShadowData) { TRC_NRM((TB, "Forwarding shadow data: %ld", DataLength)); status = IcaRawInput(m_pTSWd->pContext, NULL, pPkt, DataLength); if (!NT_SUCCESS(status)) { TRC_ERR((TB, "Failed shadow input data [%ld]: %x", DataLength, status)); } } DC_EXIT_POINT: DC_END_FN(); } /* SC_OnShadowDataReceived */ /****************************************************************************/ /* Name: SC_AllocBuffer */ /* */ /* Purpose: Allocate a send buffer */ /* */ /* Returns: TRUE - buffer allocated OK */ /* FALSE - failed to allocate buffer */ /* */ /* Params: ppPkt - (returned) pointer to allocated packet */ /* pktLen - length of packet required */ /* priority - priority on which buffer will be used */ /****************************************************************************/ NTSTATUS __fastcall SHCLASS SC_AllocBuffer(PPVOID ppPkt, UINT32 pktLen) { NTSTATUS status; DC_BEGIN_FN("SC_AllocBuffer"); // fWait is TRUE means that we will always wait for a buffer to be avail status = SM_AllocBuffer(scPSMHandle, ppPkt, pktLen, TRUE, FALSE); DC_END_FN(); return(status); } /* SC_AllocBuffer */ /****************************************************************************/ /* Name: SC_FreeBuffer */ /* */ /* Purpose: Free an unused send buffer */ /* */ /* Params: pPkt - pointer to buffer to free */ /* pktLen - size of the packet */ /* priority - priority buffer was allocated on */ /****************************************************************************/ void __fastcall SHCLASS SC_FreeBuffer(PVOID pPkt) { SM_FreeBuffer(scPSMHandle, pPkt, FALSE); } /* SC_FreeBuffer */ /****************************************************************************/ /* Name: SC_SendData */ /* */ /* Purpose: Send a packet */ /* */ /* Returns: TRUE - packet sent OK */ /* FALSE - failed to send packet */ /* */ /* Params: pPkt - packet to send */ /* dataLen - length of packet */ /* pduLen - length of PDU : this will be used as the length */ /* of the packet if it is non-zero */ /* priority - priority (0 = all priorities) */ /* personID - person to send packet to (0 = all persons) */ /****************************************************************************/ BOOL RDPCALL ShareClass::SC_SendData( PTS_SHAREDATAHEADER pPkt, UINT32 dataLen, UINT32 pduLen, UINT32 priority, NETPERSONID personID) { BOOL rc; DC_BEGIN_FN("SC_SendData"); /************************************************************************/ /* Fill in the Share control and data header(s) if this is a single PDU */ /* */ /* Since we can send multiple PDUs per packet, the total length of data */ /* to send and the PDU length are not always the same. Where they are */ /* not, each PDU will have had its length (and compression) set up as */ /* it was assembled and we should not interfere here! */ /************************************************************************/ pPkt->shareControlHeader.pduType = TS_PDUTYPE_DATAPDU | TS_PROTOCOL_VERSION; pPkt->shareControlHeader.pduSource = (UINT16)scUserID; if (pduLen != 0) { pPkt->shareControlHeader.totalLength = (UINT16)pduLen; // Fill in the Share data header. pPkt->shareID = scShareID; pPkt->streamID = (BYTE)priority; pPkt->uncompressedLength = (UINT16)pduLen; pPkt->generalCompressedType = 0; pPkt->generalCompressedLength = 0; m_pTSWd->pProtocolStatus->Output.CompressedBytes += pduLen; } // Send with false for fast-path flag. rc = SM_SendData(scPSMHandle, (PVOID)pPkt, dataLen, TS_HIGHPRIORITY, 0, FALSE, RNS_SEC_ENCRYPT, FALSE); if (!rc) { TRC_ERR((TB, "Failed to send %d bytes", dataLen)); } DC_END_FN(); return(rc); } /* SC_SendData */ /****************************************************************************/ /* SC_GetMyNetworkPersonID */ /* */ /* Returns the network person ID for this machine. */ /****************************************************************************/ NETPERSONID RDPCALL SHCLASS SC_GetMyNetworkPersonID(void) { NETPERSONID rc = 0; DC_BEGIN_FN("SC_GetMyNetworkPersonID"); SC_CHECK_STATE(SCE_GETMYNETWORKPERSONID); rc = scPartyArray[0].netPersonID; DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ /* Name: SC_KeepAlive */ /* */ /* Purpose: KeepAlive packet send processing */ /* */ /* Returns: TRUE/FALSE */ /* */ /* Operation: Send a KeepAlive PDU if required */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_KeepAlive(void) { BOOL rc = FALSE; PTS_FLOW_PDU pFlowTestPDU; DC_BEGIN_FN("SC_KeepAlive"); TRC_NRM((TB, "Time for a KeepAlive PDU")); if (scState == SCS_IN_SHARE) { // only send the FlowTestPDU if we are in the share // fWait is FALSE means that we will not wait for a buffer to be available // If the buffer is full, that means there are packets waiting to be send out, // so there is no need to send out keep alive packet anyway. if ( STATUS_SUCCESS == SM_AllocBuffer(scPSMHandle, (PPVOID) (&pFlowTestPDU), sizeof(*pFlowTestPDU), FALSE, FALSE) ) { pFlowTestPDU->flowMarker = TS_FLOW_MARKER; pFlowTestPDU->pduType = TS_PDUTYPE_FLOWTESTPDU; pFlowTestPDU->flowIdentifier = 0; pFlowTestPDU->flowNumber = 0; pFlowTestPDU->pduSource = (TSUINT16) scUserID; if (SM_SendData(scPSMHandle, pFlowTestPDU, sizeof(*pFlowTestPDU), 0, 0, FALSE, RNS_SEC_ENCRYPT, FALSE)) { TRC_NRM((TB, "Sent a KeepAlive PDU to the client")); rc = TRUE; } else { TRC_ERR((TB, "Failed to send KeepAlive PDU")); } } else { TRC_ERR((TB, "Failed to alloc buffer for KeepAlive PDU")); } } else { TRC_ERR((TB, "In the wrong state: scState=%d, no KeepAlive PDU sent", scState)); } DC_END_FN(); return rc; } /* SC_KeepAlive */ /****************************************************************************/ /* Name: SC_RedrawScreen */ /* */ /* Purpose: Redraw the desktop upon request */ /****************************************************************************/ void RDPCALL SHCLASS SC_RedrawScreen(void) { NTSTATUS Status; ICA_CHANNEL_COMMAND Cmd; DC_BEGIN_FN("SC_RedrawScreen"); TRC_NRM((TB, "Call IcaChannelInput for screen redraw")); // redraw the whole desktop Cmd.Header.Command = ICA_COMMAND_REDRAW_RECTANGLE; Cmd.RedrawRectangle.Rect.Left = 0; Cmd.RedrawRectangle.Rect.Top = 0; Cmd.RedrawRectangle.Rect.Right = (short) m_desktopWidth; Cmd.RedrawRectangle.Rect.Bottom = (short) m_desktopHeight; /************************************************************/ // Pass the filled in structure to ICADD. /************************************************************/ Status = IcaChannelInput(m_pTSWd->pContext, Channel_Command, 0, NULL, (unsigned char *) &Cmd, sizeof(ICA_CHANNEL_COMMAND)); if (Status == STATUS_SUCCESS) { TRC_NRM((TB, "Issued IcaChannelInput for Screen Redraw")); } else { TRC_ERR((TB, "Error issuing an IcaChannelInput, status=%lu", Status)); } DC_END_FN(); } /****************************************************************************/ /* SC_LocalIDToNetworkID() */ /* */ /* Converts a local person ID to the corresponding network person ID. */ /* */ /* PARAMETERS: */ /* */ /* localPersonID - a local person ID. This must be a valid local person */ /* ID. */ /* */ /* RETURNS: a network person ID. */ /****************************************************************************/ NETPERSONID RDPCALL SHCLASS SC_LocalIDToNetworkID( LOCALPERSONID localPersonID) { DC_BEGIN_FN("SC_LocalIDToNetworkID"); SC_CHECK_STATE(SCE_LOCALIDTONETWORKID); /************************************************************************/ /* Validate the localPersonID. */ /************************************************************************/ TRC_ASSERT( (SC_IsLocalPersonID(localPersonID)), (TB,"Invalid {%d}", localPersonID) ); /************************************************************************/ /* Return this party's personID. */ /************************************************************************/ TRC_DBG((TB, "localID %u is network %hu", (unsigned)localPersonID, scPartyArray[localPersonID].netPersonID)); DC_EXIT_POINT: DC_END_FN(); return(scPartyArray[localPersonID].netPersonID); } /****************************************************************************/ /* SC_IsLocalPersonID() */ /* */ /* Validates a local person ID */ /* */ /* PARAMETERS */ /* */ /* localPersonID - the local person ID to validate */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_IsLocalPersonID(LOCALPERSONID localPersonID) { BOOL rc = FALSE; DC_BEGIN_FN("SC_IsLocalPersonID"); SC_CHECK_STATE(SCE_ISLOCALPERSONID); /************************************************************************/ /* Return TRUE if the localPersonID is valid, FALSE otherwise. */ /************************************************************************/ rc = ((localPersonID < SC_DEF_MAX_PARTIES) && (scPartyArray[localPersonID].netPersonID)) ? TRUE : FALSE; DC_EXIT_POINT: DC_END_FN(); return(rc); } /****************************************************************************/ /* SC_IsNetworkPersonID() */ /* */ /* Validates a network person ID */ /* */ /* PARAMETERS */ /* */ /* personID - the network person ID to validate */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_IsNetworkPersonID(NETPERSONID netPersonID) { LOCALPERSONID localPersonID; BOOL rc = FALSE; DC_BEGIN_FN("SC_IsNetworkPersonID"); SC_CHECK_STATE(SCE_ISNETWORKPERSONID); /************************************************************************/ /* Check for a zero personID. */ /************************************************************************/ if (netPersonID == 0) { DC_QUIT; } /************************************************************************/ /* Search for the personID. */ /************************************************************************/ for ( localPersonID = 0; localPersonID < SC_DEF_MAX_PARTIES; localPersonID++ ) { if (netPersonID == scPartyArray[localPersonID].netPersonID) { rc = TRUE; DC_QUIT; } } DC_EXIT_POINT: DC_END_FN(); return(rc); } /****************************************************************************/ /* SC_SetCapabilities() */ /* */ /* Sets the SC's capabilities at start of day. */ /* This function is required because the SC is initialized before the CPC, */ /* so cannot register its capabilities in SC_Init(). */ /****************************************************************************/ void RDPCALL SHCLASS SC_SetCapabilities(void) { TS_SHARE_CAPABILITYSET caps; DC_BEGIN_FN("SC_SetCapabilities"); /************************************************************************/ /* Register capabilities. */ /************************************************************************/ caps.capabilitySetType = TS_CAPSETTYPE_SHARE; caps.nodeID = (TSUINT16)scUserID; CPC_RegisterCapabilities((PTS_CAPABILITYHEADER)&caps, sizeof(TS_SHARE_CAPABILITYSET)); DC_END_FN(); } /****************************************************************************/ /* SC_SetCombinedCapabilities() */ /* */ /* Sets the share's combined capabilities to a predetermined set of values. */ /* This is used by shadow stacks so that the host's capabilities start with */ /* the value of the previous stack. */ /****************************************************************************/ void RDPCALL SHCLASS SC_SetCombinedCapabilities(UINT cbCapsSize, PTS_COMBINED_CAPABILITIES pCaps) { DC_BEGIN_FN("SC_SetCombinedCapabilities"); /************************************************************************/ /* Initialize capabilities. */ /************************************************************************/ CPC_SetCombinedCapabilities(cbCapsSize, pCaps); DC_END_FN(); } /****************************************************************************/ /* SC_GetCombinedCapabilities() */ /* */ /* Used during initiation of a shadow to gather the currently active set of */ /* combined capabilities for the shadow client. These will be passed to */ /* the shadow target for negotiation. */ /****************************************************************************/ void RDPCALL SHCLASS SC_GetCombinedCapabilities(LOCALPERSONID localID, PUINT pcbCapsSize, PTS_COMBINED_CAPABILITIES *ppCaps) { DC_BEGIN_FN("SC_GetCombinedCapabilities"); /************************************************************************/ /* Initialize capabilities. */ /************************************************************************/ CPC_GetCombinedCapabilities(localID, pcbCapsSize, ppCaps); DC_END_FN(); } /****************************************************************************/ /* Name: SC_AddPartyToShare */ /* */ /* Purpose: Add another party to the share such that we get a new set of */ /* negotiated capabilities. This function is used when a new */ /* shadow connects. */ /* */ /* Returns: none */ /* */ /* Params: netPersonID - ID of sender of capabilities */ /* pCaps - new capabilities for person */ /* capsLength - length of capability sets */ /* */ /* Operation: see purpose */ /****************************************************************************/ NTSTATUS RDPCALL SHCLASS SC_AddPartyToShare( NETPERSONID netPersonID, PTS_COMBINED_CAPABILITIES pCaps, unsigned capsLength) { LOCALPERSONID localPersonID; BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; NTSTATUS status = STATUS_SUCCESS; PTS_GENERAL_CAPABILITYSET pGenCapSet; unsigned MPPCCompressionLevel; DC_BEGIN_FN("SC_AddPartyToShare"); /************************************************************************/ /* 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)); status = STATUS_DEVICE_BUSY; DC_QUIT; } /************************************************************************/ /* 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; memcpy(scPartyArray[localPersonID].name, L"Shadow", sizeof(L"Shadow")); 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, capsLength, (PVOID) pCaps, acceptedArray, scNumberInShare)) { /********************************************************************/ /* Some component rejected the remote party. */ /********************************************************************/ TRC_ERR((TB, "Remote party rejected")); SCCallPartyLeftShare(localPersonID, acceptedArray, scNumberInShare ); scPartyArray[localPersonID].netPersonID = 0; status = STATUS_REVISION_MISMATCH; DC_QUIT; } // For shadow connections, we must force fast-path output off to prevent // any fast-path encoding from going across the cross-server pipe. // This is to maintain backward compatibility with TS5 beta 3. // Checking for m_pTSWd->shadowState in SCPartyJoiningShare() is not // sufficient since SHADOW_TARGET is likely not to have been set yet. // Note we have to update *all* precalculated header sizes, in SC and // UP. TRC_ALT((TB,"Forcing fast-path output off in shadow")); scUseFastPathOutput = FALSE; scUpdatePDUHeaderSpace = sizeof(TS_SHAREDATAHEADER); UP_UpdateHeaderSize(); // Update the compression level. // assume no compression scUseShadowCompression = FALSE; if (pCaps != NULL) { pGenCapSet = (PTS_GENERAL_CAPABILITYSET) WDW_GetCapSet( m_pTSWd, TS_CAPSETTYPE_GENERAL, pCaps, capsLength); if (pGenCapSet != NULL) { // update the compression capability if (m_pTSWd->bCompress && (pGenCapSet->extraFlags & TS_SHADOW_COMPRESSION_LEVEL) && (m_pTSWd->pMPPCContext->ClientComprType == pGenCapSet->generalCompressionLevel)) { MPPCCompressionLevel = m_pTSWd->pMPPCContext->ClientComprType; scUseShadowCompression = TRUE; } } } if (scUseShadowCompression) { // the compression history will be flushed m_pTSWd->bFlushed = PACKET_FLUSHED; // the compression will restart over initsendcontext(m_pTSWd->pMPPCContext, MPPCCompressionLevel); } /************************************************************************/ /* The remote party is now in the share. */ /************************************************************************/ scNumberInShare++; TRC_ALT((TB, "Number in share %d", (unsigned)scNumberInShare)); DC_EXIT_POINT: DC_END_FN(); return status; } /* SC_AddPartyToShare */ /****************************************************************************/ /* Name: SC_RemovePartyFromShare */ /* */ /* Purpose: Remove a party from the share such that we get a new set of */ /* negotiated capabilities. This function is used when a shadow */ /* disconnects from the share. */ /* */ /* Params: localID - ID of person to remove. */ /****************************************************************************/ NTSTATUS RDPCALL SHCLASS SC_RemovePartyFromShare(NETPERSONID netPersonID) { BOOL acceptedArray[SC_NUM_PARTY_JOINING_FCTS]; NTSTATUS status = STATUS_SUCCESS; UINT i; DC_BEGIN_FN("SC_RemovePartyFromShare"); // Map network ID to the corresponding local ID for (i = SC_DEF_MAX_PARTIES - 1; i > 0; i--) { if (scPartyArray[i].netPersonID != netPersonID) continue; else break; } // Call PLS for remote person if (scPartyArray[i].netPersonID == netPersonID) { memset(acceptedArray, TRUE, sizeof(acceptedArray)); TRC_ALT((TB, "Party %d left Share", i)); scNumberInShare--; SCCallPartyLeftShare(i, acceptedArray, scNumberInShare); scPartyArray[i].netPersonID = 0; memset(&(scPartyArray[i]), 0, sizeof(*scPartyArray)); } else { status = STATUS_INVALID_PARAMETER; TRC_ERR((TB, "Unable to find netID: %ld. Party not removed!", netPersonID)); } scUseShadowCompression = FALSE; DC_END_FN(); return status; } /****************************************************************************/ /* SC_NetworkIDToLocalID() */ /* */ /* Converts a network person ID to the corresponding local person ID. */ /* */ /* PARAMETERS: */ /* personID - a network person ID. This must be a valid network person ID. */ /* */ /* RETURNS: a local person ID. */ /****************************************************************************/ LOCALPERSONID __fastcall SHCLASS SC_NetworkIDToLocalID( NETPERSONID netPersonID) { LOCALPERSONID localID; LOCALPERSONID rc = 0; DC_BEGIN_FN("SC_NetworkIDToLocalID"); /************************************************************************/ /* Fastpath if same ID passed in as last time. */ /************************************************************************/ if (netPersonID == scLastNetID) { TRC_DBG((TB, "Same Network ID - return same local ID")); DC_END_FN(); return(scLastLocID); } SC_CHECK_STATE(SCE_NETWORKIDTOLOCALID); TRC_ASSERT((netPersonID), (TB, "Zero personID")); /************************************************************************/ /* Search for the personID. */ /************************************************************************/ if (SC_ValidateNetworkID(netPersonID, &localID)) { rc = localID; DC_QUIT; } TRC_ABORT((TB, "Invalid [%u]", (unsigned)netPersonID)); DC_EXIT_POINT: scLastNetID = netPersonID; scLastLocID = rc; DC_END_FN(); return(rc); } /****************************************************************************/ /* SC_ValidateNetworkID() */ /* */ /* Checks that a network ID is valid and returns the local ID corresponding */ /* to it if it is. */ /* */ /* PARAMETERS: */ /* netPersonID - a network person ID. */ /* pLocalPersonID - (returned) corresponding local ID if network ID valid */ /* (can pass NULL if you do not want local ID) */ /* */ /* RETURNS: */ /* TRUE - Network ID is valid FALSE - Network ID is not valid */ /****************************************************************************/ BOOL RDPCALL SHCLASS SC_ValidateNetworkID(NETPERSONID netPersonID, LOCALPERSONID * pLocalID) { BOOL rc = FALSE; LOCALPERSONID localID; DC_BEGIN_FN("SC_ValidateNetworkID"); /************************************************************************/ /* Search for the personID. */ /************************************************************************/ for (localID = 0; localID < SC_DEF_MAX_PARTIES; localID++) { if (netPersonID == scPartyArray[localID].netPersonID) { /****************************************************************/ /* Found required person, set return values and quit */ /****************************************************************/ rc = TRUE; if (pLocalID) { *pLocalID = localID; } break; } } TRC_DBG((TB, "Network ID 0x%04u rc = 0x%04x (localID=%u)", netPersonID, rc, localID)); DC_END_FN(); return(rc); } /****************************************************************************/ // SC_FlushAndAllocPackage // // Combines a forced network flush of the current package contents with // an allocation of the standard package buffer size. // Returns FALSE on allocation failure. /****************************************************************************/ NTSTATUS __fastcall ShareClass::SC_FlushAndAllocPackage(PPDU_PACKAGE_INFO pPkgInfo) { NTSTATUS status = STATUS_SUCCESS; DC_BEGIN_FN("SC_FlushAndAllocPackage"); if (pPkgInfo->cbLen) { if (pPkgInfo->cbInUse) { // Send the package contents. if (scUseFastPathOutput) // Send with fast-path flag. SM_SendData(scPSMHandle, (PVOID)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, TS_HIGHPRIORITY, 0, TRUE, RNS_SEC_ENCRYPT, FALSE); else SC_SendData((PTS_SHAREDATAHEADER)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, 0, PROT_PRIO_MISC, 0); } else { // or free the buffer if it's been allocated but not used. TRC_NRM((TB, "Freeing unused package")); SC_FreeBuffer(pPkgInfo->pOutBuf); } } // We always allocate 8K (unless the bytes needed are greater) to reduce // the number of buffers we allocate in the OutBuf pool. It is up to // the package users to pack to wire packet sizes within this // block. status = SC_AllocBuffer(&(pPkgInfo->pOutBuf), sc8KOutBufUsableSpace); if ( STATUS_SUCCESS == status ) { // If compression is not enabled, then output directly into the // OutBuf, else output into a temporary buffer. if (!m_pTSWd->bCompress) pPkgInfo->pBuffer = (BYTE *)pPkgInfo->pOutBuf; else pPkgInfo->pBuffer = m_pTSWd->pCompressBuffer; pPkgInfo->cbLen = sc8KOutBufUsableSpace; pPkgInfo->cbInUse = 0; } else { pPkgInfo->cbLen = 0; pPkgInfo->cbInUse = 0; pPkgInfo->pBuffer = NULL; TRC_NRM((TB, "could not allocate package buffer")); } DC_END_FN(); return status; } /****************************************************************************/ /* SC_GetSpaceInPackage */ /* */ /* Purpose: Ensure there's enough space in a PDU package for the data */ /* sending the existing package if necessary */ /* */ /* Returns: pointer to the space, or NULL if none available */ /* FALSE - no space available (alloc failed) */ /* */ /* Params: pPkgInfo - pointer to package info */ /* cbNeeded - space needed in package */ /****************************************************************************/ PBYTE __fastcall SHCLASS SC_GetSpaceInPackage( PPDU_PACKAGE_INFO pPkgInfo, unsigned cbNeeded) { PBYTE pSpace; unsigned cbPackageSize; NTSTATUS status = STATUS_SUCCESS; unsigned RealAllocSize; DC_BEGIN_FN("SC_GetSpaceInPackage"); // Handle the most common case where we have an allocated buffer and // enough space. if (pPkgInfo->cbLen) { if (cbNeeded <= (pPkgInfo->cbLen - pPkgInfo->cbInUse)) goto EnoughSpace; // We have a buffer allocated, but from the fast-path check above // we know we don't have enough space. Send what we have. if (pPkgInfo->cbInUse != 0) { TRC_NRM((TB, "Not enough space - sending current package " "of %u bytes", pPkgInfo->cbInUse)); if (scUseFastPathOutput) // Send with fast-path flag. SM_SendData(scPSMHandle, (PVOID)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, TS_HIGHPRIORITY, 0, TRUE, RNS_SEC_ENCRYPT, FALSE); else SC_SendData((PTS_SHAREDATAHEADER)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, 0, PROT_PRIO_MISC, 0); } else { // or free the buffer if it's been allocated but not used. TRC_NRM((TB, "Freeing unused package")); SC_FreeBuffer(pPkgInfo->pOutBuf); } } // We always allocate 8K (unless the bytes needed are greater) to reduce // the number of buffers we allocate in the OutBuf pool. It is up to // the package users to pack to wire packet sizes within this // block. cbPackageSize = max(cbNeeded, sc8KOutBufUsableSpace); status = SC_AllocBuffer(&(pPkgInfo->pOutBuf), cbPackageSize); if ( STATUS_SUCCESS == status ) { // If compression is not enabled, then output directly into the // OutBuf, else output into a temporary buffer. if (!m_pTSWd->bCompress) pPkgInfo->pBuffer = (BYTE *)pPkgInfo->pOutBuf; else pPkgInfo->pBuffer = m_pTSWd->pCompressBuffer; pPkgInfo->cbLen = cbPackageSize; pPkgInfo->cbInUse = 0; } else { pPkgInfo->cbLen = 0; pPkgInfo->cbInUse = 0; pPkgInfo->pBuffer = NULL; TRC_NRM((TB, "could not allocate package buffer")); pSpace = NULL; DC_QUIT; } EnoughSpace: pSpace = pPkgInfo->pBuffer + pPkgInfo->cbInUse; DC_EXIT_POINT: DC_END_FN(); return pSpace; } /* SC_GetSpaceInPackage */ /****************************************************************************/ /* SC_AddToPackage */ /* */ /* Purpose: Add the bytes to the PDU package - fills in the per-pdu info */ /* */ /* Params: pPkgInfo - pointer to package info */ /* cbLen - Length of data to add */ /* bShadow - whether or not the data should be shadowed */ /****************************************************************************/ void RDPCALL SHCLASS SC_AddToPackage( PPDU_PACKAGE_INFO pPkgInfo, unsigned cbLen, BOOL bShadow) { BYTE *pPktHdr; UCHAR compressResult; ULONG CompressedSize; DC_BEGIN_FN("SC_AddToPackage"); pPktHdr = pPkgInfo->pBuffer + pPkgInfo->cbInUse; // CompressedSize is the size of the data minus headers. CompressedSize = cbLen - scUpdatePDUHeaderSpace; compressResult = 0; if (m_pTSWd->bCompress) { UCHAR *pSrcBuf = pPktHdr + scUpdatePDUHeaderSpace; // Compress or copy the data into the OutBuf. if ((cbLen > WD_MIN_COMPRESS_INPUT_BUF) && (cbLen < MAX_COMPRESS_INPUT_BUF) && ((m_pTSWd->shadowState == SHADOW_NONE) || scUseShadowCompression)) { // Copy the header over to the OutBuf memcpy((BYTE *)pPkgInfo->pOutBuf + pPkgInfo->cbInUse, pPktHdr, scUpdatePDUHeaderSpace); pPktHdr = (BYTE *)pPkgInfo->pOutBuf + pPkgInfo->cbInUse; // Attempt to compress the PDU body directly into the OutBuf compressResult = compress(pSrcBuf, pPktHdr + scUpdatePDUHeaderSpace, &CompressedSize, m_pTSWd->pMPPCContext); if (compressResult & PACKET_COMPRESSED) { unsigned CompEst; // Successful compression - update the compression ratio. TRC_ASSERT(((cbLen - scUpdatePDUHeaderSpace) >= CompressedSize), (TB,"Compression created larger size than uncompr")); scMPPCUncompTotal += cbLen - scUpdatePDUHeaderSpace; scMPPCCompTotal += CompressedSize; if (scMPPCUncompTotal >= SC_SAMPLE_SIZE) { // Compression estimate is average # of bytes that // SCH_UNCOMP_BYTES bytes of uncomp data compress to. CompEst = SCH_UNCOMP_BYTES * scMPPCCompTotal / scMPPCUncompTotal; TRC_ASSERT((CompEst <= SCH_UNCOMP_BYTES), (TB,"MPPC compression estimate above 1.0 (%u)", CompEst)); scMPPCCompTotal = 0; scMPPCUncompTotal = 0; if (CompEst < SCH_COMP_LIMIT) CompEst = SCH_COMP_LIMIT; m_pShm->sch.MPPCCompressionEst = CompEst; TRC_NRM((TB, "New MPPC compr estimate %u", CompEst)); } compressResult |= m_pTSWd->bFlushed; m_pTSWd->bFlushed = 0; } else if (compressResult & PACKET_FLUSHED) { // Overran compression history, copy over the original // uncompressed buffer. m_pTSWd->bFlushed = PACKET_FLUSHED; memcpy(pPktHdr + scUpdatePDUHeaderSpace, pSrcBuf, cbLen - scUpdatePDUHeaderSpace); m_pTSWd->pProtocolStatus->Output.CompressFlushes++; } else { TRC_ALT((TB, "Compression FAILURE")); } } else { // This packet is too small or too big, copy over the header and // uncompressed data. memcpy((UCHAR *)pPkgInfo->pOutBuf + pPkgInfo->cbInUse, pPktHdr, cbLen); pPktHdr = (UCHAR *)pPkgInfo->pOutBuf + pPkgInfo->cbInUse; } } // Fill in the header based on whether we're using fast-path. if (scUseFastPathOutput) { if (m_pTSWd->bCompress) { // Set up compression flags if we're compressing, whether // or not the compression succeeded above. pPktHdr[1] = compressResult; // Size is the size of the payload after this header. *((PUINT16_UA)(pPktHdr + 2)) = (UINT16)CompressedSize; } else { // Size is the size of the payload after this header. *((PUINT16_UA)(pPktHdr + 1)) = (UINT16)CompressedSize; } } else { TS_SHAREDATAHEADER UNALIGNED *pHdr; pHdr = (TS_SHAREDATAHEADER UNALIGNED *)pPktHdr; // Fill in the Share control header. pHdr->shareControlHeader.totalLength = (TSUINT16) (CompressedSize + scUpdatePDUHeaderSpace); pHdr->shareControlHeader.pduType = TS_PDUTYPE_DATAPDU | TS_PROTOCOL_VERSION; pHdr->shareControlHeader.pduSource = (TSUINT16)scUserID; // Fill in the Share data header. pHdr->shareID = scShareID; pHdr->streamID = PROT_PRIO_MISC; pHdr->uncompressedLength = (UINT16)cbLen; pHdr->generalCompressedType = (compressResult | m_pTSWd->bFlushed); pHdr->generalCompressedLength = (TSUINT16)(m_pTSWd->bCompress ? CompressedSize + scUpdatePDUHeaderSpace : 0); } // Advance the usage size past the header and compressed or // uncompressed data. pPkgInfo->cbInUse += CompressedSize + scUpdatePDUHeaderSpace; TRC_ASSERT((pPkgInfo->cbInUse <= pPkgInfo->cbLen), (TB,"Overflowed package!")); m_pTSWd->pProtocolStatus->Output.CompressedBytes += CompressedSize + scUpdatePDUHeaderSpace; #ifdef DC_HICOLOR // If shadowing, we need to save this data so that the shadow stack can // duplicate it. if ((m_pTSWd->shadowState == SHADOW_TARGET) && bShadow) { if (m_pTSWd->pShadowInfo) { ULONG dataSize = CompressedSize + scUpdatePDUHeaderSpace + sizeof(SHADOW_INFO) - 1; // If we've not started on the extra space, see if this will fit // in the main space if ((m_pTSWd->pShadowInfo->messageSizeEx == 0) && (m_pTSWd->pShadowInfo->messageSize + dataSize) <= WD_MAX_SHADOW_BUFFER) { memcpy(&m_pTSWd->pShadowInfo->data[m_pTSWd->pShadowInfo->messageSize], pPktHdr, CompressedSize + scUpdatePDUHeaderSpace); TRC_NRM((TB, "Saving shadow data buffer[%ld] += %ld", m_pTSWd->pShadowInfo->messageSize, CompressedSize + scUpdatePDUHeaderSpace)); m_pTSWd->pShadowInfo->messageSize += CompressedSize + scUpdatePDUHeaderSpace; } // Nope - will it fit in the extra buffer? else if ((m_pTSWd->pShadowInfo->messageSizeEx + dataSize) <= WD_MAX_SHADOW_BUFFER) { TRC_ALT((TB, "Using extra shadow space...")); memcpy(&m_pTSWd->pShadowInfo->data[WD_MAX_SHADOW_BUFFER + m_pTSWd->pShadowInfo->messageSizeEx], pPktHdr, CompressedSize + scUpdatePDUHeaderSpace); TRC_NRM((TB, "Saving shadow data bufferEx[%ld] += %ld", m_pTSWd->pShadowInfo->messageSizeEx, CompressedSize + scUpdatePDUHeaderSpace)); m_pTSWd->pShadowInfo->messageSizeEx += CompressedSize + scUpdatePDUHeaderSpace; } else { TRC_ERR((TB, "Shadow buffer too small: %p[%ld/%ld] + %ld = %ld/%ld", m_pTSWd->pShadowInfo->data, m_pTSWd->pShadowInfo->messageSizeEx, m_pTSWd->pShadowInfo->messageSize, CompressedSize + scUpdatePDUHeaderSpace, m_pTSWd->pShadowInfo->messageSize + cbLen, m_pTSWd->pShadowInfo->messageSizeEx + cbLen)); } } } #else // If shadowing, we need to save this data so that the shadow stack can // duplicate it. if ((m_pTSWd->shadowState == SHADOW_TARGET) && bShadow) { if (m_pTSWd->pShadowInfo && ((m_pTSWd->pShadowInfo->messageSize + cbLen + sizeof(SHADOW_INFO) - 1) <= WD_MAX_SHADOW_BUFFER)) { memcpy(&m_pTSWd->pShadowInfo->data[m_pTSWd->pShadowInfo->messageSize], pPktHdr, CompressedSize + scUpdatePDUHeaderSpace); m_pTSWd->pShadowInfo->messageSize += CompressedSize + scUpdatePDUHeaderSpace; TRC_NRM((TB, "Saving shadow data buffer[%ld] += %ld", m_pTSWd->pShadowInfo->messageSize - CompressedSize - scUpdatePDUHeaderSpace, CompressedSize + scUpdatePDUHeaderSpace)); } else { TRC_ERR((TB, "Shadow buffer too small: %p[%ld] + %ld = %ld", m_pTSWd->pShadowInfo->data, m_pTSWd->pShadowInfo->messageSize, CompressedSize + scUpdatePDUHeaderSpace, m_pTSWd->pShadowInfo->messageSize + cbLen)); } } #endif DC_END_FN(); } /* SC_AddToPackage */ /****************************************************************************/ /* SC_FlushPackage */ /* */ /* Purpose: Send any remaining data, or free the buffer if allocated */ /* */ /* Params: pPkgInfo - pointer to package info */ /****************************************************************************/ void RDPCALL SHCLASS SC_FlushPackage(PPDU_PACKAGE_INFO pPkgInfo) { DC_BEGIN_FN("SC_FlushPackage"); /************************************************************************/ /* If there's anything there, send it */ /************************************************************************/ if (pPkgInfo->cbInUse > 0) { // Send the package contents. if (scUseFastPathOutput) // Send directly to SM with fast-path flag. SM_SendData(scPSMHandle, (PVOID)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, TS_HIGHPRIORITY, 0, TRUE, RNS_SEC_ENCRYPT, FALSE); else SC_SendData((PTS_SHAREDATAHEADER)pPkgInfo->pOutBuf, pPkgInfo->cbInUse, 0, PROT_PRIO_MISC, 0); } /************************************************************************/ /* If there's nothing in use but a buffer has been allocated, then free */ /* it */ /************************************************************************/ else if ((pPkgInfo->cbLen != 0) && (pPkgInfo->pBuffer != NULL)) SC_FreeBuffer(pPkgInfo->pOutBuf); // Reset the package info. pPkgInfo->cbLen = 0; pPkgInfo->cbInUse = 0; pPkgInfo->pBuffer = NULL; pPkgInfo->pOutBuf = NULL; DC_END_FN(); } /****************************************************************************/ /* Name: SC_UpdateShm */ /* */ /* Purpose: Update the Shm data with sc data */ /****************************************************************************/ void RDPCALL SHCLASS SC_UpdateShm(void) { DC_BEGIN_FN("SC_UpdateShm"); m_pShm->bc.noBitmapCompressionHdr = scNoBitmapCompressionHdr; DC_END_FN(); } // // Accessor for scUseAutoReconnect // returns TRUE if we can autoreconnect // BOOL RDPCALL SHCLASS SC_IsAutoReconnectEnabled() { DC_BEGIN_FN("SC_IsAutoReconnectEnabled"); DC_END_FN(); return scUseAutoReconnect; }