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.
1189 lines
55 KiB
1189 lines
55 KiB
/****************************************************************************/
|
|
// anmapi.c
|
|
//
|
|
// Network Manger
|
|
//
|
|
// Copyright(C) Microsoft Corporation 1997-1999
|
|
/****************************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define TRC_GROUP TRC_GROUP_NETWORK
|
|
#define TRC_FILE "anmapi"
|
|
#define pTRCWd (pRealNMHandle->pWDHandle)
|
|
|
|
#include <adcg.h>
|
|
#include <acomapi.h>
|
|
#include <anmint.h>
|
|
#include <asmapi.h>
|
|
#include <nwdwapi.h>
|
|
#include <anmapi.h>
|
|
#include <nprcount.h>
|
|
#include <tschannl.h>
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_GetDataSize */
|
|
/* */
|
|
/* Purpose: Returns size of per-instance NM data required */
|
|
/* */
|
|
/* Returns: size of data required */
|
|
/* */
|
|
/* Operation: NM stores per-instance data in a piece of memory allocated */
|
|
/* by WDW. This function returns the size of the data required. */
|
|
/* A pointer to this data (the 'NM Handle') is passed into all */
|
|
/* subsequent NM functions. */
|
|
/****************************************************************************/
|
|
unsigned RDPCALL NM_GetDataSize(void)
|
|
{
|
|
DC_BEGIN_FN("NM_GetDataSize");
|
|
|
|
DC_END_FN();
|
|
return(sizeof(NM_HANDLE_DATA));
|
|
} /* NM_GetDataSize */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_Init */
|
|
/* */
|
|
/* Purpose: Initialize NM */
|
|
/* */
|
|
/* Returns: TRUE - Registered OK */
|
|
/* FALSE - Registration failed */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* pSMHandle - SM handle, stored by NM and passed on callbacks */
|
|
/* to SM */
|
|
/* pWDHandle - WD handle, required for tracing */
|
|
/* hDomainKernel - MCS handle stored from MCSInitialize() and */
|
|
/* used to attach a user. */
|
|
/* */
|
|
/* Operation: Initialize NM: */
|
|
/* - initialize per-instance data */
|
|
/* - open channel for communication with PDMCS */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL NM_Init(PVOID pNMHandle,
|
|
PVOID pSMHandle,
|
|
PTSHARE_WD pWDHandle,
|
|
DomainHandle hDomainKernel)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
DC_BEGIN_FN("NM_Init");
|
|
|
|
/************************************************************************/
|
|
/* WARNING: Don't trace before storing the WD Handle */
|
|
/************************************************************************/
|
|
pRealNMHandle->pWDHandle = pWDHandle;
|
|
pRealNMHandle->pSMHandle = pSMHandle;
|
|
pRealNMHandle->pContext = pWDHandle->pContext;
|
|
pRealNMHandle->hDomain = hDomainKernel;
|
|
|
|
DC_END_FN();
|
|
return(TRUE);
|
|
} /* NM_Init */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_Term */
|
|
/* */
|
|
/* Purpose: Terminate NM */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* */
|
|
/* Operation: Terminate NM */
|
|
/* - close channel opened by NM_Init */
|
|
/****************************************************************************/
|
|
void RDPCALL NM_Term(PVOID pNMHandle)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
unsigned i;
|
|
PNM_CHANNEL_DATA pChannelData;
|
|
|
|
DC_BEGIN_FN("NM_Term");
|
|
|
|
TRC_NRM((TB, "Terminate NM"));
|
|
|
|
/************************************************************************/
|
|
/* Free any half-received virtual channel data */
|
|
/************************************************************************/
|
|
for (i = 0, pChannelData = pRealNMHandle->channelData;
|
|
i < pRealNMHandle->channelArrayCount;
|
|
i++, pChannelData++)
|
|
{
|
|
if (pChannelData->pData != NULL)
|
|
{
|
|
TRC_NRM((TB, "Free %p", pChannelData->pData));
|
|
COM_Free(pChannelData->pData);
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* NM_Term */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_Connect */
|
|
/* */
|
|
/* Purpose: Starts the process of connecting to the Client */
|
|
/* */
|
|
/* Returns: TRUE - Connect started OK */
|
|
/* FALSE - Connect failed to start */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* pUserDataIn - user data received from Client */
|
|
/* */
|
|
/* Operation: Attach the user to the domain. PDMCS knows about only 1 */
|
|
/* domain, so that one is assumed. */
|
|
/* */
|
|
/* When the AttachUser completes, join two channels: */
|
|
/* - the dynamically allocated broadcast channel (ID returned on */
|
|
/* the SM_OnConnected callback) */
|
|
/* - the single-user channel for this user. */
|
|
/* */
|
|
/* Note that this function completes asynchronously. The caller */
|
|
/* must wait for a SM_OnConnected or SM_OnDisconnected */
|
|
/* callback to find out whether the Connect succeeded or failed. */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL NM_Connect(PVOID pNMHandle, PRNS_UD_CS_NET pUserDataIn)
|
|
{
|
|
BOOL rc = FALSE;
|
|
BOOLEAN bCompleted;
|
|
MCSError MCSErr;
|
|
ChannelHandle hChannel;
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
unsigned i, j;
|
|
unsigned userDataOutLength;
|
|
PRNS_UD_SC_NET pUserDataOut = NULL;
|
|
PCHANNEL_DEF pChannel;
|
|
UINT16 *pMCSChannel;
|
|
ChannelID ChID;
|
|
UINT32 DataLenValidate;
|
|
|
|
// for unalignment use purpose
|
|
UserHandle UserHandleTemp;
|
|
unsigned MaxSendSizeTemp;
|
|
|
|
DC_BEGIN_FN("NM_Connect");
|
|
|
|
/************************************************************************/
|
|
/* Clear the connection status */
|
|
/************************************************************************/
|
|
pRealNMHandle->connectStatus = 0;
|
|
|
|
/************************************************************************/
|
|
/* Save virtual channel data */
|
|
/************************************************************************/
|
|
if (pUserDataIn != NULL)
|
|
{
|
|
TRC_DATA_NRM("Net User Data",
|
|
pUserDataIn,
|
|
pUserDataIn->header.length);
|
|
TRC_NRM((TB, "Protocol version %#x (%#x/%#x)",
|
|
pRealNMHandle->pWDHandle->version,
|
|
_RNS_MAJOR_VERSION(pRealNMHandle->pWDHandle->version),
|
|
_RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version)));
|
|
|
|
/********************************************************************/
|
|
/* Protocol version 0x00080002 used 2-byte channel data lengths. */
|
|
/* Protocol version 0x00080003 and higher use 4-byte data lengths. */
|
|
/* If this is a 2-byte version, ignore its virtual channels. */
|
|
/********************************************************************/
|
|
if (_RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version) >= 3)
|
|
{
|
|
// Validate channle count
|
|
DataLenValidate = pUserDataIn->channelCount * sizeof(CHANNEL_DEF);
|
|
DataLenValidate += sizeof(RNS_UD_CS_NET);
|
|
|
|
if (DataLenValidate > pUserDataIn->header.length)
|
|
{
|
|
TRC_ERR((TB, "Error: Virtual channel data length %u too short for %u",
|
|
pUserDataIn->header.length, DataLenValidate));
|
|
pRealNMHandle->channelCount = 0;
|
|
pRealNMHandle->channelArrayCount = 0;
|
|
WDW_LogAndDisconnect(pRealNMHandle->pWDHandle, TRUE,
|
|
Log_RDP_VChannelDataTooShort, (PBYTE)pUserDataIn, pUserDataIn->header.length);
|
|
DC_QUIT;
|
|
}
|
|
|
|
// we reserve channel 7 for RDPDD so we can allow only the
|
|
// buffer size - 1 channels.
|
|
if (pUserDataIn->channelCount > sizeof(pRealNMHandle->channelData)
|
|
/ sizeof(pRealNMHandle->channelData[0]) - 1) {
|
|
TRC_ERR((TB, "Error: Too many virtual channels to join: %u.",
|
|
pUserDataIn->channelCount));
|
|
pRealNMHandle->channelCount = 0;
|
|
pRealNMHandle->channelArrayCount = 0;
|
|
WDW_LogAndDisconnect(pRealNMHandle->pWDHandle, TRUE,
|
|
Log_RDP_VChannelsTooMany, (PBYTE)pUserDataIn,
|
|
pUserDataIn->header.length);
|
|
DC_QUIT;
|
|
}
|
|
|
|
|
|
pRealNMHandle->channelCount = pUserDataIn->channelCount;
|
|
pChannel = (PCHANNEL_DEF)(pUserDataIn + 1);
|
|
for (i = 0, j = 0; i < pRealNMHandle->channelCount; i++, j++)
|
|
{
|
|
/************************************************************/
|
|
/* Channel 7 is used by RDPDD, so skip it */
|
|
/************************************************************/
|
|
if (i == WD_THINWIRE_CHANNEL)
|
|
{
|
|
j++;
|
|
}
|
|
|
|
/************************************************************/
|
|
/* Save channel data */
|
|
/************************************************************/
|
|
strncpy(pRealNMHandle->channelData[j].name,
|
|
pChannel[i].name,
|
|
CHANNEL_NAME_LEN);
|
|
// make sure that the name is zero-terminated
|
|
(pRealNMHandle->channelData[j].name)[CHANNEL_NAME_LEN] = 0;
|
|
pRealNMHandle->channelData[j].flags = pChannel[i].options;
|
|
|
|
TRC_NRM((TB, "Channel %d (was %d): %s",
|
|
j, i, pChannel[i].name));
|
|
}
|
|
pRealNMHandle->channelArrayCount = j;
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB,
|
|
"Minor version %#x doesn't support 4-byte channel data lengths",
|
|
_RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version)));
|
|
pRealNMHandle->channelCount = 0;
|
|
pRealNMHandle->channelArrayCount = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* No incoming user data = no virtual channels */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, "No virtual channels"));
|
|
pRealNMHandle->channelCount = 0;
|
|
pRealNMHandle->channelArrayCount = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Allocate space for returned user data */
|
|
/************************************************************************/
|
|
userDataOutLength = (sizeof(RNS_UD_SC_NET) +
|
|
(sizeof(UINT16) * pRealNMHandle->channelCount));
|
|
userDataOutLength = (unsigned)(DC_ROUND_UP_4(userDataOutLength));
|
|
pUserDataOut = COM_Malloc(userDataOutLength);
|
|
if (pUserDataOut != NULL) {
|
|
memset(pUserDataOut, 0, userDataOutLength);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc %d bytes for user data",
|
|
userDataOutLength));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Make the attach-user request call. */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, "Attach User"));
|
|
|
|
UserHandleTemp = pRealNMHandle->hUser;
|
|
MaxSendSizeTemp = pRealNMHandle->maxPDUSize;
|
|
MCSErr = MCSAttachUserRequest(pRealNMHandle->hDomain,
|
|
NM_MCSUserCallback,
|
|
SM_MCSSendDataCallback,
|
|
pNMHandle,
|
|
&UserHandleTemp,
|
|
&MaxSendSizeTemp,
|
|
(BOOLEAN *)(&bCompleted));
|
|
pRealNMHandle->hUser = UserHandleTemp;
|
|
pRealNMHandle->maxPDUSize = MaxSendSizeTemp;
|
|
if (MCSErr == MCS_NO_ERROR) {
|
|
TRC_NRM((TB, "AttachUser OK, hUser %p", pRealNMHandle->hUser));
|
|
|
|
TRC_ASSERT((bCompleted),
|
|
(TB, "MCSAttachUser didn't complete synchronously"));
|
|
|
|
// Extract extra information.
|
|
pRealNMHandle->userID = MCSGetUserIDFromHandle(pRealNMHandle->hUser);
|
|
pRealNMHandle->connectStatus |= NM_CONNECT_ATTACH;
|
|
TRC_NRM((TB, "Attached as user %x, hUser %p",
|
|
pRealNMHandle->userID, pRealNMHandle->hUser));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed AttachUserRequest, MCSErr %d", MCSErr));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Join the broadcast channel */
|
|
/************************************************************************/
|
|
MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser,
|
|
0,
|
|
&hChannel,
|
|
&bCompleted);
|
|
if (MCSErr == MCS_NO_ERROR) {
|
|
TRC_ASSERT((bCompleted),
|
|
(TB, "MCSChannelJoin didn't complete synchronously"));
|
|
|
|
// Extract information.
|
|
ChID = MCSGetChannelIDFromHandle(hChannel);
|
|
pRealNMHandle->channelID = ChID;
|
|
pRealNMHandle->hChannel = hChannel;
|
|
pRealNMHandle->connectStatus |= NM_CONNECT_JOIN_BROADCAST;
|
|
TRC_NRM((TB, "Joined broadcast channel %x (hChannel %p) OK",
|
|
ChID, hChannel));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to send ChannelJoinRequest, MCSErr %d", MCSErr));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Join the user channel */
|
|
/************************************************************************/
|
|
MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser,
|
|
pRealNMHandle->userID,
|
|
&hChannel,
|
|
&bCompleted);
|
|
if (MCSErr == MCS_NO_ERROR) {
|
|
TRC_ASSERT((bCompleted),
|
|
(TB, "MCSChannelJoin didn't complete synchronously"));
|
|
|
|
// Extract information.
|
|
pRealNMHandle->connectStatus |= NM_CONNECT_JOIN_USER;
|
|
TRC_NRM((TB, "Joined user channel (hChannel %p) OK", hChannel));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to send ChannelJoinRequest, MCSErr %d", MCSErr));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* If no virtual channels, we're done. */
|
|
/************************************************************************/
|
|
if (pRealNMHandle->channelCount == 0)
|
|
{
|
|
TRC_NRM((TB, "No virtual channels to join"));
|
|
rc = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Join virtual channels */
|
|
/************************************************************************/
|
|
for (i = 0; i < pRealNMHandle->channelArrayCount; i++)
|
|
{
|
|
if (i == WD_THINWIRE_CHANNEL)
|
|
{
|
|
TRC_NRM((TB, "Skip channel %d", WD_THINWIRE_CHANNEL));
|
|
continue;
|
|
}
|
|
|
|
MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser,
|
|
0,
|
|
&hChannel,
|
|
&bCompleted);
|
|
if (MCSErr == MCS_NO_ERROR) {
|
|
TRC_ASSERT((bCompleted),
|
|
(TB, "MCSChannelJoin didn't complete synchronously"));
|
|
|
|
ChID = MCSGetChannelIDFromHandle(hChannel);
|
|
pRealNMHandle->channelData[i].MCSChannelID = (UINT16)ChID;
|
|
TRC_NRM((TB, "Joined VC %d: %d (hChannel %p)", i, ChID, hChannel));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "ChannelJoinRequest failed, MCSErr %d", MCSErr));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Everything completed OK */
|
|
/************************************************************************/
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
if (rc)
|
|
{
|
|
/********************************************************************/
|
|
/* Everything is OK - Complete the user data */
|
|
/********************************************************************/
|
|
pUserDataOut->header.type = RNS_UD_SC_NET_ID;
|
|
pUserDataOut->header.length = (UINT16)userDataOutLength;
|
|
pUserDataOut->MCSChannelID = (UINT16)pRealNMHandle->channelID;
|
|
pUserDataOut->channelCount = (UINT16)pRealNMHandle->channelCount;
|
|
pMCSChannel = (UINT16 *)(pUserDataOut + 1);
|
|
TRC_NRM((TB, "Copy %d channels to user data out",
|
|
pRealNMHandle->channelCount));
|
|
for (i = 0, j = 0; i < pRealNMHandle->channelCount; i++, j++)
|
|
{
|
|
if (i == WD_THINWIRE_CHANNEL)
|
|
{
|
|
TRC_NRM((TB, "Skip channel %d", WD_THINWIRE_CHANNEL));
|
|
j++;
|
|
}
|
|
pMCSChannel[i] = pRealNMHandle->channelData[j].MCSChannelID;
|
|
TRC_NRM((TB, "Channel %d (%d) = %#x", i, j, pMCSChannel[i]));
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* Tell SM we're connected now */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, "Tell SM we're connecting"));
|
|
SM_OnConnected(pRealNMHandle->pSMHandle, pRealNMHandle->userID,
|
|
NM_CB_CONN_OK, pUserDataOut, pRealNMHandle->maxPDUSize);
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* Something failed - abort the connection */
|
|
/********************************************************************/
|
|
TRC_NRM((TB, "Something failed - abort the connection"));
|
|
NMAbortConnect(pRealNMHandle);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Whether we succeeded or failed, we don't need the user data any */
|
|
/* more. */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, "Free user data"));
|
|
if (pUserDataOut != NULL) {
|
|
COM_Free(pUserDataOut);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* NM_Connect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_GetMCSDomainInfo */
|
|
/* */
|
|
/* Purpose: Return the broadcast channel ID to allow shadowing of this */
|
|
/* share. */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/****************************************************************************/
|
|
ChannelID RDPCALL NM_GetMCSDomainInfo(PVOID pNMHandle)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
|
|
return pRealNMHandle->channelID;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_Disconnect */
|
|
/* */
|
|
/* Purpose: Disconnect from a Client */
|
|
/* */
|
|
/* Returns: TRUE - Disconnect started OK */
|
|
/* FALSE - Disconnect failed */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* */
|
|
/* Operation: Detach the user from the domain. */
|
|
/* */
|
|
/* Note that this function completes asynchronously. The caller */
|
|
/* must wait for a SM_OnDisconnected callback to find whether */
|
|
/* the Disconnect succeeded or failed. */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL NM_Disconnect(PVOID pNMHandle)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
BOOL rc = TRUE;
|
|
|
|
DC_BEGIN_FN("NM_Disconnect");
|
|
|
|
/************************************************************************/
|
|
/* Detach from MCS */
|
|
/************************************************************************/
|
|
if (pRealNMHandle->connectStatus & NM_CONNECT_ATTACH)
|
|
{
|
|
TRC_NRM((TB, "User attached, need to detach"));
|
|
rc = NMDetachUserReq(pRealNMHandle);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* NM_Disconnect */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_AllocBuffer */
|
|
/* */
|
|
/* Purpose: Acquire a buffer for transmission */
|
|
/* */
|
|
/* Returns: TRUE - Buffer acquired OK */
|
|
/* FALSE - No buffer available */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* ppBuffer - Buffer acquired (returned) */
|
|
/* bufferSize - size of buffer required */
|
|
/* */
|
|
/* Operation: Get a buffer from ICA (via IcaBufferAlloc) */
|
|
/* This function is synchronous. */
|
|
/****************************************************************************/
|
|
NTSTATUS __fastcall NM_AllocBuffer(PVOID pNMHandle,
|
|
PPVOID ppBuffer,
|
|
UINT32 bufferSize,
|
|
BOOLEAN fWait)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
NTSTATUS status;
|
|
POUTBUF pOutBuf;
|
|
int i;
|
|
UINT32 realBufferSize;
|
|
|
|
DC_BEGIN_FN("NM_AllocBuffer");
|
|
|
|
/************************************************************************/
|
|
/* Calculate the actual size of data required. Includes a POUTBUF */
|
|
/* prefix to point back to the beginning of the OutBuf so we can send */
|
|
/* the right thing to MCS. */
|
|
/************************************************************************/
|
|
TRC_ASSERT((bufferSize < 16384),
|
|
(TB,"Buffer req size %u will cause MCS fragmentation, unsupported",
|
|
bufferSize));
|
|
realBufferSize = bufferSize + SendDataReqPrefixBytes + sizeof(POUTBUF);
|
|
|
|
/************************************************************************/
|
|
/* Allocate an OutBuf */
|
|
/************************************************************************/
|
|
status = IcaBufferAlloc(pRealNMHandle->pContext,
|
|
fWait, /* wait/not wait for a buffer */
|
|
FALSE, /* not a control buffer */
|
|
realBufferSize,
|
|
NULL, /* no original buffer */
|
|
(PVOID *)(&pOutBuf));
|
|
if (status == STATUS_SUCCESS) { // NT_SUCCESS() does not fail STATUS_TIMEOUT
|
|
/********************************************************************/
|
|
/* The OutBuf returned includes a data buffer pointer, which points */
|
|
/* to a buffer containing */
|
|
/* - a pointer to the beginning of the OutBuf (which we set here) */
|
|
/* - SendDataReqPrefixBytes */
|
|
/* - user data buffer (size bufferSize) */
|
|
/* Set the OutBuf pBuffer pointer to the beginning of the user data.*/
|
|
/* MCS requires this. */
|
|
/* Return to the user a pointer to the user data buffer. */
|
|
/********************************************************************/
|
|
*((POUTBUF *)pOutBuf->pBuffer) = pOutBuf;
|
|
pOutBuf->pBuffer += SendDataReqPrefixBytes + sizeof(POUTBUF);
|
|
*ppBuffer = pOutBuf->pBuffer;
|
|
|
|
TRC_NRM((TB, "Alloc %d bytes OK", bufferSize));
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, "Failed to alloc %d bytes, status %x",
|
|
bufferSize, status));
|
|
|
|
//
|
|
// TODO - consider disconnect client here instead of SM_AllocBuffer(),
|
|
// keep it in SM_AllocBuffer() so that we don't introduce any regression.
|
|
//
|
|
|
|
//
|
|
// IcaBufferAlloc() returns STATUS_IO_TIMEOUT, STATUS_NO_MEMORY, and
|
|
// IcaWaitForSingleObject() which returns KeWaitForSingleObject() or
|
|
// STATUS_CTX_CLOSE_PENDING. SM_AllocBuffer() need to disconnect this client only
|
|
// when error code is STATUS_IO_TIMEOUT so we keep this return code
|
|
}
|
|
|
|
DC_END_FN();
|
|
return status;
|
|
} /* NM_AllocBuffer */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_FreeBuffer */
|
|
/* */
|
|
/* Purpose: Free a transmit buffer */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* pBuffer - Buffer to free */
|
|
/* */
|
|
/* Operation: Free a buffer (via IcaBufferFree) */
|
|
/* This function assumes the buffer was allocated using */
|
|
/* NM_AllocBuffer. */
|
|
/* */
|
|
/* This function is only for freeing buffers which are not sent. */
|
|
/* It should not be called for buffers which are sent - */
|
|
/* NM_SendData frees the buffer whether the Send succeeds or */
|
|
/* not. */
|
|
/* */
|
|
/* This function is synchronous. */
|
|
/****************************************************************************/
|
|
void __fastcall NM_FreeBuffer(PVOID pNMHandle, PVOID pBuffer)
|
|
{
|
|
POUTBUF pOutBuf;
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
|
|
DC_BEGIN_FN("NM_FreeBuffer");
|
|
|
|
/************************************************************************/
|
|
/* Get the OutBuf pointer stored in prefix */
|
|
/************************************************************************/
|
|
pOutBuf = *((POUTBUF *)
|
|
((BYTE *)pBuffer - SendDataReqPrefixBytes - sizeof(POUTBUF)));
|
|
|
|
/************************************************************************/
|
|
/* Free the buffer */
|
|
/************************************************************************/
|
|
IcaBufferFree(pRealNMHandle->pContext, pOutBuf);
|
|
|
|
DC_END_FN();
|
|
} /* NM_FreeBuffer */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_SendData */
|
|
/* */
|
|
/* Purpose: Send data to the appropriate net or pipe destination. */
|
|
/* */
|
|
/* Returns: TRUE - data sent OK */
|
|
/* FALSE - data not sent */
|
|
/* */
|
|
/* Params: pNMHandle - NM handle */
|
|
/* pData - data to send */
|
|
/* dataSize - length of data to send */
|
|
/* priority - priority to send the data on */
|
|
/* userID - user to send the data to (0 = broadcast data) */
|
|
/* FastPathOutputFlags - flags from higher layers. Low bit TRUE */
|
|
/* means send as fast-path output, using the rest of the */
|
|
/*
|
|
/* */
|
|
/* Operation: Send the data. */
|
|
/* - The buffer holding the data must have been allocated using */
|
|
/* NM_AllocBuffer. */
|
|
/* - Return code FALSE means that a network error occurred. The */
|
|
/* caller need do nothing - an NM_DISCONNECTED callback will */
|
|
/* eventually arrive. */
|
|
/* - The buffer is ALWAYS freed. */
|
|
/* */
|
|
/* This function is synchronous. */
|
|
/****************************************************************************/
|
|
BOOL __fastcall NM_SendData(
|
|
PVOID pNMHandle,
|
|
PBYTE pData,
|
|
UINT32 dataSize,
|
|
UINT32 priority,
|
|
UINT32 userID,
|
|
UINT32 FastPathOutputFlags)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
BOOL rc = TRUE;
|
|
POUTBUF pOutBuf;
|
|
MCSError MCSErr;
|
|
|
|
DC_BEGIN_FN("NM_SendData");
|
|
|
|
/************************************************************************/
|
|
/* Get the OutBuf pointer stored in prefix */
|
|
/************************************************************************/
|
|
pOutBuf = *((POUTBUF *)
|
|
((BYTE *)pData - SendDataReqPrefixBytes - sizeof(POUTBUF)));
|
|
|
|
/************************************************************************/
|
|
/* Complete the OutBuf. The pBuffer was already set up when the OutBuf */
|
|
/* was allocated. MCS needs the user data size set in the OutBuf. */
|
|
/************************************************************************/
|
|
pOutBuf->ByteCount = dataSize;
|
|
|
|
// All but shadow passthru stacks get the data sent to the network.
|
|
if (pRealNMHandle->pWDHandle->StackClass != Stack_Passthru) {
|
|
if (FastPathOutputFlags & NM_SEND_FASTPATH_OUTPUT) {
|
|
NTSTATUS Status;
|
|
SD_RAWWRITE SdWrite;
|
|
|
|
// Fast-path output skips MCS. We rewrite the security header
|
|
// into the fast-path format, complete the OutBuf, and send
|
|
// directly to the transport. For header format details, see
|
|
// at128.h. Note we need to wait for the security header
|
|
// to be written in SM before getting here in case this
|
|
// is a passthru stack.
|
|
|
|
// First, the 4-byte RNS_SECURITY_HEADER disappears, if present,
|
|
// collapsed into the high bit of the first byte.
|
|
// Note that the 8-byte MAC signature in RNS_SECURITY_HEADER1
|
|
// remains, if present.
|
|
if (!(FastPathOutputFlags & NM_NO_SECURITY_HEADER)) {
|
|
dataSize -= sizeof(RNS_SECURITY_HEADER);
|
|
pData += sizeof(RNS_SECURITY_HEADER);
|
|
}
|
|
|
|
// Work backwards from where we are: First, the total packet
|
|
// length including the header.
|
|
if (dataSize <= 125) {
|
|
// 1-byte form of length, high bit 0.
|
|
dataSize += 2;
|
|
pData -= 2;
|
|
*(pData + 1) = (BYTE)dataSize;
|
|
}
|
|
else {
|
|
// 2-byte form of length, first byte has high bit 1 and 7
|
|
// most significant bits.
|
|
dataSize += 3;
|
|
pData -= 3;
|
|
*(pData + 1) = (BYTE)(0x80 | ((dataSize & 0x7F00) >> 8));
|
|
*(pData + 2) = (BYTE)(dataSize & 0xFF);
|
|
}
|
|
|
|
// The header byte. This includes TS_OUTPUT_FASTPATH_ACTION_FASTPATH
|
|
// and TS_OUTPUT_FASTPATH_ENCRYPTED, if present in the
|
|
// fast-path output flags.
|
|
*pData = (BYTE)(TS_OUTPUT_FASTPATH_ACTION_FASTPATH |
|
|
(FastPathOutputFlags &
|
|
TS_OUTPUT_FASTPATH_ENCRYPTION_MASK));
|
|
|
|
// Set up the OutBuf with its final contents.
|
|
pOutBuf->pBuffer = pData;
|
|
pOutBuf->ByteCount = dataSize;
|
|
|
|
// Send downward.
|
|
SdWrite.pBuffer = NULL;
|
|
SdWrite.ByteCount = 0;
|
|
SdWrite.pOutBuf = pOutBuf;
|
|
|
|
Status = IcaCallNextDriver(pRealNMHandle->pWDHandle->pContext,
|
|
SD$RAWWRITE, &SdWrite);
|
|
if (NT_SUCCESS(Status)) {
|
|
// Increment protocol counters.
|
|
pRealNMHandle->pWDHandle->pProtocolStatus->Output.WdFrames++;
|
|
pRealNMHandle->pWDHandle->pProtocolStatus->Output.WdBytes +=
|
|
dataSize;
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Failed IcaRawWrite to network, status=%X",
|
|
Status));
|
|
rc = FALSE;
|
|
// We do not free the OutBuf here, TD is supposed to do it.
|
|
}
|
|
}
|
|
else {
|
|
TRC_DBG((TB, "Send data on channel %x", userID));
|
|
MCSErr = MCSSendDataRequest(pRealNMHandle->hUser,
|
|
userID == 0 ? pRealNMHandle->hChannel : NULL,
|
|
NORMAL_SEND_DATA,
|
|
(ChannelID)userID,
|
|
(MCSPriority)priority,
|
|
SEGMENTATION_BEGIN | SEGMENTATION_END,
|
|
pOutBuf);
|
|
if (MCSErr == MCS_NO_ERROR) {
|
|
TRC_DATA_NRM("Send OK", pOutBuf, dataSize);
|
|
}
|
|
else
|
|
{
|
|
TRC_ERR((TB, "Failed to send OutBuf %p, buffer %p, MCSErr %x",
|
|
pOutBuf, pData, MCSErr));
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef DC_COUNTERS
|
|
if (rc) {
|
|
PTSHARE_WD m_pTSWd = pRealNMHandle->pWDHandle;
|
|
|
|
if (dataSize > CORE_IN_COUNT[IN_MAX_PKT_SIZE])
|
|
{
|
|
CORE_IN_COUNT[IN_MAX_PKT_SIZE] = dataSize;
|
|
}
|
|
CORE_IN_COUNT[IN_PKT_TOTAL_SENT]++;
|
|
if (dataSize < 201) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD1]++;
|
|
} else if (dataSize < 401) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD2]++;
|
|
} else if (dataSize < 601) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD3]++;
|
|
} else if (dataSize < 801) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD4]++;
|
|
} else if (dataSize < 1001) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD5]++;
|
|
} else if (dataSize < 1201 ) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD6]++;
|
|
} else if (dataSize < 1401) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD7]++;
|
|
} else if (dataSize < 1601) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD8]++;
|
|
} else if (dataSize < 2001) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD9]++;
|
|
} else if (dataSize < 4001) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD10]++;
|
|
} else if (dataSize < 6001) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD11]++;
|
|
} else if (dataSize < 8001) {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD12]++;
|
|
} else {
|
|
CORE_IN_COUNT[IN_PKT_BYTE_SPREAD13]++;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Raw write the potentially encrypted data to the shadow stack
|
|
else {
|
|
SD_RAWWRITE SdWrite;
|
|
NTSTATUS status;
|
|
|
|
TRC_ASSERT((!(FastPathOutputFlags & NM_SEND_FASTPATH_OUTPUT)),
|
|
(TB,"Fast-path output requested across shadow pipe!"));
|
|
|
|
SdWrite.pOutBuf = pOutBuf;
|
|
SdWrite.pBuffer = NULL;
|
|
SdWrite.ByteCount = 0;
|
|
|
|
status = IcaCallNextDriver(pRealNMHandle->pContext, SD$RAWWRITE, &SdWrite);
|
|
if (status == STATUS_SUCCESS) {
|
|
TRC_DBG((TB, "RawWrite: %ld bytes", dataSize));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "RawWrite failed: %lx", status));
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
} /* NM_SendData */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_MCSUserCallback */
|
|
/* */
|
|
/* Purpose: Direct callback from MCS */
|
|
/* */
|
|
/* Returns: nothing */
|
|
/* */
|
|
/* Params: hUser - should be our user handle */
|
|
/* Message - the callback type */
|
|
/* Params - Cast to the right type of parameter depending on */
|
|
/* callback */
|
|
/* UserDefined - our NM handle */
|
|
/* */
|
|
/* Operation: Called by MCS for callbacks. */
|
|
/* */
|
|
/* Processing depends on the callback type */
|
|
/* - MCS_DETACH_USER_INDICATION */
|
|
/* - call SM_OnDisconnected */
|
|
/* - all others are ignored. */
|
|
/****************************************************************************/
|
|
void __stdcall NM_MCSUserCallback(UserHandle hUser,
|
|
unsigned Message,
|
|
void *Params,
|
|
void *UserDefined)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)UserDefined;
|
|
|
|
DC_BEGIN_FN("NM_MCSUserCallback");
|
|
|
|
/************************************************************************/
|
|
/* First check that this is our UserHandle */
|
|
/************************************************************************/
|
|
ASSERT(hUser == pRealNMHandle->hUser);
|
|
|
|
/************************************************************************/
|
|
/* If the Share Core is dead, don't do any of this */
|
|
/************************************************************************/
|
|
if (pRealNMHandle->dead)
|
|
{
|
|
TRC_ALT((TB, "Callback %s (%d) ignored because we're dead",
|
|
Message == MCS_ATTACH_USER_CONFIRM ? "MCS_ATTACH_USER_CONFIRM" :
|
|
Message == MCS_CHANNEL_JOIN_CONFIRM ? "MCS_CHANNEL_JOIN_CONFIRM" :
|
|
Message == MCS_DETACH_USER_INDICATION ? "MCS_DETACH_USER_INDICATION" :
|
|
Message == MCS_SEND_DATA_INDICATION ? "MCS_SEND_DATA_INDICATION" :
|
|
"- Unknown - ",
|
|
Message));
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Handle callbacks we care about */
|
|
/************************************************************************/
|
|
switch (Message)
|
|
{
|
|
case MCS_DETACH_USER_INDICATION:
|
|
{
|
|
DetachUserIndication *pDUin;
|
|
|
|
TRC_NRM((TB, "DetachUserIndication"));
|
|
|
|
pDUin = (DetachUserIndication *)Params;
|
|
NMDetachUserInd(pRealNMHandle,
|
|
pDUin->Reason,
|
|
pDUin->UserID);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TRC_ERR((TB, "Unhandled MCS callback type %d", Message ));
|
|
}
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
} /* NM_MCSUserCallback */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_Dead */
|
|
/****************************************************************************/
|
|
void RDPCALL NM_Dead(PVOID pNMHandle, BOOL dead)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
DC_BEGIN_FN("NM_Dead");
|
|
|
|
TRC_NRM((TB, "NM Dead ? %s", pRealNMHandle->dead ? "Y" : "N"));
|
|
pRealNMHandle->dead = dead;
|
|
|
|
DC_END_FN();
|
|
} /* NM_Dead */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_VirtualQueryBindings */
|
|
/* */
|
|
/* Purpose: Return virtual channel bindings to WD */
|
|
/* */
|
|
/* Params: pNMHandle - NM Handle */
|
|
/* pVBind - pointer to virtual bindings structure to fill in */
|
|
/* vBindLength - size (bytes) of virtual bindings structure */
|
|
/* pBytesReturned - size (bytes) of data returned */
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL NM_VirtualQueryBindings(PVOID pNMHandle,
|
|
PSD_VCBIND pVBind,
|
|
ULONG vBindLength,
|
|
PULONG pBytesReturned)
|
|
{
|
|
NTSTATUS status;
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
USHORT virtualClass;
|
|
UINT i;
|
|
|
|
DC_BEGIN_FN("NM_VirtualQueryBindings");
|
|
|
|
/************************************************************************/
|
|
/* First see if we have any bindings to report */
|
|
/************************************************************************/
|
|
if (pRealNMHandle->channelCount == 0)
|
|
{
|
|
TRC_ALT((TB, "No Virtual Channels to report"));
|
|
*pBytesReturned = 0;
|
|
status = STATUS_SUCCESS;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Check there is enough space to report them */
|
|
/************************************************************************/
|
|
*pBytesReturned = (pRealNMHandle->channelCount * sizeof(SD_VCBIND));
|
|
if (vBindLength < *pBytesReturned)
|
|
{
|
|
TRC_ERR((TB, "Not enough space for %d VCs: need/got %d/%d",
|
|
pRealNMHandle->channelCount, *pBytesReturned, vBindLength));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Copy channel names and assign numbers */
|
|
/************************************************************************/
|
|
for (i = 0, virtualClass = 0;
|
|
i < pRealNMHandle->channelCount;
|
|
i++, virtualClass++, pVBind++)
|
|
{
|
|
/********************************************************************/
|
|
/* Can't use channel 7 as it's used by RDPDD */
|
|
/********************************************************************/
|
|
if (i == WD_THINWIRE_CHANNEL)
|
|
{
|
|
TRC_NRM((TB, "Skip channel %d", i));
|
|
virtualClass++;
|
|
}
|
|
|
|
strcpy(pVBind->VirtualName,
|
|
pRealNMHandle->channelData[virtualClass].name);
|
|
pVBind->VirtualClass = virtualClass;
|
|
pVBind->Flags = 0;
|
|
|
|
if (pRealNMHandle->channelData[virtualClass].flags & CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT) {
|
|
pVBind->Flags |= SD_CHANNEL_FLAG_SHADOW_PERSISTENT;
|
|
}
|
|
TRC_NRM((TB, "Assigned channel %d to %s",
|
|
pVBind->VirtualClass, pVBind->VirtualName));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* That's all */
|
|
/************************************************************************/
|
|
status = STATUS_SUCCESS;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(status);
|
|
} /* NM_VirtualQueryBindings */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_MCSChannelToVirtual */
|
|
/* */
|
|
/* Purpose: Convert an MCS channel ID into a virtual channel ID */
|
|
/* */
|
|
/* Returns: Virtual Channel ID */
|
|
/* */
|
|
/* Params: pNMHandle - NM Handle */
|
|
/* channelID - MCS Channel ID */
|
|
/* ppChannelData - data stored for this channel (returned) */
|
|
/****************************************************************************/
|
|
VIRTUALCHANNELCLASS RDPCALL NM_MCSChannelToVirtual(
|
|
PVOID pNMHandle,
|
|
UINT16 channelID,
|
|
PPNM_CHANNEL_DATA ppChannelData)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
PNM_CHANNEL_DATA pChannelData;
|
|
unsigned i;
|
|
VIRTUALCHANNELCLASS rc;
|
|
|
|
DC_BEGIN_FN("NM_MCSChannelToVirtual");
|
|
|
|
/************************************************************************/
|
|
/* Find this MCS Channel */
|
|
/************************************************************************/
|
|
TRC_DBG((TB, "Find MCS channel %hx", channelID));
|
|
for (i = 0, pChannelData = pRealNMHandle->channelData;
|
|
i < pRealNMHandle->channelArrayCount;
|
|
i++, pChannelData++)
|
|
{
|
|
TRC_DBG((TB, "Compare entry %d: %hx", i, pChannelData->MCSChannelID));
|
|
if (pChannelData->MCSChannelID == channelID)
|
|
{
|
|
rc = i;
|
|
*ppChannelData = pChannelData;
|
|
TRC_NRM((TB, "MCS channel %hx is VC %d", channelID, rc));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* If we get here, we failed to find a match */
|
|
/************************************************************************/
|
|
TRC_NRM((TB, "No match for MCS channel ID %hx", channelID));
|
|
rc = -1;
|
|
*ppChannelData = NULL;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* NM_MCSChannelToVirtual */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_VirtualChannelToMCS */
|
|
/* */
|
|
/* Purpose: Convert a virtual channel ID into an MCS channel ID */
|
|
/* */
|
|
/* Returns: MCS Channel ID */
|
|
/* */
|
|
/* Params: pNMHandle - NM Handle */
|
|
/* channelID - virtual channel ID */
|
|
/* ppChannelData - data stored for this channel (returned) */
|
|
/****************************************************************************/
|
|
INT16 RDPCALL NM_VirtualChannelToMCS(PVOID pNMHandle,
|
|
VIRTUALCHANNELCLASS channelID,
|
|
PPNM_CHANNEL_DATA ppChannelData)
|
|
{
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
INT16 rc;
|
|
|
|
DC_BEGIN_FN("NM_VirtualChannelToMCS");
|
|
|
|
/************************************************************************/
|
|
/* Check the virtual channel is in range */
|
|
/************************************************************************/
|
|
if (channelID >= (VIRTUALCHANNELCLASS)(pRealNMHandle->channelArrayCount))
|
|
{
|
|
TRC_ERR((TB, "Unknown virtual channel %d", channelID));
|
|
rc = -1;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Find this Virtual Channel */
|
|
/************************************************************************/
|
|
rc = pRealNMHandle->channelData[channelID].MCSChannelID;
|
|
*ppChannelData = &(pRealNMHandle->channelData[channelID]);
|
|
|
|
TRC_NRM((TB, "Virtual channel %d = MCS Channel %hx", channelID, rc));
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(rc);
|
|
} /* NM_VirtualChannelToMCS */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: NM_QueryChannels */
|
|
/* */
|
|
/* Purpose: Return virtual channel data */
|
|
/* */
|
|
/* Returns: TRUE/FALSE */
|
|
/* */
|
|
/* Params: pNMHandle - NM Handle */
|
|
/* pOutbuf - buffer to receive output data */
|
|
/* outbufLength - size of outbuf */
|
|
/* pBytesReturned - amount of data returned */
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL NM_QueryChannels(PVOID pNMHandle,
|
|
PVOID pOutbuf,
|
|
unsigned outbufLength,
|
|
PULONG pBytesReturned)
|
|
{
|
|
NTSTATUS status;
|
|
PCHANNEL_CONNECT_IN pChannelConnect;
|
|
PCHANNEL_CONNECT_DEF pChannelDef;
|
|
PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle;
|
|
unsigned bytesNeeded;
|
|
unsigned i;
|
|
|
|
DC_BEGIN_FN("NM_QueryChannels");
|
|
|
|
/************************************************************************/
|
|
/* Check enough space has been supplied */
|
|
/************************************************************************/
|
|
bytesNeeded = sizeof(CHANNEL_CONNECT_IN) +
|
|
(pRealNMHandle->channelArrayCount * sizeof(CHANNEL_CONNECT_DEF));
|
|
if (outbufLength < bytesNeeded)
|
|
{
|
|
TRC_ERR((TB, "Not enough space: need/got %d/%d",
|
|
bytesNeeded, outbufLength));
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Complete the returned data */
|
|
/************************************************************************/
|
|
pChannelConnect = (PCHANNEL_CONNECT_IN)pOutbuf;
|
|
pChannelConnect->channelCount = pRealNMHandle->channelArrayCount;
|
|
pChannelDef = (PCHANNEL_CONNECT_DEF)(pChannelConnect + 1);
|
|
for (i = 0; i < pRealNMHandle->channelArrayCount; i++)
|
|
{
|
|
strcpy(pChannelDef[i].name, pRealNMHandle->channelData[i].name);
|
|
pChannelDef[i].ID = i;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Return status and bytesReturned */
|
|
/************************************************************************/
|
|
*pBytesReturned = bytesNeeded;
|
|
status = STATUS_SUCCESS;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return(status);
|
|
} /* NM_QueryChannels */
|
|
|
|
|