summaryrefslogtreecommitdiffstats
path: root/sesman/chansrv/drdynvc.c
diff options
context:
space:
mode:
authorLaxmikant Rashinkar <LK.Rashinkar@gmail.com>2012-09-27 19:48:44 -0700
committerLaxmikant Rashinkar <LK.Rashinkar@gmail.com>2012-09-27 19:48:44 -0700
commit5b0eaa4a9b828da75563238c245061284ef09a73 (patch)
tree349083d6fb7db973125362cc6d2e02085ab9c880 /sesman/chansrv/drdynvc.c
parent42a56cd33ed83a399784958ae4e5490ed21b0b7c (diff)
downloadxrdp-proprietary-5b0eaa4a9b828da75563238c245061284ef09a73.tar.gz
xrdp-proprietary-5b0eaa4a9b828da75563238c245061284ef09a73.zip
o added support for dynamic virtual channels
o added echo test routine in simple.c for testing DVC using Microsoft's ECHO protocol
Diffstat (limited to 'sesman/chansrv/drdynvc.c')
-rw-r--r--sesman/chansrv/drdynvc.c501
1 files changed, 500 insertions, 1 deletions
diff --git a/sesman/chansrv/drdynvc.c b/sesman/chansrv/drdynvc.c
index da0e8fe3..6bcac45e 100644
--- a/sesman/chansrv/drdynvc.c
+++ b/sesman/chansrv/drdynvc.c
@@ -1,7 +1,7 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
- * Copyright (C) Jay Sorg 2012
+ * Copyright (C) Laxmikant Rashinkar 2012 LK.Rashinkar@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,3 +15,502 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#include "drdynvc.h"
+
+int g_drdynvc_chan_id;
+int g_drdynvc_inited = 0;
+
+/**
+ * bring up dynamic virtual channel
+ *
+ * @return 0 on success, -1 on response
+ ******************************************************************************/
+int APP_CC
+drdynvc_init(void)
+{
+ /* bring up X11 */
+ xcommon_init();
+
+ /* let client know what version of DVC we support */
+ drdynvc_send_capability_request(2);
+
+ return 0;
+}
+
+/**
+ * let DVC Manager on client end know what version of protocol we support
+ * client will respond with version that it supports
+ *
+ * @return 0 on success, -1 on response
+ ******************************************************************************/
+static int APP_CC
+drdynvc_send_capability_request(uint16_t version)
+{
+ struct stream *s;
+ int bytes_in_stream;
+
+ /* setup stream */
+ make_stream(s);
+ init_stream(s, MAX_PDU_SIZE);
+
+ out_uint8(s, 0x50); /* insert cmd */
+ out_uint8(s, 0x00); /* insert padding */
+ out_uint16_le(s, version); /* 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 */
+
+ /* send command to client */
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+ free_stream(s);
+
+ return 0;
+}
+
+/**
+ * process capability response received from DVC manager at client end
+ *
+ * @param s stream containing client response
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+static int APP_CC
+drdynvc_process_capability_response(struct stream *s, unsigned char cmd)
+{
+ int cap_version;
+
+ /* skip padding */
+ in_uint8s(s, 1);
+
+ /* read client's version */
+ in_uint16_le(s, cap_version);
+
+ if (cap_version != 2)
+ {
+ LOG(0, ("drdynvc_process_capability_response: incompatible DVC version %d detected", cap_version));
+ return -1;
+ }
+
+ LOG(0, ("drdynvc_process_capability_response: DVC version 2 selected"));
+ g_drdynvc_inited = 1;
+
+ return 0;
+}
+
+/**
+ * create a new dynamic virtual channel
+ *
+ * @pram channel_id channel id number
+ * @pram channel_name name of channel
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int APP_CC
+drdynvc_send_open_channel_request(int chan_pri, unsigned int chan_id,
+ char *chan_name)
+{
+ struct stream *s;
+ int bytes_in_stream;
+ int cbChId;
+ int name_length;
+
+ if ((chan_name == NULL) || (strlen(chan_name) == 0))
+ {
+ LOG(0, ("drdynvc_send_open_channel_request: bad channel name specified"));
+ return -1;
+ }
+
+ make_stream(s);
+ init_stream(s, MAX_PDU_SIZE);
+
+ name_length = strlen(chan_name);
+
+ /* dummy command for now */
+ out_uint8(s, 0);
+
+ /* insert channel id */
+ cbChId = drdynvc_insert_uint_124(s, chan_id);
+
+ /* insert channel name */
+ out_uint8a(s, chan_name, name_length + 1);
+
+ /* insert command */
+ s->data[0] = CMD_DVC_OPEN_CHANNEL | ((chan_pri << 2) & 0x0c) | cbChId;
+
+ /* send command */
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+ free_stream(s);
+
+ return 0;
+}
+
+static int APP_CC
+drdynvc_process_open_channel_response(struct stream *s, unsigned char cmd)
+{
+ struct xrdp_api_data *adp;
+
+ uint32_t chan_id;
+ int creation_status;
+
+ drdynvc_get_chan_id(s, cmd, &chan_id);
+ in_uint32_le(s, creation_status);
+
+ /* LK_TODO now do something using useful! */
+
+ if (creation_status < 0)
+ {
+ // TODO
+ }
+ else
+ {
+ /* get struct xrdp_api_data containing this channel id */
+ if ((adp = struct_from_dvc_chan_id(chan_id)) == NULL)
+ {
+ LOG(0, ("drdynvc_process_open_channel_response: error : "
+ "could not find xrdp_api_data containing chan_id %d", chan_id));
+
+ return -1;
+ }
+
+ adp->is_connected = 1;
+ }
+
+ return 0;
+}
+
+int APP_CC
+drdynvc_send_close_channel_request(unsigned int chan_id)
+{
+ struct stream *s;
+ int bytes_in_stream;
+ int cbChId;
+
+ make_stream(s);
+ init_stream(s, MAX_PDU_SIZE);
+
+ /* insert dummy cmd for now */
+ out_uint8(s, 0);
+
+ /* insert channel id */
+ cbChId = drdynvc_insert_uint_124(s, chan_id);
+
+ /* insert command */
+ s->data[0] = CMD_DVC_CLOSE_CHANNEL | cbChId;
+
+ /* send command */
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+
+ free_stream(s);
+ return 0;
+}
+
+static int APP_CC
+drdynvc_process_close_channel_response(struct stream *s, unsigned char cmd)
+{
+ uint32_t chan_id;
+
+ drdynvc_get_chan_id(s, cmd, &chan_id);
+
+ /* LK_TODO now do something using useful! */
+
+ return 0;
+}
+
+/*
+ * send data to client
+ *
+ * @param chan_id the virtual channel to write to
+ * @param data data to write
+ * @param data_size number of bytes to write
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int APP_CC drdynvc_write_data(uint32_t chan_id, char *data, int data_size)
+{
+ struct stream *s;
+ char *saved_ptr;
+ int cbChId;
+ int Len;
+ int bytes_in_stream;
+ int frag_size;
+
+ if (data == NULL)
+ {
+ LOG(0, ("drdynvc_write_data: data is NULL\n"));
+ return -1;
+ }
+
+ if (data_size <= 0)
+ {
+ return 0;
+ }
+
+ make_stream(s);
+ init_stream(s, MAX_PDU_SIZE);
+
+ /* this is a dummy write */
+ out_uint8(s, 0);
+
+ /* insert channel id */
+ cbChId = drdynvc_insert_uint_124(s, chan_id);
+
+ /* will data fit into one pkt? */
+ bytes_in_stream = stream_length_before_p(s);
+
+ if ((bytes_in_stream + data_size) <= MAX_PDU_SIZE)
+ {
+ /* yes it will - insert data */
+ out_uint8p(s, data, data_size);
+
+ /* insert command */
+ s->data[0] = CMD_DVC_DATA | cbChId;
+
+ /* write data to client */
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+ free_stream(s);
+ return 0;
+ }
+
+ /* no it won't - fragment it */
+
+ saved_ptr = s->p;
+
+ /* let client know how much data to expect */
+ Len = drdynvc_insert_uint_124(s, data_size);
+
+ /* insert data into first fragment */
+ frag_size = MAX_PDU_SIZE - stream_length_before_p(s);
+ out_uint8p(s, data, frag_size);
+
+ /* insert command */
+ s->data[0] = CMD_DVC_DATA_FIRST | Len << 2 | cbChId;
+
+ /* write first fragment to client */
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+ data_size -= frag_size;
+ data += frag_size;
+ s->data[0] = CMD_DVC_DATA | cbChId;
+ s->p = saved_ptr;
+
+ /* now send rest of the data using CMD_DVC_DATA */
+ while (data_size > 0)
+ {
+ frag_size = MAX_PDU_SIZE - stream_length_before_p(s);
+
+ if (frag_size > data_size)
+ {
+ frag_size = data_size;
+ }
+
+ out_uint8p(s, data, frag_size);
+ bytes_in_stream = stream_length_before_p(s);
+ send_channel_data(g_drdynvc_chan_id, s->data, bytes_in_stream);
+ data_size -= frag_size;
+ data += frag_size;
+ s->p = saved_ptr;
+ }
+
+ free_stream(s);
+ return 0;
+}
+
+static int APP_CC
+drdynvc_process_data_first(struct stream *s, unsigned char cmd)
+{
+ struct xrdp_api_data *adp;
+ struct stream *ls;
+
+ uint32_t chan_id;
+ int bytes_in_stream;
+ int data_len;
+ int Len;
+
+ drdynvc_get_chan_id(s, cmd, &chan_id);
+
+ Len = (cmd >> 2) & 0x03;
+
+ if (Len == 0)
+ {
+ in_uint8(s, data_len);
+ }
+ else if (Len == 1)
+ {
+ in_uint16_le(s, data_len);
+ }
+ else
+ {
+ in_uint32_le(s, data_len);
+ }
+
+ bytes_in_stream = stream_length_after_p(s);
+
+ /* get struct xrdp_api_data containing this channel id */
+ if ((adp = struct_from_dvc_chan_id(chan_id)) == NULL)
+ {
+ LOG(0, ("drdynvc_process_data_first: error : "
+ "could not find xrdp_api_data containing chan_id %d", chan_id));
+
+ return -1;
+ }
+
+ ls = trans_get_out_s(adp->transp, MAX_PDU_SIZE);
+ out_uint8p(ls, s->p, bytes_in_stream);
+ s_mark_end(ls);
+ trans_force_write(adp->transp);
+
+ return 0;
+}
+
+static int APP_CC
+drdynvc_process_data(struct stream *s, unsigned char cmd)
+{
+ struct xrdp_api_data *adp;
+ struct stream *ls;
+
+ uint32_t chan_id;
+ int bytes_in_stream;
+
+ drdynvc_get_chan_id(s, cmd, &chan_id);
+ bytes_in_stream = stream_length_after_p(s);
+
+ /* get struct xrdp_api_data containing this channel id */
+ if ((adp = struct_from_dvc_chan_id(chan_id)) == NULL)
+ {
+ LOG(0, ("drdynvc_process_data: error : "
+ "could not find xrdp_api_data containing chan_id %d", chan_id));
+
+ return -1;
+ }
+
+ ls = trans_get_out_s(adp->transp, MAX_PDU_SIZE);
+ out_uint8p(ls, s->p, bytes_in_stream);
+ s_mark_end(ls);
+ trans_force_write(adp->transp);
+
+ return 0;
+}
+
+/**
+ * process incoming data on a dynamic virtual channel
+ *
+ * @pram s stream containing the incoming data
+ * @pram chand_id LK_TODO
+ * @pram chan_flags LK_TODO
+ * @pram length LK_TODO
+ * @pram total_length LK_TODO
+ *
+ * @return 0 on success, -1 on failure
+ ******************************************************************************/
+int APP_CC
+drdynvc_data_in(struct stream *s, int chan_id, int chan_flags, int length,
+ int total_length)
+{
+ unsigned char cmd;
+
+ in_uint8(s, cmd); /* read command */
+
+ switch (cmd & 0xf0)
+ {
+ case CMD_DVC_CAPABILITY:
+ drdynvc_process_capability_response(s, cmd);
+ break;
+
+ case CMD_DVC_OPEN_CHANNEL:
+ drdynvc_process_open_channel_response(s, cmd);
+ break;
+
+ case CMD_DVC_CLOSE_CHANNEL:
+ drdynvc_process_close_channel_response(s, cmd);
+ break;
+
+ case CMD_DVC_DATA_FIRST:
+ drdynvc_process_data_first(s, cmd);
+ break;
+
+ case CMD_DVC_DATA:
+ drdynvc_process_data(s, cmd);
+ break;
+
+ default:
+ LOG(0, ("drdynvc_data_in: got unknown command 0x%x", cmd));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * insert a byte, short or 32bit value into specified stream
+ *
+ * @param s stream used for insertion
+ * @param val value to insert
+ *
+ * @return 0 for byte insertions
+ * @return 1 for short insertion
+ * @return 2 for uint32_t insertions
+ ******************************************************************************/
+static int APP_CC
+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;
+}
+
+/*
+ * extract channel id from stream
+ *
+ * @param s stream containing channel id
+ * @param cmd first byte in stream
+ * @param chan_id return channel id here
+ ******************************************************************************/
+static int APP_CC
+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)
+ {
+ in_uint8(s, chan_id);
+ }
+ else if (cbChId == 1)
+ {
+ in_uint16_le(s, chan_id);
+ }
+ else
+ {
+ in_uint32_le(s, chan_id);
+ }
+
+ *chan_id_p = chan_id;
+
+ return 0;
+}