You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2996 lines
134 KiB
2996 lines
134 KiB
/****************************************************************************/
|
|
// ascapi.c
|
|
//
|
|
// Share Controller API Functions.
|
|
//
|
|
// Copyright (C) Microsoft Corp., PictureTel 1992-1997
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define TRC_FILE "ascapi"
|
|
#include <as_conf.hpp>
|
|
|
|
extern "C"
|
|
{
|
|
#include <acomapi.h>
|
|
#include <asmapi.h>
|
|
#include <asmint.h>
|
|
}
|
|
|
|
#include <string.h>
|
|
|
|
|
|
/****************************************************************************/
|
|
// 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 <ascdata.c>
|
|
#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;
|
|
}
|