diff options
author | Laxmikant Rashinkar <LK.Rashinkar@gmail.com> | 2012-09-27 19:48:44 -0700 |
---|---|---|
committer | Laxmikant Rashinkar <LK.Rashinkar@gmail.com> | 2012-09-27 19:48:44 -0700 |
commit | 5b0eaa4a9b828da75563238c245061284ef09a73 (patch) | |
tree | 349083d6fb7db973125362cc6d2e02085ab9c880 /sesman/chansrv/drdynvc.c | |
parent | 42a56cd33ed83a399784958ae4e5490ed21b0b7c (diff) | |
download | xrdp-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.c | 501 |
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; +} |