drdynvc: code cleanup of the client dynamic channel (#8148)

This patch does various cleanups in the client dynamic channel. The main
goal of the cleanup was to add the sending of Close messages to the server
when a channel is locally closed. The refcounter is there to ensures that
the DVC_CHANNEL is not freed while some pieces of code are still holding a
reference on it.

I did some tests by using a custom server-side echo channel at
https://github.com/hardening/echoChannel, it allows to send a given amount of
packets and then close (to test server-side initiated closes). It compiles
with mingw (so under linux) and so it can be easily deployed (no deps).
This commit is contained in:
David Fort 2022-08-25 08:47:00 +02:00 committed by GitHub
parent 4fc7a9417d
commit 103f13575c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 257 additions and 203 deletions

View File

@ -23,6 +23,7 @@
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <winpr/interlocked.h>
#include <freerdp/channels/drdynvc.h>
@ -30,10 +31,7 @@
#define TAG CHANNELS_TAG("drdynvc.client")
static UINT dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
BOOL bSendClosePDU);
static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
static void dvcman_channel_free(void* channel);
static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
UINT32 dataSize, BOOL* close);
static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s);
@ -199,20 +197,32 @@ static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
return dvc->channel_name;
}
static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId)
static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId, BOOL doRef)
{
IWTSVirtualChannel* channel = NULL;
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
DVCMAN_CHANNEL* dvcChannel;
HashTable_Lock(dvcman->channelsById);
dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
if (dvcChannel)
channel = &dvcChannel->iface;
{
if (doRef)
InterlockedIncrement(&dvcChannel->refCounter);
}
HashTable_Unlock(dvcman->channelsById);
return channel;
return dvcChannel;
}
static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId)
{
DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
if (!channel)
return NULL;
return &channel->iface;
}
static void dvcman_plugin_terminate(void* plugin)
@ -264,9 +274,6 @@ static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
obj = HashTable_KeyObject(dvcman->channelsById);
obj->fnObjectEquals = channelIdMatch;
obj = HashTable_ValueObject(dvcman->channelsById);
obj->fnObjectFree = dvcman_channel_free;
dvcman->pool = StreamPool_New(TRUE, 10);
if (!dvcman->pool)
goto fail;
@ -336,28 +343,116 @@ static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager*
return ERROR_INVALID_FUNCTION;
}
static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
{
WINPR_ASSERT(channel);
if (channel->dvc_data)
Stream_Release(channel->dvc_data);
DeleteCriticalSection(&(channel->lock));
free(channel->channel_name);
free(channel);
}
static void dvcman_channel_unref(DVCMAN_CHANNEL* channel)
{
DVCMAN* dvcman;
if (InterlockedDecrement(&channel->refCounter))
return;
dvcman = channel->dvcman;
HashTable_Remove(dvcman->channelsById, &channel->channel_id);
dvcman_channel_free(channel);
}
static UINT dvcchannel_send_close(DVCMAN_CHANNEL* channel)
{
DVCMAN* dvcman = channel->dvcman;
drdynvcPlugin* drdynvc = dvcman->drdynvc;
wStream* s = StreamPool_Take(dvcman->pool, 5);
if (!s)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
Stream_Write_UINT32(s, channel->channel_id);
return drdynvc_send(drdynvc, s);
}
static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, bool perRequest)
{
UINT error = CHANNEL_RC_OK;
drdynvcPlugin* drdynvc;
DrdynvcClientContext* context;
switch (channel->state)
{
case DVC_CHANNEL_INIT:
break;
case DVC_CHANNEL_RUNNING:
drdynvc = channel->dvcman->drdynvc;
context = drdynvc->context;
if (perRequest)
WLog_Print(drdynvc->log, WLOG_DEBUG, "sending close confirm for '%s'",
channel->channel_name);
error = dvcchannel_send_close(channel);
if (error != CHANNEL_RC_OK)
{
const char* msg = "error when sending close confirm for '%s'";
if (perRequest)
msg = "error when sending closeRequest for '%s'";
WLog_Print(drdynvc->log, WLOG_DEBUG, msg, channel->channel_name);
}
channel->state = DVC_CHANNEL_CLOSED;
if (channel->channel_callback)
{
IFCALL(channel->channel_callback->OnClose, channel->channel_callback);
channel->channel_callback = NULL;
}
if (channel->dvcman && channel->dvcman->drdynvc)
{
if (context)
{
IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
channel->pInterface);
}
}
dvcman_channel_unref(channel);
break;
case DVC_CHANNEL_CLOSED:
break;
}
return error;
}
static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
const char* ChannelName)
{
DVCMAN_CHANNEL* channel;
if (dvcman_find_channel_by_id(pChannelMgr, ChannelId))
{
WLog_Print(drdynvc->log, WLOG_ERROR,
"Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
ChannelName);
return NULL;
}
channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
if (!channel)
goto fail;
return NULL;
channel->dvcman = (DVCMAN*)pChannelMgr;
channel->channel_id = ChannelId;
channel->channel_name = _strdup(ChannelName);
channel->refCounter = 1;
channel->state = DVC_CHANNEL_INIT, channel->channel_name = _strdup(ChannelName);
if (!channel->channel_name)
goto fail;
@ -371,50 +466,6 @@ fail:
return NULL;
}
static void dvcman_channel_free(void* arg)
{
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)arg;
UINT error = CHANNEL_RC_OK;
if (channel)
{
if (channel->channel_callback)
{
IFCALL(channel->channel_callback->OnClose, channel->channel_callback);
channel->channel_callback = NULL;
}
if (channel->status == CHANNEL_RC_OK)
{
IWTSVirtualChannel* ichannel = (IWTSVirtualChannel*)channel;
if (channel->dvcman && channel->dvcman->drdynvc)
{
DrdynvcClientContext* context = channel->dvcman->drdynvc->context;
if (context)
{
IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
channel->pInterface);
}
}
error = IFCALLRESULT(CHANNEL_RC_OK, ichannel->Close, ichannel);
if (error != CHANNEL_RC_OK)
WLog_ERR(TAG, "Close failed with error %" PRIu32 "!", error);
}
if (channel->dvc_data)
Stream_Release(channel->dvc_data);
DeleteCriticalSection(&(channel->lock));
free(channel->channel_name);
}
free(channel);
}
static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
{
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
@ -493,7 +544,8 @@ static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, con
LeaveCriticalSection(&(channel->lock));
/* Close delayed, it removes the channel struct */
if (close)
dvcman_close_channel(channel->dvcman->drdynvc->channel_mgr, channel->channel_id, TRUE);
dvcman_channel_close(channel, FALSE);
return status;
}
@ -510,7 +562,7 @@ static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
return CHANNEL_RC_BAD_CHANNEL;
WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
return CHANNEL_RC_OK;
return dvcman_channel_close(channel, FALSE);
}
/**
@ -518,37 +570,64 @@ static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT dvcman_create_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId, const char* ChannelName)
static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId, const char* ChannelName, UINT* res)
{
BOOL bAccept;
DVCMAN_CHANNEL* channel;
DVCMAN_CHANNEL* channel = NULL;
DrdynvcClientContext* context;
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
DVCMAN_LISTENER* listener;
IWTSVirtualChannelCallback* pCallback = NULL;
UINT error;
WINPR_ASSERT(res);
HashTable_Lock(dvcman->listeners);
listener = (DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
if (!listener)
{
error = ERROR_NOT_FOUND;
*res = ERROR_NOT_FOUND;
goto out;
}
if (!(channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName)))
channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
if (channel)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
switch (channel->state)
{
case DVC_CHANNEL_RUNNING:
WLog_Print(drdynvc->log, WLOG_ERROR,
"Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
ChannelName);
*res = CHANNEL_RC_ALREADY_OPEN;
goto out;
case DVC_CHANNEL_CLOSED:
case DVC_CHANNEL_INIT:
default:
WLog_Print(drdynvc->log, WLOG_ERROR, "not expecting a createChannel from state %d",
channel->state);
*res = CHANNEL_RC_INITIALIZATION_ERROR;
goto out;
}
}
else
{
if (!(channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName)))
{
WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
*res = CHANNEL_RC_NO_MEMORY;
goto out;
}
}
channel->status = ERROR_NOT_CONNECTED;
if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
{
WLog_Print(drdynvc->log, WLOG_ERROR, "unable to register channel in our channel list");
error = ERROR_INTERNAL_ERROR;
*res = ERROR_INTERNAL_ERROR;
dvcman_channel_free(channel);
channel = NULL;
goto out;
}
@ -556,42 +635,45 @@ static UINT dvcman_create_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelMana
channel->iface.Close = dvcman_close_channel_iface;
bAccept = TRUE;
error = listener->listener_callback->OnNewChannelConnection(
*res = listener->listener_callback->OnNewChannelConnection(
listener->listener_callback, &channel->iface, NULL, &bAccept, &pCallback);
if (error != CHANNEL_RC_OK)
if (*res != CHANNEL_RC_OK)
{
WLog_Print(drdynvc->log, WLOG_ERROR,
"OnNewChannelConnection failed with error %" PRIu32 "!", error);
error = ERROR_INTERNAL_ERROR;
"OnNewChannelConnection failed with error %" PRIu32 "!", *res);
*res = ERROR_INTERNAL_ERROR;
dvcman_channel_unref(channel);
goto out;
}
if (!bAccept)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "OnNewChannelConnection returned with bAccept FALSE!");
error = ERROR_INTERNAL_ERROR;
*res = ERROR_INTERNAL_ERROR;
dvcman_channel_unref(channel);
channel = NULL;
goto out;
}
WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
listener->channel_name, channel->channel_id);
channel->status = CHANNEL_RC_OK;
channel->state = DVC_CHANNEL_RUNNING;
channel->channel_callback = pCallback;
channel->pInterface = listener->iface.pInterface;
context = dvcman->drdynvc->context;
IFCALLRET(context->OnChannelConnected, error, context, ChannelName, listener->iface.pInterface);
if (error != CHANNEL_RC_OK)
IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
if (*res != CHANNEL_RC_OK)
{
WLog_Print(drdynvc->log, WLOG_ERROR,
"context.OnChannelConnected failed with error %" PRIu32 "", error);
"context.OnChannelConnected failed with error %" PRIu32 "", *res);
}
out:
HashTable_Unlock(dvcman->listeners);
return error;
return channel;
}
/**
@ -599,21 +681,12 @@ out:
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId)
static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
{
DVCMAN_CHANNEL* channel;
IWTSVirtualChannelCallback* pCallback;
UINT error;
channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
UINT error = CHANNEL_RC_OK;
if (!channel)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
return ERROR_INTERNAL_ERROR;
}
if (channel->status == CHANNEL_RC_OK)
if (channel->state == DVC_CHANNEL_RUNNING)
{
pCallback = channel->channel_callback;
@ -624,57 +697,15 @@ static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelManage
{
WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
error);
return error;
goto out;
}
}
WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "", ChannelId);
WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "",
channel->channel_id);
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
BOOL bSendClosePDU)
{
DVCMAN_CHANNEL* channel;
UINT error = CHANNEL_RC_OK;
DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
drdynvcPlugin* drdynvc = dvcman->drdynvc;
channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (!channel)
{
// WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %"PRIu32" not found!", ChannelId);
/**
* Windows 8 / Windows Server 2012 send close requests for channels that failed to be
* created. Do not warn, simply return success here.
*/
return CHANNEL_RC_OK;
}
if (drdynvc && bSendClosePDU)
{
wStream* s = StreamPool_Take(dvcman->pool, 5);
if (!s)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
error = CHANNEL_RC_NO_MEMORY;
}
else
{
Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
Stream_Write_UINT32(s, ChannelId);
error = drdynvc_send(drdynvc, s);
}
}
HashTable_Remove(dvcman->channelsById, &ChannelId);
out:
return error;
}
@ -683,24 +714,8 @@ UINT dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 Channel
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT dvcman_receive_channel_data_first(drdynvcPlugin* drdynvc,
IWTSVirtualChannelManager* pChannelMgr,
UINT32 ChannelId, UINT32 length)
static UINT dvcman_receive_channel_data_first(DVCMAN_CHANNEL* channel, UINT32 length)
{
DVCMAN_CHANNEL* channel;
channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (!channel)
{
/**
* Windows Server 2012 R2 can send some messages over
* Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
* registered on our side. Ignoring it works.
*/
WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
return CHANNEL_RC_OK;
}
if (channel->dvc_data)
Stream_Release(channel->dvc_data);
@ -708,6 +723,7 @@ static UINT dvcman_receive_channel_data_first(drdynvcPlugin* drdynvc,
if (!channel->dvc_data)
{
drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
return CHANNEL_RC_NO_MEMORY;
}
@ -721,32 +737,24 @@ static UINT dvcman_receive_channel_data_first(drdynvcPlugin* drdynvc,
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
wStream* data, UINT32 ThreadingFlags)
static UINT dvcman_receive_channel_data(DVCMAN_CHANNEL* channel, wStream* data,
UINT32 ThreadingFlags)
{
UINT status = CHANNEL_RC_OK;
DVCMAN_CHANNEL* channel;
size_t dataSize = Stream_GetRemainingLength(data);
channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
if (!channel)
{
/* Windows 8.1 tries to open channels not created.
* Ignore cases like this. */
WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
return CHANNEL_RC_OK;
}
if (channel->dvc_data)
{
drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
/* Fragmented data */
if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
Stream_Release(channel->dvc_data);
channel->dvc_data = NULL;
return ERROR_INVALID_DATA;
status = ERROR_INVALID_DATA;
goto out;
}
Stream_Copy(data, channel->dvc_data, dataSize);
@ -766,6 +774,7 @@ static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
status = channel->channel_callback->OnDataReceived(channel->channel_callback, data);
}
out:
return status;
}
@ -871,6 +880,7 @@ static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const B
if (dataSize == 0)
{
/* TODO: shall treat that case with write(0) that do a close */
*close = TRUE;
Stream_Release(data_out);
}
@ -1062,6 +1072,7 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int c
char* name;
size_t length;
DVCMAN* dvcman;
DVCMAN_CHANNEL* channel;
UINT32 retStatus;
WINPR_UNUSED(Sp);
@ -1100,7 +1111,6 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int c
WLog_Print(drdynvc->log, WLOG_DEBUG,
"process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name);
data_out = StreamPool_Take(dvcman->pool, pos + 4);
if (!data_out)
@ -1113,6 +1123,8 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int c
Stream_SetPosition(s, 1);
Stream_Copy(s, data_out, pos - 1);
channel =
dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
switch (channel_status)
{
case CHANNEL_RC_OK:
@ -1135,29 +1147,23 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int c
Stream_Write_UINT32(data_out, retStatus);
status = drdynvc_send(drdynvc, data_out);
if (status != CHANNEL_RC_OK)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
WTSErrorToString(status), status);
dvcman_channel_unref(channel);
return status;
}
if (channel_status == CHANNEL_RC_OK)
{
if ((status = dvcman_open_channel(drdynvc, drdynvc->channel_mgr, ChannelId)))
if ((status = dvcman_open_channel(drdynvc, channel)))
{
WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_open_channel failed with error %" PRIu32 "!", status);
return status;
}
}
else
{
if ((status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, FALSE)))
WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_close_channel failed with error %" PRIu32 "!", status);
}
return status;
}
@ -1170,9 +1176,10 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int c
static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
UINT32 ThreadingFlags)
{
UINT status;
UINT status = CHANNEL_RC_OK;
UINT32 Length;
UINT32 ChannelId;
DVCMAN_CHANNEL* channel;
if (!Stream_CheckAndLogRequiredLength(
TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
@ -1183,15 +1190,32 @@ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChI
WLog_Print(drdynvc->log, WLOG_TRACE,
"process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
cbChId, ChannelId, Length);
status = dvcman_receive_channel_data_first(drdynvc, drdynvc->channel_mgr, ChannelId, Length);
channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
if (!channel)
{
/**
* Windows Server 2012 R2 can send some messages over
* Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
* registered on our side. Ignoring it works.
*/
WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
return CHANNEL_RC_OK;
}
if (channel->state != DVC_CHANNEL_RUNNING)
goto out;
status = dvcman_receive_channel_data_first(channel, Length);
if (status == CHANNEL_RC_OK)
status = dvcman_receive_channel_data(drdynvc, drdynvc->channel_mgr, ChannelId, s,
ThreadingFlags);
status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
if (status != CHANNEL_RC_OK)
status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE);
status = dvcman_channel_close(channel, FALSE);
out:
dvcman_channel_unref(channel);
return status;
}
@ -1204,7 +1228,8 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wSt
UINT32 ThreadingFlags)
{
UINT32 ChannelId;
UINT status;
DVCMAN_CHANNEL* channel;
UINT status = CHANNEL_RC_OK;
if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
return ERROR_INVALID_DATA;
@ -1212,12 +1237,28 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wSt
ChannelId = drdynvc_read_variable_uint(s, cbChId);
WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
cbChId, ChannelId);
status =
dvcman_receive_channel_data(drdynvc, drdynvc->channel_mgr, ChannelId, s, ThreadingFlags);
channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
if (!channel)
{
/**
* Windows Server 2012 R2 can send some messages over
* Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
* registered on our side. Ignoring it works.
*/
WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
return CHANNEL_RC_OK;
}
if (channel->state != DVC_CHANNEL_RUNNING)
goto out;
status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
if (status != CHANNEL_RC_OK)
status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE);
status = dvcman_channel_close(channel, FALSE);
out:
dvcman_channel_unref(channel);
return status;
}
@ -1228,8 +1269,8 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wSt
*/
static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
{
UINT error;
UINT32 ChannelId;
DVCMAN_CHANNEL* channel;
if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
return ERROR_INVALID_DATA;
@ -1239,11 +1280,17 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cb
"process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
ChannelId);
if ((error = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE)))
WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_channel failed with error %" PRIu32 "!",
error);
channel = (DVCMAN_CHANNEL*)dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
if (!channel)
{
WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
ChannelId);
return CHANNEL_RC_OK;
}
return error;
dvcman_channel_close(channel, TRUE);
dvcman_channel_unref(channel);
return CHANNEL_RC_OK;
}
/**
@ -1413,10 +1460,9 @@ static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam,
static BOOL channelByIdCleanerFn(const void* key, void* value, void* arg)
{
drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)value;
dvcman_close_channel(drdynvc->channel_mgr, channel->channel_id, FALSE);
dvcman_channel_close(channel, FALSE);
return TRUE;
}

View File

@ -70,11 +70,19 @@ typedef struct
rdpContext* context;
} DVCMAN_ENTRY_POINTS;
typedef enum
{
DVC_CHANNEL_INIT,
DVC_CHANNEL_RUNNING,
DVC_CHANNEL_CLOSED
} DVC_CHANNEL_STATE;
typedef struct
{
IWTSVirtualChannel iface;
int status;
volatile LONG refCounter;
DVC_CHANNEL_STATE state;
DVCMAN* dvcman;
void* pInterface;
UINT32 channel_id;