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.
1096 lines
45 KiB
1096 lines
45 KiB
/****************************************************************************/
|
|
// adcsapi.cpp
|
|
//
|
|
// RDP top-level component API functions.
|
|
//
|
|
// Copyright (C) Microsoft Corp., PictureTel 1992-1997
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define TRC_FILE "adcsapi"
|
|
#include <randlib.h>
|
|
#include <as_conf.hpp>
|
|
#include <asmapi.h>
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: DCS_Init */
|
|
/* */
|
|
/* Initializes DCS. */
|
|
/* */
|
|
/* PARAMETERS: */
|
|
/* pTSWd - TShare WD handle */
|
|
/* pSMHandle - SM handle */
|
|
/* */
|
|
/* RETURNS: */
|
|
/* TRUE if DCS successfully initialized, FALSE otherwise. */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL SHCLASS DCS_Init(PTSHARE_WD pTSWd, PVOID pSMHandle)
|
|
{
|
|
BOOL rc = FALSE;
|
|
unsigned retCode;
|
|
TS_GENERAL_CAPABILITYSET GeneralCaps;
|
|
long arcUpdateIntervalSeconds;
|
|
UINT32 drawGdipSupportLevel;
|
|
NTSTATUS Status;
|
|
|
|
DC_BEGIN_FN("DCS_Init");
|
|
|
|
TRC_ALT((TB, "Initializing Core!"));
|
|
|
|
#define DC_INIT_DATA
|
|
#include <adcsdata.c>
|
|
#undef DC_INIT_DATA
|
|
|
|
dcsInitialized = TRUE;
|
|
|
|
// Read the registry key for the draw gdiplus support control
|
|
COM_OpenRegistry(pTSWd, WINSTATION_INI_SECTION_NAME);
|
|
Status = COMReadEntry(pTSWd, L"DrawGdiplusSupportLevel", (PVOID)&drawGdipSupportLevel, sizeof(INT32),
|
|
REG_DWORD);
|
|
if (Status != STATUS_SUCCESS) {
|
|
/********************************************************************/
|
|
/* We failed to read the value - copy in the default */
|
|
/********************************************************************/
|
|
TRC_ERR((TB, "Failed to read int32. Using default."));
|
|
drawGdipSupportLevel = TS_DRAW_GDIPLUS_DEFAULT;
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Read from registry, gdipSupportLevel is %d", drawGdipSupportLevel));
|
|
}
|
|
COM_CloseRegistry(pTSWd);
|
|
|
|
/************************************************************************/
|
|
/* Open registry here. Apart from SM_Init, all registry reads should */
|
|
/* be done from this function or its children. We close the key at the */
|
|
/* end of this function. */
|
|
/************************************************************************/
|
|
COM_OpenRegistry(pTSWd, DCS_INI_SECTION_NAME);
|
|
|
|
/************************************************************************/
|
|
/* Initialize individual modules. */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, "Initializing components..."));
|
|
|
|
// we can't do any registry reading on this csrss thread,
|
|
// it'll cause deadlock in the system
|
|
arcUpdateIntervalSeconds = DCS_ARC_UPDATE_INTERVAL_DFLT;
|
|
|
|
//
|
|
// Units specified are in seconds, convert to 100ns count
|
|
//
|
|
dcsMinArcUpdateInterval.QuadPart =
|
|
(LONGLONG)DCS_TIME_ONE_SECOND * (ULONGLONG)arcUpdateIntervalSeconds;
|
|
TRC_NRM((TB,"Set ARC update interval to %d seconds",
|
|
arcUpdateIntervalSeconds));
|
|
|
|
/************************************************************************/
|
|
// Share Controller - must go first
|
|
/************************************************************************/
|
|
if (SC_Init(pSMHandle)) {
|
|
// Capabilities Coordinator. Must be before all components which
|
|
// register capabilities structures.
|
|
CPC_Init();
|
|
|
|
// Register the SC's capabilities. This is required because SC is
|
|
// initialized before CPC, so cannot register its capabilities in
|
|
// SC_Init().
|
|
SC_SetCapabilities();
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "SC Initialization failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Register the general capabilities structure. */
|
|
/************************************************************************/
|
|
GeneralCaps.capabilitySetType = TS_CAPSETTYPE_GENERAL;
|
|
GeneralCaps.osMajorType = TS_OSMAJORTYPE_WINDOWS;
|
|
GeneralCaps.osMinorType = TS_OSMINORTYPE_WINDOWS_NT;
|
|
GeneralCaps.protocolVersion = TS_CAPS_PROTOCOLVERSION;
|
|
|
|
/************************************************************************/
|
|
/* Mark the old DOS 6 compression field as unsupported. */
|
|
/************************************************************************/
|
|
GeneralCaps.pad2octetsA = 0;
|
|
GeneralCaps.generalCompressionTypes = 0;
|
|
|
|
// Server supports no BC header and fast-path output, returning long credentials
|
|
// and sending the autoreconnect cookie
|
|
// Also supports receiving safer encrypted data from the client (better salted
|
|
// checksum)
|
|
//
|
|
GeneralCaps.extraFlags = TS_EXTRA_NO_BITMAP_COMPRESSION_HDR |
|
|
TS_FASTPATH_OUTPUT_SUPPORTED | TS_LONG_CREDENTIALS_SUPPORTED |
|
|
TS_AUTORECONNECT_COOKIE_SUPPORTED |
|
|
TS_ENC_SECURE_CHECKSUM;
|
|
|
|
/************************************************************************/
|
|
/* We don't support remote machines changing their capabilities during */
|
|
/* a call */
|
|
/************************************************************************/
|
|
GeneralCaps.updateCapabilityFlag = TS_CAPSFLAG_UNSUPPORTED;
|
|
|
|
/************************************************************************/
|
|
/* We don't support unshare requests from remote parties */
|
|
/************************************************************************/
|
|
GeneralCaps.remoteUnshareFlag = TS_CAPSFLAG_UNSUPPORTED;
|
|
|
|
/************************************************************************/
|
|
/* Now do the extension caps - these don't fit in the level 1 caps. */
|
|
/************************************************************************/
|
|
GeneralCaps.generalCompressionLevel = 0;
|
|
|
|
/************************************************************************/
|
|
/* We can receive a TS_REFRESH_RECT_PDU */
|
|
/************************************************************************/
|
|
GeneralCaps.refreshRectSupport = TS_CAPSFLAG_SUPPORTED;
|
|
|
|
/************************************************************************/
|
|
/* We can receive a TS_SUPPRESS_OUTPUT_PDU */
|
|
/************************************************************************/
|
|
GeneralCaps.suppressOutputSupport = TS_CAPSFLAG_SUPPORTED;
|
|
|
|
CPC_RegisterCapabilities((PTS_CAPABILITYHEADER)&GeneralCaps,
|
|
sizeof(TS_GENERAL_CAPABILITYSET));
|
|
|
|
//
|
|
// Register virtual channel capabilities
|
|
//
|
|
TS_VIRTUALCHANNEL_CAPABILITYSET VcCaps;
|
|
VcCaps.capabilitySetType = TS_CAPSETTYPE_VIRTUALCHANNEL;
|
|
VcCaps.lengthCapability = sizeof(TS_VIRTUALCHANNEL_CAPABILITYSET);
|
|
//Indicate support for 8K VC compression (from client->server)
|
|
//I.e server understands compressed channels
|
|
VcCaps.vccaps1 = TS_VCCAPS_COMPRESSION_8K;
|
|
CPC_RegisterCapabilities((PTS_CAPABILITYHEADER)&VcCaps,
|
|
sizeof(TS_VIRTUALCHANNEL_CAPABILITYSET));
|
|
|
|
TS_DRAW_GDIPLUS_CAPABILITYSET DrawGdipCaps;
|
|
DrawGdipCaps.capabilitySetType = TS_CAPSETTYPE_DRAWGDIPLUS;
|
|
DrawGdipCaps.lengthCapability = sizeof(TS_DRAW_GDIPLUS_CAPABILITYSET);
|
|
DrawGdipCaps.drawGdiplusSupportLevel = drawGdipSupportLevel;
|
|
DrawGdipCaps.drawGdiplusCacheLevel = TS_DRAW_GDIPLUS_CACHE_LEVEL_ONE;
|
|
CPC_RegisterCapabilities((PTS_CAPABILITYHEADER)&DrawGdipCaps,
|
|
sizeof(TS_DRAW_GDIPLUS_CAPABILITYSET));
|
|
|
|
USR_Init();
|
|
CA_Init();
|
|
PM_Init();
|
|
SBC_Init();
|
|
BC_Init();
|
|
CM_Init();
|
|
IM_Init();
|
|
SSI_Init();
|
|
SCH_Init();
|
|
|
|
TRC_NRM((TB, "** All successfully initialized **"));
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* Close the registry key that we opened earlier. */
|
|
/************************************************************************/
|
|
COM_CloseRegistry(pTSWd);
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: DCS_Term */
|
|
/* */
|
|
/* Terminates DCS. */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_Term()
|
|
{
|
|
DC_BEGIN_FN("DCS_Term");
|
|
|
|
if (!dcsInitialized) {
|
|
TRC_ERR((TB,"DCS_Term() called when we have not been initialized"));
|
|
}
|
|
|
|
SBC_Term();
|
|
PM_Term();
|
|
CA_Term();
|
|
BC_Term();
|
|
CM_Term();
|
|
IM_Term();
|
|
SSI_Term();
|
|
SCH_Term();
|
|
CPC_Term();
|
|
USR_Term();
|
|
SC_Term();
|
|
|
|
dcsInitialized = FALSE;
|
|
TRC_NRM((TB, "BYE!"));
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* API FUNCTION: DCS_TimeToDoStuff */
|
|
/* */
|
|
/* This function is called to send updates etc in the correct order. */
|
|
/* */
|
|
/* PARAMETERS: IN - pOutputIn - input from TShareDD */
|
|
/* OUT - pSchCurrentMode - current Scheduler mode */
|
|
/* */
|
|
/* RETURNS: Millisecs to set the timer for (-1 means infinite). */
|
|
/****************************************************************************/
|
|
/* Scheduling is the responsibility of the WDW, DD and SCH components. */
|
|
/* These ensure that DCS_TimeToDoStuff() gets called. The Scheduler is in */
|
|
/* one of three states: asleep, normal or turbo. When it is asleep, this */
|
|
/* function is not called. When it is in normal mode, this function is */
|
|
/* called at least once, but the scheduler is a lazy guy, so will fall */
|
|
/* asleep again unless you keep prodding him. In turbo mode this function */
|
|
/* is called repeatedly and rapidly, but only for a relatively short time, */
|
|
/* after which the scheduler falls back into normal mode, and from there */
|
|
/* falls asleep. */
|
|
/* */
|
|
/* Hence when a component realises it has some processing to do later, */
|
|
/* which is called from DCS_TimeToDoStuff(), it calls */
|
|
/* SCH_ContinueScheduling(SCH_MODE_NORMAL) which guarantees that this */
|
|
/* function will be called at least one more time. If the component wants */
|
|
/* DCS_TimeToDoStuff() to be called again, it must make another call to */
|
|
/* SCH_ContinueScheduling(), which prods the Scheduler again. */
|
|
/* */
|
|
/* The objective is to only keep the scheduler awake when it is really */
|
|
/* necessary. */
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL SHCLASS DCS_TimeToDoStuff(PTSHARE_DD_OUTPUT_IN pOutputIn,
|
|
PUINT32 pSchCurrentMode,
|
|
PINT32 pNextTimer)
|
|
{
|
|
INT32 timeToSet;
|
|
ULARGE_INTEGER sysTime;
|
|
UINT32 sysTimeLowPart;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
DC_BEGIN_FN("DCS_TimeToDoStuff");
|
|
|
|
#ifdef DC_DEBUG
|
|
/************************************************************************/
|
|
/* Update trace config in shared memory. */
|
|
/************************************************************************/
|
|
TRC_MaybeCopyConfig(m_pTSWd, &(m_pShm->trc));
|
|
#endif
|
|
|
|
// Determine if we should do anything based on:
|
|
// 1. SCH determining that this is the time to do something.
|
|
// 2. We are in a share.
|
|
if (SCH_ShouldWeDoStuff(pOutputIn->forceSend) && SC_InShare()) {
|
|
PDU_PACKAGE_INFO pkgInfo = {0};
|
|
|
|
// We check for the need to send a cursor-moved packet, by comparing
|
|
// the current mouse position to the last known position at the last
|
|
// mouse packet, only a few times a second, to reduce the traffic for
|
|
// shadow clients.
|
|
KeQuerySystemTime((PLARGE_INTEGER)&sysTime);
|
|
sysTimeLowPart = sysTime.LowPart;
|
|
if ((sysTimeLowPart - dcsLastMiscTime) > DCS_MISC_PERIOD) {
|
|
dcsLastMiscTime = sysTimeLowPart;
|
|
IM_CheckUpdateCursor(&pkgInfo, sysTimeLowPart);
|
|
}
|
|
|
|
//
|
|
// Check if the ARC cookie needs to be updated
|
|
//
|
|
if (scEnablePeriodicArcUpdate &&
|
|
scUseAutoReconnect &&
|
|
((sysTime.QuadPart - dcsLastArcUpdateTime.QuadPart) > dcsMinArcUpdateInterval.QuadPart)) {
|
|
|
|
dcsLastArcUpdateTime = sysTime;
|
|
DCS_UpdateAutoReconnectCookie();
|
|
|
|
}
|
|
|
|
// Try to send updates now.
|
|
TRC_DBG((TB, "Send updates"));
|
|
|
|
//
|
|
// *** Keep the code path but still return status code ***
|
|
//
|
|
status = UP_SendUpdates(pOutputIn->pFrameBuf, pOutputIn->frameBufWidth,
|
|
&pkgInfo);
|
|
|
|
// Call the cursor manager to decide if a new cursor needs to be
|
|
// sent to the remote system.
|
|
CM_Periodic(&pkgInfo);
|
|
|
|
// Flush any remaining data in the package.
|
|
SC_FlushPackage(&pkgInfo);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Check whether we have any pending callbacks. */
|
|
/************************************************************************/
|
|
if (dcsCallbackTimerPending)
|
|
DCS_UpdateShm();
|
|
|
|
/************************************************************************/
|
|
/* Find out the timer period to set */
|
|
/************************************************************************/
|
|
*pNextTimer = SCH_EndOfDoingStuff(pSchCurrentMode);
|
|
|
|
DC_END_FN();
|
|
return status;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* DCS_DiscardAllOutput */
|
|
/* */
|
|
/* This routine will discard accumulated orders, screen data, and any */
|
|
/* pending shadow data. It is currently only called when the WD is dead */
|
|
/* to prevent the DD from looping during shadow termination, or when a */
|
|
/* disconnect or terminate is occuring. */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_DiscardAllOutput()
|
|
{
|
|
RECTL sdaRect[BA_MAX_ACCUMULATED_RECTS];
|
|
unsigned cRects;
|
|
|
|
// Blow the order heap, screen data, and clear any shadow data.
|
|
OA_ResetOrderList();
|
|
BA_GetBounds(sdaRect, &cRects);
|
|
|
|
if (m_pTSWd->pShadowInfo != NULL)
|
|
{
|
|
m_pTSWd->pShadowInfo->messageSize = 0;
|
|
#ifdef DC_HICOLOR
|
|
m_pTSWd->pShadowInfo->messageSizeEx = 0;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_ReceivedShurdownRequestPDU */
|
|
/* */
|
|
/* Purpose: Handles ShutdownRequestPDU. */
|
|
/* */
|
|
/* Params: personID: originator of the PDU */
|
|
/* pPDU: the PDU */
|
|
/* */
|
|
/* Operation: See embedded comments for each PDU type */
|
|
/* */
|
|
/* Note that since a ShutdownRequestPDU is not really directed at a */
|
|
/* particular component, DCS_ReceivedPacket is intended as a generic */
|
|
/* received packet handler, and it therefore takes a generic 2nd parameter */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_ReceivedShutdownRequestPDU(
|
|
PTS_SHAREDATAHEADER pDataPDU,
|
|
unsigned DataLength,
|
|
NETPERSONID personID)
|
|
{
|
|
UINT32 packetSize;
|
|
PTS_SHUTDOWN_DENIED_PDU pResponsePDU;
|
|
|
|
DC_BEGIN_FN("DCS_ReceivedPacket");
|
|
|
|
if (dcsUserLoggedOn) {
|
|
TRC_NRM((TB, "User logged on - deny shutdown request"));
|
|
packetSize = sizeof(TS_SHUTDOWN_DENIED_PDU);
|
|
|
|
if (STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pResponsePDU, packetSize)) {
|
|
pResponsePDU->shareDataHeader.pduType2 =
|
|
TS_PDUTYPE2_SHUTDOWN_DENIED;
|
|
|
|
// The way in which this packet is handled in a multi-party
|
|
// call is not yet clear. We could send a directed or
|
|
// broadcast reponse to the client(s). I've chosen
|
|
// broadcast.
|
|
SC_SendData((PTS_SHAREDATAHEADER)pResponsePDU, packetSize,
|
|
packetSize, PROT_PRIO_MISC, 0);
|
|
}
|
|
else {
|
|
TRC_ALT((TB,"Failed to allocate packet for "
|
|
"TS_SHUTDOWN_DENIED_PDU"));
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "User not logged on - disconnect"));
|
|
WDW_Disconnect(m_pTSWd);
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* DCS_ReceivedPacket */
|
|
|
|
/****************************************************************************
|
|
DCS_UpdateAutoReconnectCookie
|
|
|
|
Updates the autoreconnection cookie and sends it to the client
|
|
*****************************************************************************/
|
|
BOOL RDPCALL SHCLASS DCS_UpdateAutoReconnectCookie()
|
|
{
|
|
BOOL fRet = FALSE;
|
|
ARC_SC_PRIVATE_PACKET arcSCPkt;
|
|
|
|
DC_BEGIN_FN("DCS_UpdateAutoReconnectCookie");
|
|
|
|
arcSCPkt.cbLen = sizeof(ARC_SC_PRIVATE_PACKET);
|
|
arcSCPkt.LogonId = m_pTSWd->arcReconnectSessionID;
|
|
arcSCPkt.Version = 1;
|
|
|
|
if (NewGenRandom(NULL,
|
|
NULL,
|
|
arcSCPkt.ArcRandomBits,
|
|
sizeof(arcSCPkt.ArcRandomBits))) {
|
|
|
|
#ifdef ARC_INSTRUMENT_RDPWD
|
|
LPDWORD pdwArcRandom = (LPDWORD)arcSCPkt.ArcRandomBits;
|
|
KdPrint(("ARC-RDPWD:Sending arc for SID:%d - random: 0x%x,0x%x,0x%x,0x%x\n",
|
|
arcSCPkt.LogonId,
|
|
pdwArcRandom[0],pdwArcRandom[1],pdwArcRandom[2],pdwArcRandom[3]));
|
|
#endif
|
|
|
|
//
|
|
// Try to send the updated packet and if it succeeds
|
|
//
|
|
if (DCS_SendAutoReconnectCookie(&arcSCPkt)) {
|
|
|
|
//
|
|
// Update the locally stored ARC cookie even though
|
|
// all we know is that we attempted to send it
|
|
// i.e. if the client link drops before it recvs the
|
|
// cookie then it won't be able to ARC.
|
|
//
|
|
|
|
memcpy(m_pTSWd->arcCookie, arcSCPkt.ArcRandomBits,
|
|
ARC_SC_SECURITY_TOKEN_LEN);
|
|
m_pTSWd->arcTokenValid = TRUE;
|
|
|
|
#ifdef ARC_INSTRUMENT_RDPWD
|
|
KdPrint(("ARC-RDPWD:ACTUALLY SENT ARC for SID:%d\n", arcSCPkt.LogonId));
|
|
#endif
|
|
|
|
fRet = TRUE;
|
|
}
|
|
else {
|
|
#ifdef ARC_INSTRUMENT_RDPWD
|
|
KdPrint(("ARC-RDPWD:Failed to send new ARC for SID:%d\n", arcSCPkt.LogonId));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return fRet;
|
|
}
|
|
|
|
BOOL RDPCALL SHCLASS DCS_FlushAutoReconnectCookie()
|
|
{
|
|
memset(m_pTSWd->arcCookie, 0, ARC_SC_SECURITY_TOKEN_LEN);
|
|
m_pTSWd->arcTokenValid = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
DCS_SendAutoReconnectCookie
|
|
|
|
Sends a autoreconnect cookie tot the client
|
|
*****************************************************************************/
|
|
BOOL RDPCALL SHCLASS DCS_SendAutoReconnectCookie(
|
|
PARC_SC_PRIVATE_PACKET pArcSCPkt)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
PTS_SAVE_SESSION_INFO_PDU pInfoPDU = NULL;
|
|
PTS_LOGON_INFO_EXTENDED pLogonInfoExPkt = NULL;
|
|
UINT32 cbLogonInfoExLen = sizeof(TS_SAVE_SESSION_INFO_PDU) +
|
|
sizeof(ARC_SC_PRIVATE_PACKET) +
|
|
sizeof(TSUINT32);
|
|
TSUINT32 cbAutoReconnectInfoLen = sizeof(ARC_SC_PRIVATE_PACKET);
|
|
TSUINT32 cbWrittenLen = 0;
|
|
|
|
DC_BEGIN_FN("DCS_SendAutoReconnectCookie");
|
|
|
|
//
|
|
//Send down the autoreconnect cookie
|
|
//
|
|
if (scUseAutoReconnect) {
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pInfoPDU,
|
|
cbLogonInfoExLen)) {
|
|
|
|
// Zero out the structure and set basic header info.
|
|
memset(pInfoPDU, 0, cbLogonInfoExLen);
|
|
pInfoPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SAVE_SESSION_INFO;
|
|
pInfoPDU->data.InfoType = TS_INFOTYPE_LOGON_EXTENDED_INFO;
|
|
|
|
// Now fill up the rest of the packet
|
|
pLogonInfoExPkt = &pInfoPDU->data.Info.LogonInfoEx;
|
|
pLogonInfoExPkt->Length = sizeof(TS_LOGON_INFO_EXTENDED) +
|
|
sizeof(ARC_SC_PRIVATE_PACKET) +
|
|
sizeof(TSUINT32);
|
|
|
|
//
|
|
// For now the only info we pass down is
|
|
// the autoreconnect cookie
|
|
//
|
|
pLogonInfoExPkt->Flags = LOGON_EX_AUTORECONNECTCOOKIE;
|
|
|
|
//Copy in the fields
|
|
//Autoreconnect field length
|
|
PBYTE pBuf = (PBYTE)(pLogonInfoExPkt+1);
|
|
memcpy(pBuf, &cbAutoReconnectInfoLen, sizeof(TSUINT32));
|
|
pBuf += sizeof(TSUINT32);
|
|
cbWrittenLen += sizeof(TSUINT32);
|
|
//Autoreconnect cookie
|
|
memcpy(pBuf, pArcSCPkt, cbAutoReconnectInfoLen);
|
|
pBuf += cbAutoReconnectInfoLen;
|
|
cbWrittenLen += cbAutoReconnectInfoLen;
|
|
|
|
TRC_ASSERT(cbWrittenLen + sizeof(TS_LOGON_INFO_EXTENDED) <=
|
|
pLogonInfoExPkt->Length,
|
|
(TB,"Wrote to much data to packet"));
|
|
|
|
fRet = SC_SendData((PTS_SHAREDATAHEADER)pInfoPDU,
|
|
cbLogonInfoExLen,
|
|
cbLogonInfoExLen,
|
|
PROT_PRIO_UPDATES, 0);
|
|
} else {
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"PTS_SAVE_SESSION_INFO_PDU"));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_UserLoggedOn */
|
|
/* */
|
|
/* Purpose: Notify that a user has logged on */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_UserLoggedOn(PLOGONINFO pLogonInfo)
|
|
{
|
|
PTS_SAVE_SESSION_INFO_PDU pInfoPDU;
|
|
|
|
DC_BEGIN_FN("DCS_UserLoggedOn");
|
|
|
|
// This can get called before we're initialised in the console remoting
|
|
// case. Since in that case the class data hasn't been initialized, we
|
|
// can't use dcsInitialized to check whether we're inited or not. We
|
|
//must use the non-class variable TSWd->shareClassInit.
|
|
if (m_pTSWd->shareClassInit) {
|
|
|
|
// Note that a user has successfully logged on
|
|
dcsUserLoggedOn = TRUE;
|
|
m_pTSWd->sessionId = pLogonInfo->SessionId;
|
|
|
|
// If a different domain/username has been selected for a non-
|
|
// autologon session, then send the new values back to the client to
|
|
// be cached for future use
|
|
if (!(m_pTSWd->pInfoPkt->flags & RNS_INFO_AUTOLOGON) &&
|
|
(m_pTSWd->pInfoPkt->flags & RNS_INFO_LOGONNOTIFY ||
|
|
(wcscmp((const PWCHAR)(pLogonInfo->Domain),
|
|
(const PWCHAR)(m_pTSWd->pInfoPkt->Domain)) ||
|
|
wcscmp((const PWCHAR)(pLogonInfo->UserName),
|
|
(const PWCHAR)(m_pTSWd->pInfoPkt->UserName))))) {
|
|
|
|
// Get a buffer.
|
|
// The buffer size and how it is going to be filled depends on the client's
|
|
// capability to accept Long Credentials on return. Pre-Whistler Clients dont
|
|
// support Long Credentials
|
|
|
|
if (scUseLongCredentials == FALSE) {
|
|
|
|
// Pre Whistler Client - has no capability for accepting long credentials
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pInfoPDU, sizeof(TS_SAVE_SESSION_INFO_PDU)) ) {
|
|
|
|
// Zero out the structure and set basic header info.
|
|
memset(pInfoPDU, 0, sizeof(TS_SAVE_SESSION_INFO_PDU));
|
|
pInfoPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SAVE_SESSION_INFO;
|
|
|
|
// Now fill up the rest of the packet
|
|
|
|
pInfoPDU->data.InfoType = TS_INFOTYPE_LOGON;
|
|
|
|
// Fill in the Domain info.
|
|
pInfoPDU->data.Info.LogonInfo.cbDomain =
|
|
(wcslen((const PWCHAR)(pLogonInfo->Domain)) + 1) *
|
|
sizeof(WCHAR);
|
|
|
|
memcpy(pInfoPDU->data.Info.LogonInfo.Domain,
|
|
pLogonInfo->Domain,
|
|
pInfoPDU->data.Info.LogonInfo.cbDomain);
|
|
|
|
// Fill in the UserName info.
|
|
// In case the fDontDisplayLastUserName is set we should
|
|
// not send back the username for caching.
|
|
pInfoPDU->data.Info.LogonInfo.cbUserName =
|
|
(m_pTSWd->fDontDisplayLastUserName) ? 0 :
|
|
(wcslen((const PWCHAR)(pLogonInfo->UserName)) + 1) *
|
|
sizeof(WCHAR);
|
|
|
|
memcpy(pInfoPDU->data.Info.LogonInfo.UserName,
|
|
pLogonInfo->UserName,
|
|
pInfoPDU->data.Info.LogonInfo.cbUserName);
|
|
|
|
// Fill in the Session Id info.
|
|
pInfoPDU->data.Info.LogonInfo.SessionId =
|
|
pLogonInfo->SessionId;
|
|
|
|
// Send it
|
|
SC_SendData((PTS_SHAREDATAHEADER)pInfoPDU,
|
|
sizeof(TS_SAVE_SESSION_INFO_PDU),
|
|
sizeof(TS_SAVE_SESSION_INFO_PDU),
|
|
PROT_PRIO_UPDATES, 0);
|
|
|
|
} else {
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"PTS_SAVE_SESSION_INFO_PDU"));
|
|
}
|
|
|
|
} else {
|
|
// Client CAN accept long Credentials
|
|
TSUINT32 DomainLen, UserNameLen, DataLen ;
|
|
|
|
DomainLen = (wcslen((const PWCHAR)(pLogonInfo->Domain)) + 1) * sizeof(WCHAR);
|
|
// In case fDontDisplayLastUserName is set we won't send the user name
|
|
UserNameLen = (m_pTSWd->fDontDisplayLastUserName) ? 0 :
|
|
(wcslen((const PWCHAR)(pLogonInfo->UserName)) + 1) * sizeof(WCHAR);
|
|
|
|
// Compute the length of the data u r sending
|
|
DataLen = sizeof(TS_SAVE_SESSION_INFO_PDU) + DomainLen + UserNameLen ;
|
|
|
|
TRC_DBG((TB, "DCS_UserLoggedOn : DomainLength allocated = %ul", DomainLen));
|
|
TRC_DBG((TB, "DCS_UserLoggedOn : UserNameLength allocated = %ul", UserNameLen));
|
|
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pInfoPDU, DataLen) ) {
|
|
|
|
// Zero out the structure and set basic header info.
|
|
memset(pInfoPDU, 0, DataLen);
|
|
pInfoPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SAVE_SESSION_INFO;
|
|
|
|
// Now fill up the rest of the packet
|
|
pInfoPDU->data.InfoType = TS_INFOTYPE_LOGON_LONG;
|
|
|
|
pInfoPDU->data.Info.LogonInfoVersionTwo.Version = SAVE_SESSION_PDU_VERSION_ONE ;
|
|
pInfoPDU->data.Info.LogonInfoVersionTwo.Size = sizeof(TS_LOGON_INFO_VERSION_2) ;
|
|
|
|
// Fill in the Session Id info.
|
|
pInfoPDU->data.Info.LogonInfoVersionTwo.SessionId =
|
|
pLogonInfo->SessionId;
|
|
|
|
// Fill in the Domain info.
|
|
pInfoPDU->data.Info.LogonInfoVersionTwo.cbDomain = DomainLen ;
|
|
|
|
// Fill in the UserName info.
|
|
pInfoPDU->data.Info.LogonInfoVersionTwo.cbUserName = UserNameLen ;
|
|
|
|
memcpy((PBYTE)(pInfoPDU + 1),
|
|
pLogonInfo->Domain,
|
|
DomainLen);
|
|
|
|
// Note that in case the fDontDisplayLastUserName is TRUE
|
|
// the UserNameLen is 0 so we won't copy anything.
|
|
memcpy((PBYTE)(pInfoPDU + 1) + DomainLen,
|
|
pLogonInfo->UserName,
|
|
UserNameLen);
|
|
|
|
// Send it
|
|
SC_SendData((PTS_SHAREDATAHEADER)pInfoPDU,
|
|
DataLen,
|
|
DataLen,
|
|
PROT_PRIO_UPDATES, 0);
|
|
|
|
|
|
} else {
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"PTS_SAVE_SESSION_INFO_PDU"));
|
|
}
|
|
|
|
} // Client can accept long credentials
|
|
|
|
}
|
|
else
|
|
{
|
|
//Send back a plain logon notification (without session update
|
|
//information)
|
|
//because the ActiveX control needs to expose an
|
|
//OnLoginComplete event. Older clients (e.g 2195)
|
|
//will just ignore this PDU
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pInfoPDU,
|
|
sizeof(TS_SAVE_SESSION_INFO_PDU)) ) {
|
|
|
|
// Zero out the structure and set basic header info.
|
|
memset(pInfoPDU, 0, sizeof(TS_SAVE_SESSION_INFO_PDU));
|
|
pInfoPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SAVE_SESSION_INFO;
|
|
|
|
// Now fill up the rest of the packet
|
|
|
|
pInfoPDU->data.InfoType = TS_INFOTYPE_LOGON_PLAINNOTIFY;
|
|
|
|
// Nothing else is needed
|
|
// Send it
|
|
//
|
|
SC_SendData((PTS_SHAREDATAHEADER)pInfoPDU,
|
|
sizeof(TS_SAVE_SESSION_INFO_PDU),
|
|
sizeof(TS_SAVE_SESSION_INFO_PDU),
|
|
PROT_PRIO_UPDATES, 0);
|
|
|
|
} else {
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"PTS_SAVE_SESSION_INFO_PDU"));
|
|
}
|
|
}
|
|
|
|
pInfoPDU = NULL;
|
|
if (pLogonInfo->Flags & LI_USE_AUTORECONNECT) {
|
|
|
|
if (scUseAutoReconnect) {
|
|
|
|
//
|
|
//Send down the autoreconnect cookie
|
|
//
|
|
m_pTSWd->arcReconnectSessionID = pLogonInfo->SessionId;
|
|
if (DCS_UpdateAutoReconnectCookie()) {
|
|
|
|
//
|
|
// Record the update time to prevent a double-update
|
|
// in DCS_TimeToDoStuff
|
|
//
|
|
KeQuerySystemTime((PLARGE_INTEGER)&dcsLastArcUpdateTime);
|
|
scEnablePeriodicArcUpdate = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
DCS_FlushAutoReconnectCookie();
|
|
scEnablePeriodicArcUpdate = FALSE;
|
|
}
|
|
|
|
}
|
|
} else {
|
|
TRC_ALT((TB, "Called before init"));
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* DCS_UserLoggedOn */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_WDWKeyboardSetIndicators */
|
|
/* */
|
|
/* Purpose: Notify that keyboard indicators have changed */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_WDWKeyboardSetIndicators(void)
|
|
{
|
|
PTS_SET_KEYBOARD_INDICATORS_PDU pKeyPDU;
|
|
|
|
DC_BEGIN_FN("DCS_WDWKeyboardSetIndicators");
|
|
|
|
if ((_RNS_MAJOR_VERSION(m_pTSWd->version) > 8) ||
|
|
(_RNS_MINOR_VERSION(m_pTSWd->version) >= 1))
|
|
{
|
|
// Get a buffer.
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pKeyPDU,
|
|
sizeof(TS_SET_KEYBOARD_INDICATORS_PDU)) )
|
|
{
|
|
// Zero out the structure and set basic header info.
|
|
memset(pKeyPDU, 0, sizeof(TS_SET_KEYBOARD_INDICATORS_PDU));
|
|
|
|
pKeyPDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SET_KEYBOARD_INDICATORS;
|
|
|
|
pKeyPDU->UnitId = m_pTSWd->KeyboardIndicators.UnitId;
|
|
pKeyPDU->LedFlags = m_pTSWd->KeyboardIndicators.LedFlags;
|
|
|
|
// Send it.
|
|
SC_SendData((PTS_SHAREDATAHEADER)pKeyPDU,
|
|
sizeof(TS_SET_KEYBOARD_INDICATORS_PDU),
|
|
sizeof(TS_SET_KEYBOARD_INDICATORS_PDU),
|
|
PROT_PRIO_UPDATES, 0);
|
|
}
|
|
else {
|
|
TRC_ALT((TB, "Failed to alloc pkt for PTS_SET_KEYBOARD_INDICATORS_PDU"));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* DCS_WDWKeyboardSetIndicators */
|
|
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_WDWKeyboardSetImeStatus */
|
|
/* */
|
|
/* Purpose: Notify that ime status have changed */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_WDWKeyboardSetImeStatus(void)
|
|
{
|
|
PTS_SET_KEYBOARD_IME_STATUS_PDU pImePDU;
|
|
|
|
DC_BEGIN_FN("DCS_WDWKeyboardSetImeStatus");
|
|
|
|
if ((_RNS_MAJOR_VERSION(m_pTSWd->version) > 8) ||
|
|
(_RNS_MINOR_VERSION(m_pTSWd->version) >= 2))
|
|
{
|
|
// Get a buffer.
|
|
if ( STATUS_SUCCESS == SC_AllocBuffer((PPVOID)&pImePDU,
|
|
sizeof(TS_SET_KEYBOARD_IME_STATUS_PDU)) )
|
|
{
|
|
// Zero out the structure and set basic header info.
|
|
memset(pImePDU, 0, sizeof(TS_SET_KEYBOARD_IME_STATUS_PDU));
|
|
|
|
pImePDU->shareDataHeader.pduType2 = TS_PDUTYPE2_SET_KEYBOARD_IME_STATUS;
|
|
|
|
pImePDU->UnitId = m_pTSWd->KeyboardImeStatus.UnitId;
|
|
pImePDU->ImeOpen = m_pTSWd->KeyboardImeStatus.ImeOpen;
|
|
pImePDU->ImeConvMode = m_pTSWd->KeyboardImeStatus.ImeConvMode;
|
|
|
|
// Send it.
|
|
SC_SendData((PTS_SHAREDATAHEADER)pImePDU,
|
|
sizeof(TS_SET_KEYBOARD_IME_STATUS_PDU),
|
|
sizeof(TS_SET_KEYBOARD_IME_STATUS_PDU),
|
|
PROT_PRIO_UPDATES, 0);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc pkt for PTS_SET_KEYBOARD_IME_STATUS_PDU"));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* DCS_WDWKeyboardSetImeStatus */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_TriggerUpdateShmCallback */
|
|
/* */
|
|
/* Purpose: Triggers a callback to the XX_UpdateShm functions on the */
|
|
/* correct WinStation context. */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_TriggerUpdateShmCallback(void)
|
|
{
|
|
DC_BEGIN_FN("DCS_TriggerUpdateShmCallback");
|
|
|
|
if (!dcsUpdateShmPending)
|
|
{
|
|
TRC_NRM((TB, "Trigger timer for UpdateShm"));
|
|
dcsUpdateShmPending = TRUE;
|
|
|
|
if (!dcsCallbackTimerPending)
|
|
{
|
|
WDW_StartRITTimer(m_pTSWd, 0);
|
|
dcsCallbackTimerPending = TRUE;
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* DCS_TriggerUpdateShmCallback */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_TriggerCBDataReady */
|
|
/* */
|
|
/* Purpose: Triggers a call to the clipboard data ready function in the */
|
|
/* correct WinStation context. */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_TriggerCBDataReady(void)
|
|
{
|
|
DC_BEGIN_FN("DCS_TriggerCBDataReady");
|
|
|
|
TRC_NRM((TB, "Trigger timer for CBDataReady"));
|
|
|
|
if (!dcsCallbackTimerPending)
|
|
WDW_StartRITTimer(m_pTSWd, 10); // @@@ try 10ms delay
|
|
|
|
DC_END_FN();
|
|
} /* DCS_TriggerCBDataReady */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_UpdateShm */
|
|
/* */
|
|
/* Purpose: Update SHM */
|
|
/* */
|
|
/* Operation: Guaranteed to be called in a context where m_pShm is valid. */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_UpdateShm(void)
|
|
{
|
|
DC_BEGIN_FN("DCS_UpdateShm");
|
|
|
|
TRC_NRM((TB, "Check for specific wake-up calls."));
|
|
|
|
if (dcsUpdateShmPending)
|
|
{
|
|
TRC_NRM((TB, "Call UpdateShm calls"));
|
|
|
|
// A Global flag indicating shm updates for all components
|
|
m_pShm->fShmUpdate = TRUE;
|
|
|
|
SSI_UpdateShm();
|
|
SBC_UpdateShm();
|
|
BA_UpdateShm();
|
|
OA_UpdateShm();
|
|
OE_UpdateShm();
|
|
CM_UpdateShm();
|
|
SCH_UpdateShm();
|
|
SC_UpdateShm();
|
|
|
|
dcsUpdateShmPending = FALSE;
|
|
}
|
|
dcsCallbackTimerPending = FALSE;
|
|
|
|
DC_END_FN();
|
|
} /* DCS_UpdateShm */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_SendErrorInfo */
|
|
/* */
|
|
/* Purpose: Sends last error information to the client so that it can */
|
|
/* Display meaningful error messages to users about disconnects */
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_SendErrorInfo(TSUINT32 errInfo)
|
|
{
|
|
PTS_SET_ERROR_INFO_PDU pErrorPDU = NULL;
|
|
PTS_SHAREDATAHEADER pHdr = NULL;
|
|
DC_BEGIN_FN("DCS_SendErrorInfo");
|
|
|
|
TRC_ASSERT(m_pTSWd->bSupportErrorInfoPDU,
|
|
(TB,"DCS_SendErrorInfo called but client doesn't"
|
|
"support errorinfo PDU"));
|
|
//Send a PDU to the client to indicate the last error state
|
|
//this is analogous to win32's SetLastError() the PDU doesn't
|
|
//trigger a disconnect. The normal code path to disconnect
|
|
//is unchanged so we don't worry about affecting compatability with
|
|
//older clients
|
|
|
|
if ( STATUS_SUCCESS == SM_AllocBuffer( m_pTSWd->pSmInfo,
|
|
(PPVOID) &pErrorPDU,
|
|
sizeof(TS_SET_ERROR_INFO_PDU),
|
|
FALSE, //never wait for an error packet
|
|
FALSE) )
|
|
{
|
|
// Zero out the structure and set basic header info.
|
|
memset(pErrorPDU, 0, sizeof(TS_SET_ERROR_INFO_PDU));
|
|
|
|
//
|
|
// First set the share data header info
|
|
//
|
|
pHdr = (PTS_SHAREDATAHEADER)pErrorPDU;
|
|
pHdr->shareControlHeader.pduType = TS_PDUTYPE_DATAPDU |
|
|
TS_PROTOCOL_VERSION;
|
|
pHdr->shareControlHeader.pduSource = 0; //user id may not be
|
|
//available yet
|
|
pHdr->shareControlHeader.totalLength = sizeof(TS_SET_ERROR_INFO_PDU);
|
|
pHdr->shareID = 0;
|
|
pHdr->streamID = (BYTE)PROT_PRIO_UPDATES;
|
|
pHdr->uncompressedLength = (UINT16)sizeof(TS_SET_ERROR_INFO_PDU);
|
|
pHdr->generalCompressedType = 0;
|
|
pHdr->generalCompressedLength = 0;
|
|
m_pTSWd->pProtocolStatus->Output.CompressedBytes +=
|
|
sizeof(TS_SET_ERROR_INFO_PDU);
|
|
|
|
//
|
|
// Error pdu specific info
|
|
//
|
|
pErrorPDU->shareDataHeader.pduType2 =
|
|
TS_PDUTYPE2_SET_ERROR_INFO_PDU;
|
|
pErrorPDU->errorInfo = errInfo;
|
|
|
|
|
|
TRC_NRM((TB,"Sending ErrorInfo PDU for err:%d", errInfo));
|
|
// Send it
|
|
if(!SM_SendData( m_pTSWd->pSmInfo,
|
|
pErrorPDU,
|
|
sizeof(TS_SET_ERROR_INFO_PDU),
|
|
0, 0, FALSE, RNS_SEC_ENCRYPT, FALSE))
|
|
{
|
|
TRC_ERR((TB, "Failed to SM_SendData for "
|
|
"TS_SET_ERROR_INFO_PDU"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"TS_SET_ERROR_INFO_PDU"));
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Name: DCS_SendAutoReconnectStatus
|
|
/*
|
|
/* Purpose: Sends autoreconnect status info to the client
|
|
/* (e.g. that autoreconnect failed so the client should go back to
|
|
/* displaying output so the user can enter cred at winlogon)
|
|
/*
|
|
/* Params:
|
|
/*
|
|
/*
|
|
/****************************************************************************/
|
|
void RDPCALL SHCLASS DCS_SendAutoReconnectStatus(TSUINT32 arcStatus)
|
|
{
|
|
PTS_AUTORECONNECT_STATUS_PDU pArcStatus = NULL;
|
|
PTS_SHAREDATAHEADER pHdr = NULL;
|
|
DC_BEGIN_FN("DCS_SendErrorInfo");
|
|
|
|
TRC_ASSERT(scUseAutoReconnect,
|
|
(TB,"DCS_SendAutoReconnectStatus called but client doesn't"
|
|
"support autoreconnect status PDU"));
|
|
|
|
if ( STATUS_SUCCESS == SM_AllocBuffer( m_pTSWd->pSmInfo,
|
|
(PPVOID) &pArcStatus,
|
|
sizeof(TS_AUTORECONNECT_STATUS_PDU),
|
|
FALSE, //never wait for an error packet
|
|
FALSE) )
|
|
{
|
|
// Zero out the structure and set basic header info.
|
|
memset(pArcStatus, 0, sizeof(TS_AUTORECONNECT_STATUS_PDU));
|
|
|
|
//
|
|
// First set the share data header info
|
|
//
|
|
pHdr = (PTS_SHAREDATAHEADER)pArcStatus;
|
|
pHdr->shareControlHeader.pduType = TS_PDUTYPE_DATAPDU |
|
|
TS_PROTOCOL_VERSION;
|
|
pHdr->shareControlHeader.pduSource = 0; //user id may not be
|
|
//available yet
|
|
pHdr->shareControlHeader.totalLength =
|
|
sizeof(TS_AUTORECONNECT_STATUS_PDU);
|
|
pHdr->shareID = scShareID;
|
|
pHdr->streamID = (BYTE)PROT_PRIO_UPDATES;
|
|
pHdr->uncompressedLength =
|
|
(UINT16)sizeof(TS_AUTORECONNECT_STATUS_PDU);
|
|
pHdr->generalCompressedType = 0;
|
|
pHdr->generalCompressedLength = 0;
|
|
m_pTSWd->pProtocolStatus->Output.CompressedBytes +=
|
|
sizeof(TS_AUTORECONNECT_STATUS_PDU);
|
|
|
|
//
|
|
// Error pdu specific info
|
|
//
|
|
pArcStatus->shareDataHeader.pduType2 =
|
|
TS_PDUTYPE2_ARC_STATUS_PDU;
|
|
pArcStatus->arcStatus = arcStatus;
|
|
|
|
|
|
TRC_NRM((TB,"Sending ArcStatus PDU for status:%d", arcStatus));
|
|
// Send it
|
|
if(!SM_SendData( m_pTSWd->pSmInfo,
|
|
pArcStatus,
|
|
sizeof(TS_AUTORECONNECT_STATUS_PDU),
|
|
0, 0, FALSE, RNS_SEC_ENCRYPT, FALSE))
|
|
{
|
|
TRC_ERR((TB, "Failed to SM_SendData for "
|
|
"TS_AUTORECONNECT_STATUS_PDU"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRC_ALT((TB, "Failed to alloc pkt for "
|
|
"TS_AUTORECONNECT_STATUS_PDU"));
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|