63afb676e6
The dynamic channel handler in xrdp_channel.c is updated to allow the procs `data_first` pointer to be NULL. If this is done, the channel handler performs all the dechunking necessary for the channel, and only complete data PDUs are passed to procs 'data' callback. This facility is applied to the dynamic channels supported by xrdp_mm.c. The incoming callbacks for these channels now provide complete support for the specification in [MS-RDPEDYC]. The existing channels were incomplete in these respects: 1) The "Microsoft::Windows::RDS::Graphics" channel handler did not support incoming PDUs between 1591 and 1600 bytes. The specification calls for these to be sent as a single DATA_FIRST PDU. 2) The "Microsoft::Windows::RDS::DisplayControl" channel handler did not support incoming PDUs over 1590 bytes.
1220 lines
37 KiB
C
1220 lines
37 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2006-2013
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* channel layer
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include "libxrdp.h"
|
|
#include "dechunker.h"
|
|
#include "string_calls.h"
|
|
#include "xrdp_channel.h"
|
|
|
|
#define CMD_DVC_OPEN_CHANNEL 0x10
|
|
#define CMD_DVC_DATA_FIRST 0x20
|
|
#define CMD_DVC_DATA 0x30
|
|
#define CMD_DVC_CLOSE_CHANNEL 0x40
|
|
#define CMD_DVC_CAPABILITY 0x50
|
|
|
|
#define XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id) \
|
|
(xrdp_channel_get_item((self), (chan_id)) != NULL \
|
|
? xrdp_channel_get_item((self), (chan_id))->name \
|
|
: "unknown")
|
|
|
|
/*****************************************************************************/
|
|
/* returns pointer or nil on error */
|
|
static struct mcs_channel_item *
|
|
xrdp_channel_get_item(struct xrdp_channel *self, int channel_id)
|
|
{
|
|
struct mcs_channel_item *channel;
|
|
|
|
if (self->mcs_layer->channel_list == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Channel list is NULL, returning NULL");
|
|
return NULL ;
|
|
}
|
|
|
|
channel = (struct mcs_channel_item *)
|
|
list_get_item(self->mcs_layer->channel_list, channel_id);
|
|
return channel;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
struct xrdp_channel *
|
|
xrdp_channel_create(struct xrdp_sec *owner, struct xrdp_mcs *mcs_layer)
|
|
{
|
|
struct xrdp_channel *self;
|
|
|
|
self = (struct xrdp_channel *)g_malloc(sizeof(struct xrdp_channel), 1);
|
|
self->sec_layer = owner;
|
|
self->mcs_layer = mcs_layer;
|
|
self->drdynvc_channel_id = -1;
|
|
return self;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
void
|
|
xrdp_channel_delete(struct xrdp_channel *self)
|
|
{
|
|
int i;
|
|
if (self == 0)
|
|
{
|
|
return;
|
|
}
|
|
vc_dechunker_free(self->drdynvc_dc);
|
|
for (i = 0 ; i < DRDYNVC_CHANNEL_COUNT ; ++i)
|
|
{
|
|
dyn_dechunker_free(self->drdynvcs[i].dc);
|
|
}
|
|
g_memset(self, 0, sizeof(struct xrdp_channel));
|
|
g_free(self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
int
|
|
xrdp_channel_init(struct xrdp_channel *self, struct stream *s)
|
|
{
|
|
if (xrdp_sec_init(self->sec_layer, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_channel_init: xrdp_sec_init failed");
|
|
return 1;
|
|
}
|
|
|
|
s_push_layer(s, channel_hdr, 8);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
/* This sends data out to the secure layer. */
|
|
int
|
|
xrdp_channel_send(struct xrdp_channel *self, struct stream *s, int channel_id,
|
|
int total_data_len, int flags)
|
|
{
|
|
struct mcs_channel_item *channel;
|
|
|
|
channel = xrdp_channel_get_item(self, channel_id);
|
|
|
|
if (channel == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Request to send a message to non-existent channel_id %d",
|
|
channel_id);
|
|
return 1;
|
|
}
|
|
|
|
if (channel->disabled)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"Request to send a message to the disabled channel %s (%d)",
|
|
channel->name, channel_id);
|
|
return 0; /* not an error */
|
|
}
|
|
|
|
s_pop_layer(s, channel_hdr);
|
|
out_uint32_le(s, total_data_len);
|
|
|
|
/*
|
|
* According to 2.2.1.3.4.1 Channel Definition Structure (CHANNEL_DEF):
|
|
* CHANNEL_OPTION_SHOW_PROTOCOL 0x00200000
|
|
* The value of this flag MUST be ignored by the server. The
|
|
* visibility of the Channel PDU Header (section 2.2.6.1.1) is
|
|
* determined by the CHANNEL_FLAG_SHOW_PROTOCOL
|
|
* (0x00000010) flag as defined in the flags field (section
|
|
* 2.2.6.1.1).
|
|
*
|
|
* That's flag makes MSTSC crash when using RAIL channel.
|
|
*/
|
|
// if (channel->flags & XR_CHANNEL_OPTION_SHOW_PROTOCOL)
|
|
// {
|
|
// flags |= CHANNEL_FLAG_SHOW_PROTOCOL;
|
|
// }
|
|
|
|
out_uint32_le(s, flags);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Adding header [MS-RDPBCGR] CHANNEL_PDU_HEADER "
|
|
"length %d, flags 0x%8.8x", total_data_len, flags);
|
|
|
|
if (xrdp_sec_send(self->sec_layer, s, channel->chanid) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "xrdp_channel_send: xrdp_sec_send failed");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* returns error */
|
|
/* this will inform the callback, whatever it is that some channel data is
|
|
ready. the default for this is a call to xrdp_wm.c. */
|
|
static int
|
|
xrdp_channel_call_callback(struct xrdp_channel *self, struct stream *s,
|
|
int channel_id,
|
|
int total_data_len, int flags)
|
|
{
|
|
struct xrdp_session *session;
|
|
int rv;
|
|
int size;
|
|
|
|
rv = 0;
|
|
session = self->sec_layer->rdp_layer->session;
|
|
|
|
if (session != 0)
|
|
{
|
|
if (session->callback != 0)
|
|
{
|
|
size = (int)(s->end - s->p);
|
|
/* in xrdp_wm.c */
|
|
rv = session->callback(session->id, 0x5555,
|
|
MAKELONG(channel_id, flags),
|
|
size, (tbus)(s->p), total_data_len);
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "session->callback is NULL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "session is NULL");
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Write a variable length unsigned int (1, 2, or 4 bytes) to the stream.
|
|
*
|
|
* The number of bytes written is the minimum number of bytes needed to
|
|
* represent the value.
|
|
*
|
|
* @param s the stream to write to
|
|
* @param val the value to write
|
|
* @return the DYNVC cbId length code for the number of bytes written (see [MS-RDPEDYC] 2.2.2.1)
|
|
*/
|
|
static int
|
|
drdynvc_insert_uint_124(struct stream *s, uint32_t val)
|
|
{
|
|
int ret_val;
|
|
|
|
if (val <= 0xff)
|
|
{
|
|
out_uint8(s, val);
|
|
ret_val = 0;
|
|
}
|
|
else if (val <= 0xffff)
|
|
{
|
|
out_uint16_le(s, val);
|
|
ret_val = 1;
|
|
}
|
|
else
|
|
{
|
|
out_uint32_le(s, val);
|
|
ret_val = 2;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Read a variable length unsigned int (1, 2, or 4 bytes) from the stream.
|
|
*
|
|
* The number of bytes read is determined by the cbId bit field flag in the
|
|
* cmd argument (see [MS-RDPEDYC] 2.2.2.1).
|
|
*
|
|
* @param s [in] the stream to read from
|
|
* @param cmd [in] the cmd byte which contains the cbId bit field flag
|
|
* @param chan_id_p [out] a pointer to the value read from the stream
|
|
* @return error code
|
|
*/
|
|
static int
|
|
drdynvc_get_chan_id(struct stream *s, char cmd, uint32_t *chan_id_p)
|
|
{
|
|
int cbChId;
|
|
int chan_id;
|
|
|
|
cbChId = cmd & 0x03;
|
|
if (cbChId == 0)
|
|
{
|
|
if (!s_check_rem_and_log(s, 1, "Parsing [MS-RDPEDYC] channel id"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8(s, chan_id);
|
|
}
|
|
else if (cbChId == 1)
|
|
{
|
|
if (!s_check_rem_and_log(s, 2, "Parsing [MS-RDPEDYC] channel id"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint16_le(s, chan_id);
|
|
}
|
|
else
|
|
{
|
|
if (!s_check_rem_and_log(s, 4, "Parsing [MS-RDPEDYC] channel id"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint32_le(s, chan_id);
|
|
}
|
|
*chan_id_p = chan_id;
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Process a [MS-RDPEDYC] DYNVC_CAPS_RSP message.
|
|
*/
|
|
static int
|
|
drdynvc_process_capability_response(struct xrdp_channel *self,
|
|
int cmd, struct stream *s)
|
|
{
|
|
struct xrdp_session *session;
|
|
int cap_version;
|
|
int rv;
|
|
|
|
if (!s_check_rem_and_log(s, 3, "Parsing [MS-RDPEDYC] DYNVC_CAPS_RSP"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint8s(s, 1); /* skip padding */
|
|
in_uint16_le(s, cap_version); /* Version */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPEDYC] DYNVC_CAPS_RSP "
|
|
"version %d", cap_version);
|
|
|
|
if ((cap_version != 2) && (cap_version != 3))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Dynamic Virtual Channel version %d is not supported",
|
|
cap_version);
|
|
return 1;
|
|
}
|
|
self->drdynvc_state = 1;
|
|
session = self->sec_layer->rdp_layer->session;
|
|
rv = session->callback(session->id, 0x5558, 0, 0, 0, 0);
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Process a [MS-RDPEDYC] DYNVC_CREATE_RSP message.
|
|
*/
|
|
static int
|
|
drdynvc_process_open_channel_response(struct xrdp_channel *self,
|
|
int cmd, struct stream *s)
|
|
{
|
|
struct xrdp_session *session;
|
|
int creation_status;
|
|
uint32_t chan_id;
|
|
struct xrdp_drdynvc *drdynvc;
|
|
|
|
if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) /* ChannelId */
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [MS-RDPEDYC] DYNVC_CREATE_RSP failed");
|
|
return 1;
|
|
}
|
|
if (!s_check_rem_and_log(s, 4, "Parsing [MS-RDPEDYC] DYNVC_CREATE_RSP"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint32_le(s, creation_status); /* CreationStatus */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPEDYC] DYNVC_CREATE_RSP "
|
|
"ChannelId %d, CreationStatus %d", chan_id, creation_status);
|
|
if (chan_id >= DRDYNVC_CHANNEL_COUNT)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Received [MS-RDPEDYC] DYNVC_CREATE_RSP "
|
|
"for an invalid channel id %d", chan_id);
|
|
return 1;
|
|
}
|
|
|
|
session = self->sec_layer->rdp_layer->session;
|
|
drdynvc = self->drdynvcs + chan_id;
|
|
if (creation_status == 0)
|
|
{
|
|
drdynvc->status = XRDP_DRDYNVC_STATUS_OPEN;
|
|
}
|
|
else
|
|
{
|
|
drdynvc->status = XRDP_DRDYNVC_STATUS_CLOSED;
|
|
dyn_dechunker_free(drdynvc->dc);
|
|
drdynvc->dc = NULL;
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG,
|
|
"Dynamic Virtual Channel %s (%d) updated: status = %s",
|
|
XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id),
|
|
chan_id,
|
|
XRDP_DRDYNVC_STATUS_TO_STR(drdynvc->status));
|
|
if (drdynvc->open_response != NULL)
|
|
{
|
|
return drdynvc->open_response(session->id, chan_id, creation_status);
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "Dynamic Virtual Channel %s (%d): "
|
|
"callback 'open_response' is NULL",
|
|
XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id),
|
|
chan_id);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Process a [MS-RDPEDYC] DYNVC_CLOSE message.
|
|
*/
|
|
static int
|
|
drdynvc_process_close_channel_response(struct xrdp_channel *self,
|
|
int cmd, struct stream *s)
|
|
{
|
|
struct xrdp_session *session;
|
|
uint32_t chan_id;
|
|
struct xrdp_drdynvc *drdynvc;
|
|
|
|
if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) /* ChannelId */
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"drdynvc_process_close_channel_response: drdynvc_get_chan_id failed");
|
|
return 1;
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPEDYC] DYNVC_CLOSE "
|
|
"ChannelId %d", chan_id);
|
|
session = self->sec_layer->rdp_layer->session;
|
|
if (chan_id >= DRDYNVC_CHANNEL_COUNT)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Received message for an invalid "
|
|
"channel id %d", chan_id);
|
|
return 1;
|
|
}
|
|
|
|
drdynvc = self->drdynvcs + chan_id;
|
|
|
|
if (dyn_dechunker_pending(drdynvc->dc))
|
|
{
|
|
// The last PDU wasn't completed
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Dynamic Virtual Channel %s (%d) closing with outstanding data",
|
|
XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id),
|
|
chan_id);
|
|
}
|
|
|
|
drdynvc->status = XRDP_DRDYNVC_STATUS_CLOSED;
|
|
dyn_dechunker_free(drdynvc->dc);
|
|
drdynvc->dc = NULL;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG,
|
|
"Dynamic Virtual Channel %s (%d) updated: status = %s",
|
|
XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id),
|
|
chan_id,
|
|
XRDP_DRDYNVC_STATUS_TO_STR(drdynvc->status));
|
|
|
|
if (drdynvc->close_response != NULL)
|
|
{
|
|
return drdynvc->close_response(session->id, chan_id);
|
|
}
|
|
LOG_DEVEL(LOG_LEVEL_WARNING, "Dynamic Virtual Channel %s (%d): "
|
|
"callback 'close_response' is NULL",
|
|
XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id),
|
|
chan_id);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Process a [MS-RDPEDYC] DYNVC_DATA_FIRST message.
|
|
*/
|
|
static int
|
|
drdynvc_process_data_first(struct xrdp_channel *self,
|
|
int cmd, struct stream *s)
|
|
{
|
|
uint32_t chan_id;
|
|
int rv = 0;
|
|
if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) /* ChannelId */
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [MS-RDPEDYC] DYNVC_DATA_FIRST channel ID failed");
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
int len = (cmd >> 2) & 0x03;
|
|
int total_bytes;
|
|
switch (len)
|
|
{
|
|
case 0:
|
|
// Technically this can't happen as DATA_FIRST is only used for
|
|
// PDUs over 1590 bytes ([MS-RDPEDYC] 2.2.3)
|
|
if (!s_check_rem(s, 1))
|
|
{
|
|
goto short_pdu;
|
|
}
|
|
in_uint8(s, total_bytes); /* Length */
|
|
break;
|
|
|
|
case 1:
|
|
if (!s_check_rem(s, 2))
|
|
{
|
|
goto short_pdu;
|
|
}
|
|
in_uint16_le(s, total_bytes); /* Length */
|
|
break;
|
|
|
|
case 2:
|
|
if (!s_check_rem(s, 4))
|
|
{
|
|
goto short_pdu;
|
|
}
|
|
in_uint32_le(s, total_bytes); /* Length */
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"[MS-RDPEDYC] DYNVC_DATA_FIRST has bad Len field");
|
|
return 1;
|
|
}
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPEDYC] DYNVC_DATA_FIRST "
|
|
"ChannelId %d, Length %d, Data (omitted from the log)",
|
|
chan_id, total_bytes);
|
|
|
|
if (chan_id >= DRDYNVC_CHANNEL_COUNT)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Received [MS-RDPEDYC] DYNVC_DATA_FIRST "
|
|
"for an invalid channel id %d", chan_id);
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
struct xrdp_drdynvc *drdynvc = self->drdynvcs + chan_id;
|
|
|
|
if (drdynvc->data_first != NULL)
|
|
{
|
|
// Caller has requested to defragment PDUs themself
|
|
struct xrdp_session *session =
|
|
self->sec_layer->rdp_layer->session;
|
|
rv = drdynvc->data_first(session->id, chan_id, s, total_bytes);
|
|
}
|
|
else
|
|
{
|
|
// Get the dechunker working on the stream
|
|
enum dyn_dechunker_status status;
|
|
status = dyn_dechunker_process_first_chunk(drdynvc->dc,
|
|
s, total_bytes);
|
|
switch (status)
|
|
{
|
|
case E_DYN_INLINE_CHUNK:
|
|
// Pass through as data PDU
|
|
if (drdynvc->data != NULL)
|
|
{
|
|
struct xrdp_session *session =
|
|
self->sec_layer->rdp_layer->session;
|
|
rv = drdynvc->data(session->id, chan_id, s);
|
|
}
|
|
break;
|
|
|
|
case E_DYN_IN_PROGRESS:
|
|
break;
|
|
|
|
case E_DYN_ERROR:
|
|
rv = 1;
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Dechunker returned unknown error %d",
|
|
(int)status);
|
|
rv = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
|
|
short_pdu:
|
|
LOG(LOG_LEVEL_ERROR, "[MS-RDPEDYC] DYNVC_DATA_FIRST is too short");
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Process a [MS-RDPEDYC] DYNVC_DATA message.
|
|
*/
|
|
static int
|
|
drdynvc_process_data(struct xrdp_channel *self,
|
|
int cmd, struct stream *s)
|
|
{
|
|
int rv = 0;
|
|
uint32_t chan_id;
|
|
|
|
if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) /* ChannelId */
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Parsing [MS-RDPEDYC] DYNVC_DATA channel ID failed");
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received [MS-RDPEDYC] DYNVC_DATA "
|
|
"ChannelId %d, Length %d, Data (omitted from the log)",
|
|
chan_id, s_rem(s));
|
|
if (chan_id >= DRDYNVC_CHANNEL_COUNT)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Received DYNVC_DATA PDU for an invalid "
|
|
"channel id %d", chan_id);
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
struct stream *ls = NULL; // Set if the application to be called
|
|
int free_ls = 0; // Set if we need to clear ls when we're done
|
|
struct xrdp_drdynvc *drdynvc = self->drdynvcs + chan_id;
|
|
if (drdynvc->data_first != NULL)
|
|
{
|
|
// Caller is processing all PDUs directly
|
|
ls = s;
|
|
}
|
|
else
|
|
{
|
|
// Pass the PDU to the dechunker
|
|
enum dyn_dechunker_status dechunker_status =
|
|
dyn_dechunker_process_data_chunk(drdynvc->dc, s);
|
|
switch (dechunker_status)
|
|
{
|
|
case E_DYN_INLINE_CHUNK:
|
|
ls = s;
|
|
break;
|
|
|
|
case E_DYN_IN_PROGRESS:
|
|
break;
|
|
|
|
case E_DYN_READY:
|
|
ls = dyn_dechunker_get_stream(drdynvc->dc);
|
|
// We now own the stream, so must delete it
|
|
free_ls = 1;
|
|
break;
|
|
|
|
case E_DYN_ERROR:
|
|
// Error has been logged
|
|
rv = 1;
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Dechunker returned unknown error %d",
|
|
(int)dechunker_status);
|
|
rv = 1;
|
|
}
|
|
}
|
|
|
|
if (ls != NULL)
|
|
{
|
|
if (drdynvc->data != NULL)
|
|
{
|
|
struct xrdp_session *session =
|
|
self->sec_layer->rdp_layer->session;
|
|
rv = drdynvc->data(session->id, chan_id, ls);
|
|
}
|
|
|
|
if (free_ls)
|
|
{
|
|
free_stream(ls);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Process a [MS-RDPBCGR] 2.2.6.1 Virtual Channel PDU and re-assemble the
|
|
* data chunks as needed.
|
|
*/
|
|
static int
|
|
xrdp_channel_process_drdynvc(struct xrdp_channel *self,
|
|
struct mcs_channel_item *channel,
|
|
struct stream *s)
|
|
{
|
|
int total_length;
|
|
int flags;
|
|
int cmd;
|
|
int rv;
|
|
struct stream *ls = NULL;
|
|
int free_ls = 0;
|
|
enum vc_dechunker_status dechunker_status;
|
|
|
|
if (!s_check_rem_and_log(s, 8, "Parsing [MS-RDPBCGR] CHANNEL_PDU_HEADER"))
|
|
{
|
|
return 1;
|
|
}
|
|
in_uint32_le(s, total_length); /* length */
|
|
in_uint32_le(s, flags); /* flags */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [MS-RDPBCGR] CHANNEL_PDU_HEADER "
|
|
"length %d, flags 0x%8.8x", total_length, flags);
|
|
dechunker_status = vc_dechunker_process_chunk(
|
|
self->drdynvc_dc,
|
|
s, flags, total_length);
|
|
switch (dechunker_status)
|
|
{
|
|
case E_VC_INLINE_CHUNK:
|
|
ls = s;
|
|
break;
|
|
|
|
case E_VC_IN_PROGRESS:
|
|
return 0;
|
|
break;
|
|
|
|
case E_VC_READY:
|
|
ls = vc_dechunker_get_stream(self->drdynvc_dc);
|
|
// We now own the stream, so must delete it
|
|
free_ls = 1;
|
|
break;
|
|
|
|
case E_VC_ERROR:
|
|
// Error has been logged
|
|
return 1;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Dechunker returned unknown error %d",
|
|
(int)dechunker_status);
|
|
return 1;
|
|
}
|
|
if (ls == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "BUG: ls must not be NULL");
|
|
return 1;
|
|
}
|
|
in_uint8(ls, cmd); /* cbId (low 2 bits), Sp (2 bits), Cmd (hi 4 bits) */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [MS-RDPEDYC] "
|
|
"cbId %d, Sp %d, Cmd 0x%2.2x",
|
|
(cmd & 0x03), (cmd & 0x0c) >> 2, (cmd & 0xf0) >> 4);
|
|
rv = 1;
|
|
switch (cmd & 0xf0)
|
|
{
|
|
case CMD_DVC_CAPABILITY:
|
|
rv = drdynvc_process_capability_response(self, cmd, ls);
|
|
break;
|
|
case CMD_DVC_OPEN_CHANNEL:
|
|
rv = drdynvc_process_open_channel_response(self, cmd, ls);
|
|
break;
|
|
case CMD_DVC_CLOSE_CHANNEL:
|
|
rv = drdynvc_process_close_channel_response(self, cmd, ls);
|
|
break;
|
|
case CMD_DVC_DATA_FIRST:
|
|
rv = drdynvc_process_data_first(self, cmd, ls);
|
|
break;
|
|
case CMD_DVC_DATA:
|
|
rv = drdynvc_process_data(self, cmd, ls);
|
|
break;
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Received header [MS-RDPEDYC] with "
|
|
"unknown command 0x%2.2x", cmd);
|
|
break;
|
|
}
|
|
if (free_ls)
|
|
{
|
|
free_stream(ls);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Process a static ([MS-RDPBCGR] 2.2.6) or dynamic (MS-RDPEDYC 2.2.3)
|
|
* virtual channel message.
|
|
* returns error */
|
|
/* This is called from the secure layer to process an incoming non global
|
|
channel packet.
|
|
'chanid' passed in here is the mcs channel id so it MCS_GLOBAL_CHANNEL
|
|
plus something. */
|
|
int
|
|
xrdp_channel_process(struct xrdp_channel *self, struct stream *s,
|
|
int chanid)
|
|
{
|
|
int length;
|
|
int flags;
|
|
int rv;
|
|
int channel_id;
|
|
struct mcs_channel_item *channel;
|
|
|
|
/* this assumes that the channels are in order of chanid(mcs channel id)
|
|
but they should be, see xrdp_sec_process_mcs_data_channels
|
|
the first channel should be MCS_GLOBAL_CHANNEL + 1, second
|
|
one should be MCS_GLOBAL_CHANNEL + 2, and so on */
|
|
channel_id = (chanid - MCS_GLOBAL_CHANNEL) - 1;
|
|
channel = xrdp_channel_get_item(self, channel_id);
|
|
if (channel == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Received a message for an unknown channel id. channel id %d",
|
|
chanid);
|
|
return 1;
|
|
}
|
|
if (channel->disabled)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Received a message for the disabled channel %s (%d)",
|
|
channel->name, chanid);
|
|
return 0; /* not an error */
|
|
}
|
|
if (channel_id == self->drdynvc_channel_id)
|
|
{
|
|
return xrdp_channel_process_drdynvc(self, channel, s);
|
|
}
|
|
rv = 0;
|
|
in_uint32_le(s, length); /* length */
|
|
in_uint32_le(s, flags); /* flags */
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Received header [MS-RDPBCGR] CHANNEL_PDU_HEADER "
|
|
"length %d, flags 0x%8.8x", length, flags);
|
|
rv = xrdp_channel_call_callback(self, s, channel_id, length, flags);
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Send a [MS-RDPEDYC] DYNVC_CAPS_VERSION2 message */
|
|
static int
|
|
xrdp_channel_drdynvc_send_capability_request(struct xrdp_channel *self)
|
|
{
|
|
struct stream *s;
|
|
int flags;
|
|
int total_data_len;
|
|
int channel_id;
|
|
char *phold;
|
|
|
|
/* setup stream */
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
if (xrdp_channel_init(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_send_capability_request: xrdp_channel_init failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
phold = s->p;
|
|
out_uint8(s, 0x50); /* insert cbId (2 bits), Sp (2 bits), cmd (4 bits) */
|
|
out_uint8(s, 0x00); /* insert padding */
|
|
out_uint16_le(s, 2); /* insert version */
|
|
/* channel priority unused for now */
|
|
out_uint16_le(s, 0x0000); /* priority charge 0 */
|
|
out_uint16_le(s, 0x0000); /* priority charge 1 */
|
|
out_uint16_le(s, 0x0000); /* priority charge 2 */
|
|
out_uint16_le(s, 0x0000); /* priority charge 3 */
|
|
s_mark_end(s);
|
|
/* send command to client */
|
|
total_data_len = (int) (s->end - phold);
|
|
flags = XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST;
|
|
channel_id = self->drdynvc_channel_id;
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_CAPS_VERSION2 "
|
|
"cbId 0, Sp 0, Cmd 0x05, Version 2, PriorityCharge0 0, "
|
|
"PriorityCharge1 0, PriorityCharge2 0, PriorityCharge3 0");
|
|
if (xrdp_channel_send(self, s, channel_id, total_data_len, flags) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_send_capability_request: xrdp_channel_send failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
int
|
|
xrdp_channel_drdynvc_start(struct xrdp_channel *self)
|
|
{
|
|
int rv = 0;
|
|
LOG_DEVEL(LOG_LEVEL_INFO,
|
|
"xrdp_channel_drdynvc_start: drdynvc_channel_id %d",
|
|
self->drdynvc_channel_id);
|
|
if (self->drdynvc_channel_id != -1)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_INFO,
|
|
"xrdp_channel_drdynvc_start: already started");
|
|
}
|
|
else
|
|
{
|
|
int index;
|
|
int count;
|
|
struct mcs_channel_item *ci;
|
|
struct mcs_channel_item *dci;
|
|
dci = NULL;
|
|
count = self->mcs_layer->channel_list->count;
|
|
for (index = 0; index < count; index++)
|
|
{
|
|
ci = (struct mcs_channel_item *)
|
|
list_get_item(self->mcs_layer->channel_list, index);
|
|
if (ci != NULL)
|
|
{
|
|
if (g_strcasecmp(ci->name, DRDYNVC_SVC_CHANNEL_NAME) == 0)
|
|
{
|
|
dci = ci;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dci == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Static channel '%s' not found.",
|
|
DRDYNVC_SVC_CHANNEL_NAME);
|
|
rv = -1;
|
|
}
|
|
else if (dci->disabled)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Static channel '%s' is disabled.",
|
|
DRDYNVC_SVC_CHANNEL_NAME);
|
|
rv = -1;
|
|
}
|
|
else if ((self->drdynvc_dc =
|
|
vc_dechunker_init(DRDYNVC_SVC_CHANNEL_NAME,
|
|
CHANNEL_CHUNK_LENGTH)) == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "No memory");
|
|
rv = -1;
|
|
}
|
|
else
|
|
{
|
|
self->drdynvc_channel_id = (dci->chanid - MCS_GLOBAL_CHANNEL) - 1;
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, DRDYNVC_SVC_CHANNEL_NAME
|
|
"Initializing Dynamic Virtual Channel with channel id %d",
|
|
self->drdynvc_channel_id);
|
|
xrdp_channel_drdynvc_send_capability_request(self);
|
|
}
|
|
}
|
|
|
|
if (rv != 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Dynamic channels will not be available");
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Send a [MS-RDPEDYC] DYNVC_CREATE_REQ message to request the creation of a channel.
|
|
*/
|
|
int
|
|
xrdp_channel_drdynvc_open(struct xrdp_channel *self, const char *name,
|
|
int flags, struct xrdp_drdynvc_procs *procs,
|
|
int *chan_id)
|
|
{
|
|
struct stream *s;
|
|
int ChId;
|
|
int cbChId;
|
|
int chan_pri;
|
|
int static_channel_id;
|
|
int name_length;
|
|
int total_data_len;
|
|
int static_flags;
|
|
char *cmd_ptr;
|
|
struct dyn_dechunker *dc = NULL;
|
|
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
if (xrdp_channel_init(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_open: xrdp_channel_init failed");
|
|
goto cleanup;
|
|
}
|
|
|
|
// If there's no 'data_first' proc, we need a dechunker for the
|
|
// channel
|
|
if (procs->data_first == NULL)
|
|
{
|
|
if ((dc = dyn_dechunker_init(name)) == NULL)
|
|
{
|
|
// Error logged
|
|
goto cleanup;
|
|
}
|
|
}
|
|
cmd_ptr = s->p;
|
|
out_uint8(s, 0); /* set later */
|
|
ChId = 1;
|
|
while (self->drdynvcs[ChId].status != XRDP_DRDYNVC_STATUS_CLOSED)
|
|
{
|
|
ChId++;
|
|
if (ChId >= DRDYNVC_CHANNEL_COUNT)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Attempting to create a new channel when the maximum "
|
|
"number of channels have already been created.");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
cbChId = drdynvc_insert_uint_124(s, ChId); /* ChannelId */
|
|
name_length = g_strlen(name);
|
|
out_uint8a(s, name, name_length + 1); /* ChannelName */
|
|
chan_pri = 0;
|
|
/* cbId (low 2 bits), Pri (2 bits), Cmd (hi 4 bits) */
|
|
cmd_ptr[0] = CMD_DVC_OPEN_CHANNEL | ((chan_pri << 2) & 0x0c) | cbChId;
|
|
static_channel_id = self->drdynvc_channel_id;
|
|
static_flags = XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST;
|
|
s_mark_end(s);
|
|
total_data_len = (int) (s->end - cmd_ptr);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_CREATE_REQ "
|
|
"cbId %d, Pri %d, Cmd 0x%2.2x, ChannelId %d, ChannelName [%s]",
|
|
cbChId, chan_pri, CMD_DVC_OPEN_CHANNEL, ChId, name);
|
|
if (xrdp_channel_send(self, s, static_channel_id, total_data_len,
|
|
static_flags) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Sending [MS-RDPEDYC] DYNVC_CREATE_REQ failed");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (procs->data == NULL)
|
|
{
|
|
// Not expected. Log this once.
|
|
LOG(LOG_LEVEL_WARNING, "Dynamic Virtual Channel %s (%d): "
|
|
"callback 'data' is NULL", name, ChId);
|
|
}
|
|
|
|
*chan_id = ChId;
|
|
self->drdynvcs[ChId].open_response = procs->open_response;
|
|
self->drdynvcs[ChId].close_response = procs->close_response;
|
|
self->drdynvcs[ChId].data_first = procs->data_first;
|
|
self->drdynvcs[ChId].data = procs->data;
|
|
self->drdynvcs[ChId].status = XRDP_DRDYNVC_STATUS_OPEN_SENT;
|
|
self->drdynvcs[ChId].dc = dc;
|
|
free_stream(s);
|
|
return 0;
|
|
|
|
cleanup:
|
|
free_stream(s);
|
|
dyn_dechunker_free(dc);
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Send a [MS-RDPEDYC] DYNVC_CLOSE message to request the closing of a channel.
|
|
*/
|
|
int
|
|
xrdp_channel_drdynvc_close(struct xrdp_channel *self, int chan_id)
|
|
{
|
|
struct stream *s;
|
|
int ChId;
|
|
int cbChId;
|
|
int static_channel_id;
|
|
int total_data_len;
|
|
int static_flags;
|
|
char *cmd_ptr;
|
|
|
|
if ((chan_id < 0) || (chan_id >= DRDYNVC_CHANNEL_COUNT))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to close an invalid "
|
|
"channel id %d", chan_id);
|
|
return 1;
|
|
}
|
|
if ((self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN) &&
|
|
(self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN_SENT))
|
|
{
|
|
/* not open */
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to close a channel that is not open. "
|
|
"channel id %d, channel status %s",
|
|
chan_id,
|
|
XRDP_DRDYNVC_STATUS_TO_STR(self->drdynvcs[chan_id].status));
|
|
return 1;
|
|
}
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
if (xrdp_channel_init(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_close: xrdp_channel_init failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
cmd_ptr = s->p;
|
|
out_uint8(s, 0); /* set later */
|
|
ChId = chan_id;
|
|
cbChId = drdynvc_insert_uint_124(s, ChId); /* ChannelId */
|
|
/* cbId (low 2 bits), Sp (2 bits), Cmd (hi 4 bits) */
|
|
cmd_ptr[0] = CMD_DVC_CLOSE_CHANNEL | cbChId;
|
|
static_channel_id = self->drdynvc_channel_id;
|
|
static_flags = XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST;
|
|
s_mark_end(s);
|
|
total_data_len = (int) (s->end - cmd_ptr);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_CLOSE "
|
|
"cbId %d, Sp 0, Cmd 0x%2.2x, ChannelId %d",
|
|
cbChId, CMD_DVC_CLOSE_CHANNEL, ChId);
|
|
if (xrdp_channel_send(self, s, static_channel_id, total_data_len,
|
|
static_flags) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_open: xrdp_channel_send failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
free_stream(s);
|
|
self->drdynvcs[ChId].status = XRDP_DRDYNVC_STATUS_CLOSE_SENT;
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Send a [MS-RDPEDYC] DYNVC_DATA_FIRST message.
|
|
*/
|
|
int
|
|
xrdp_channel_drdynvc_data_first(struct xrdp_channel *self, int chan_id,
|
|
const char *data, int data_bytes,
|
|
int total_data_bytes)
|
|
{
|
|
struct stream *s;
|
|
int ChId;
|
|
int cbChId;
|
|
int cbTotalDataSize;
|
|
int static_channel_id;
|
|
int total_data_len;
|
|
int static_flags;
|
|
char *cmd_ptr;
|
|
|
|
if ((chan_id < 0) || (chan_id >= DRDYNVC_CHANNEL_COUNT))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to send data to an invalid "
|
|
"channel id %d", chan_id);
|
|
return 1;
|
|
}
|
|
if (self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to send data to a channel that "
|
|
"is not open. channel id %d, channel status %s",
|
|
chan_id,
|
|
XRDP_DRDYNVC_STATUS_TO_STR(self->drdynvcs[chan_id].status));
|
|
return 1;
|
|
}
|
|
if (data_bytes > 1590)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Payload for channel id %d is is too big. "
|
|
"data_bytes %d", chan_id, data_bytes);
|
|
return 1;
|
|
}
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
if (xrdp_channel_init(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_data_first: xrdp_channel_init failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
cmd_ptr = s->p;
|
|
out_uint8(s, 0); /* set later */
|
|
ChId = chan_id;
|
|
cbChId = drdynvc_insert_uint_124(s, ChId); /* ChannelId */
|
|
cbTotalDataSize = drdynvc_insert_uint_124(s, total_data_bytes); /* Length */
|
|
out_uint8p(s, data, data_bytes); /* Data */
|
|
/* cbId (low 2 bits), Len (2 bits), Cmd (hi 4 bits) */
|
|
cmd_ptr[0] = CMD_DVC_DATA_FIRST | (cbTotalDataSize << 2) | cbChId;
|
|
static_channel_id = self->drdynvc_channel_id;
|
|
static_flags = XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST;
|
|
s_mark_end(s);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_DATA_FIRST "
|
|
"cbId %d, Len %d, Cmd 0x%2.2x, ChannelId %d, Length %d",
|
|
cbChId, cbTotalDataSize, CMD_DVC_DATA_FIRST, ChId, total_data_bytes);
|
|
total_data_len = (int) (s->end - cmd_ptr);
|
|
if (xrdp_channel_send(self, s, static_channel_id, total_data_len,
|
|
static_flags) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_data_first: xrdp_channel_send failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
free_stream(s);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*
|
|
* Send a [MS-RDPEDYC] DYNVC_DATA message.
|
|
*/
|
|
int
|
|
xrdp_channel_drdynvc_data(struct xrdp_channel *self, int chan_id,
|
|
const char *data, int data_bytes)
|
|
{
|
|
struct stream *s;
|
|
int ChId;
|
|
int cbChId;
|
|
int static_channel_id;
|
|
int total_data_len;
|
|
int static_flags;
|
|
char *cmd_ptr;
|
|
|
|
if ((chan_id < 0) || (chan_id >= DRDYNVC_CHANNEL_COUNT))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to send data to an invalid "
|
|
"channel id %d", chan_id);
|
|
return 1;
|
|
}
|
|
if (self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Attempting to send data to a channel that "
|
|
"is not open. channel id %d, channel status %s",
|
|
chan_id,
|
|
XRDP_DRDYNVC_STATUS_TO_STR(self->drdynvcs[chan_id].status));
|
|
return 1;
|
|
}
|
|
if (data_bytes > 1590)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Payload for channel id %d is is too big. "
|
|
"data_bytes %d", chan_id, data_bytes);
|
|
return 1;
|
|
}
|
|
make_stream(s);
|
|
init_stream(s, 8192);
|
|
if (xrdp_channel_init(self, s) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_data: xrdp_channel_init failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
cmd_ptr = s->p;
|
|
out_uint8(s, 0); /* set later */
|
|
ChId = chan_id;
|
|
cbChId = drdynvc_insert_uint_124(s, ChId); /* ChannelId */
|
|
out_uint8p(s, data, data_bytes); /* Data */
|
|
/* cbId (low 2 bits), Sp (2 bits), Cmd (hi 4 bits) */
|
|
cmd_ptr[0] = CMD_DVC_DATA | cbChId;
|
|
static_channel_id = self->drdynvc_channel_id;
|
|
static_flags = XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST;
|
|
s_mark_end(s);
|
|
total_data_len = (int) (s->end - cmd_ptr);
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_DATA "
|
|
"cbId %d, Sp 0, Cmd 0x%2.2x, ChannelId %d",
|
|
cbChId, CMD_DVC_DATA_FIRST, ChId);
|
|
if (xrdp_channel_send(self, s, static_channel_id, total_data_len,
|
|
static_flags) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"xrdp_channel_drdynvc_data: xrdp_channel_send failed");
|
|
free_stream(s);
|
|
return 1;
|
|
}
|
|
free_stream(s);
|
|
return 0;
|
|
}
|