diff options
author | jsorg71 <jay.sorg@gmail.com> | 2018-10-11 22:09:20 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-11 22:09:20 -0700 |
commit | ae1514c1679e87edfdeba5ca3eb2b3659ef44b1a (patch) | |
tree | 9f07228ba6be179dd5c0a46e070ca86c16214ab4 /libxrdp/xrdp_channel.c | |
parent | 6049cf8dad919ded363c0dd5ac53a56e4c82b5ad (diff) | |
download | xrdp-proprietary-ae1514c1679e87edfdeba5ca3eb2b3659ef44b1a.tar.gz xrdp-proprietary-ae1514c1679e87edfdeba5ca3eb2b3659ef44b1a.zip |
dynamic virtual channel improvements
remove not used chansrv <-> xrdp messages
move static channel disable control into libxrdp
remove some blocking read, write chansrv calls
add drdynvc calls to libxrdp
add drdynvc calls to chansrv
channel cleanup
Diffstat (limited to 'libxrdp/xrdp_channel.c')
-rw-r--r-- | libxrdp/xrdp_channel.c | 649 |
1 files changed, 645 insertions, 4 deletions
diff --git a/libxrdp/xrdp_channel.c b/libxrdp/xrdp_channel.c index 4f4f3ef3..9662582e 100644 --- a/libxrdp/xrdp_channel.c +++ b/libxrdp/xrdp_channel.c @@ -31,6 +31,17 @@ #define CHANNEL_FLAG_LAST 0x02 #define CHANNEL_FLAG_SHOW_PROTOCOL 0x10 +#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_STATUS_CLOSED 0 +#define XRDP_DRDYNVC_STATUS_OPEN_SENT 1 +#define XRDP_DRDYNVC_STATUS_OPEN 2 +#define XRDP_DRDYNVC_STATUS_CLOSE_SENT 3 + /*****************************************************************************/ /* returns pointer or nil on error */ static struct mcs_channel_item * @@ -58,6 +69,7 @@ xrdp_channel_create(struct xrdp_sec *owner, struct xrdp_mcs *mcs_layer) 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; } @@ -70,7 +82,7 @@ xrdp_channel_delete(struct xrdp_channel *self) { return; } - + free_stream(self->s); g_memset(self, 0, sizeof(struct xrdp_channel)); g_free(self); } @@ -106,6 +118,12 @@ xrdp_channel_send(struct xrdp_channel *self, struct stream *s, int channel_id, return 1; } + if (channel->disabled) + { + g_writeln("xrdp_channel_send, channel disabled"); + return 0; /* not an error */ + } + s_pop_layer(s, channel_hdr); out_uint32_le(s, total_data_len); @@ -176,6 +194,333 @@ xrdp_channel_call_callback(struct xrdp_channel *self, struct stream *s, } /*****************************************************************************/ +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; +} + +/*****************************************************************************/ +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(s, 1)) + { + return 1; + } + in_uint8(s, chan_id); + } + else if (cbChId == 1) + { + if (!s_check_rem(s, 2)) + { + return 1; + } + in_uint16_le(s, chan_id); + } + else + { + if (!s_check_rem(s, 4)) + { + return 1; + } + in_uint32_le(s, chan_id); + } + *chan_id_p = chan_id; + return 0; +} + +/*****************************************************************************/ +static int +drdynvc_process_capability_response(struct xrdp_channel *self, + int cmd, struct stream *s) +{ + struct xrdp_session *session; + int cap_version; + int rv; + + /* skip padding */ + in_uint8s(s, 1); + /* read client's version */ + in_uint16_le(s, cap_version); + if ((cap_version != 2) && (cap_version != 3)) + { + g_writeln("drdynvc_process_capability_response: incompatible DVC " + "version %d detected", cap_version); + return 1; + } + g_writeln("drdynvc_process_capability_response: DVC version %d selected", + cap_version); + self->drdynvc_state = 1; + session = self->sec_layer->rdp_layer->session; + rv = session->callback(session->id, 0x5558, 0, 0, 0, 0); + return rv; +} + +/*****************************************************************************/ +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) + { + return 1; + } + if (!s_check_rem(s, 4)) + { + return 1; + } + in_uint32_le(s, creation_status); + //g_writeln("drdynvc_process_open_channel_response: chan_id 0x%x " + // "creation_status %d", chan_id, creation_status); + session = self->sec_layer->rdp_layer->session; + if (chan_id > 255) + { + return 1; + } + drdynvc = self->drdynvcs + chan_id; + if (creation_status == 0) + { + drdynvc->status = XRDP_DRDYNVC_STATUS_OPEN; + } + else + { + drdynvc->status = XRDP_DRDYNVC_STATUS_CLOSED; + } + if (drdynvc->open_response != NULL) + { + return drdynvc->open_response(session->id, chan_id, creation_status); + } + return 0; +} + +/*****************************************************************************/ +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) + { + return 1; + } + //g_writeln("drdynvc_process_close_channel_response: chan_id 0x%x", chan_id); + session = self->sec_layer->rdp_layer->session; + if (chan_id > 255) + { + return 1; + } + drdynvc = self->drdynvcs + chan_id; + drdynvc->status = XRDP_DRDYNVC_STATUS_CLOSED; + if (drdynvc->close_response != NULL) + { + return drdynvc->close_response(session->id, chan_id); + } + return 0; +} + +/*****************************************************************************/ +static int +drdynvc_process_data_first(struct xrdp_channel *self, + int cmd, struct stream *s) +{ + struct xrdp_session *session; + uint32_t chan_id; + int len; + int bytes; + int total_bytes; + struct xrdp_drdynvc *drdynvc; + + if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) + { + return 1; + } + len = (cmd >> 2) & 0x03; + if (len == 0) + { + if (!s_check_rem(s, 1)) + { + return 1; + } + in_uint8(s, total_bytes); + } + else if (len == 1) + { + if (!s_check_rem(s, 2)) + { + return 1; + } + in_uint16_le(s, total_bytes); + } + else + { + if (!s_check_rem(s, 4)) + { + return 1; + } + in_uint32_le(s, total_bytes); + } + bytes = (int) (s->end - s->p); + //g_writeln("drdynvc_process_data_first: bytes %d total_bytes %d", bytes, total_bytes); + session = self->sec_layer->rdp_layer->session; + if (chan_id > 255) + { + return 1; + } + drdynvc = self->drdynvcs + chan_id; + if (drdynvc->data_first != NULL) + { + return drdynvc->data_first(session->id, chan_id, s->p, + bytes, total_bytes); + } + return 0; +} + +/*****************************************************************************/ +static int +drdynvc_process_data(struct xrdp_channel *self, + int cmd, struct stream *s) +{ + struct xrdp_session *session; + uint32_t chan_id; + int bytes; + struct xrdp_drdynvc *drdynvc; + + if (drdynvc_get_chan_id(s, cmd, &chan_id) != 0) + { + return 1; + } + bytes = (int) (s->end - s->p); + //g_writeln("drdynvc_process_data: bytes %d", bytes); + session = self->sec_layer->rdp_layer->session; + if (chan_id > 255) + { + return 1; + } + drdynvc = self->drdynvcs + chan_id; + if (drdynvc->data != NULL) + { + return drdynvc->data(session->id, chan_id, s->p, bytes); + } + return 0; +} + +/*****************************************************************************/ +static int +xrdp_channel_process_drdynvc(struct xrdp_channel *self, + struct mcs_channel_item *channel, + struct stream *s) +{ + int total_length; + int length; + int flags; + int cmd; + int rv; + struct stream *ls; + + if (!s_check_rem(s, 8)) + { + return 1; + } + in_uint32_le(s, total_length); + in_uint32_le(s, flags); + //g_writeln("xrdp_channel_process_drdynvc: total_length %d flags 0x%8.8x", + // total_length, flags); + ls = NULL; + switch (flags & 3) + { + case 0: + length = (int) (s->end - s->p); + out_uint8a(self->s, s->p, length); + in_uint8s(s, length); + return 0; + case 1: + free_stream(self->s); + make_stream(self->s); + init_stream(self->s, total_length); + length = (int) (s->end - s->p); + out_uint8a(self->s, s->p, length); + in_uint8s(s, length); + return 0; + case 2: + length = (int) (s->end - s->p); + out_uint8a(self->s, s->p, length); + in_uint8s(s, length); + ls = self->s; + break; + case 3: + ls = s; + break; + default: + g_writeln("xrdp_channel_process_drdynvc: error"); + return 1; + } + if (ls == NULL) + { + return 1; + } + in_uint8(ls, cmd); /* read command */ + //g_writeln("xrdp_channel_process_drdynvc: cmd 0x%x", cmd); + rv = 1; + switch (cmd & 0xf0) + { + case CMD_DVC_CAPABILITY: + rv = drdynvc_process_capability_response(self, cmd, s); + break; + case CMD_DVC_OPEN_CHANNEL: + rv = drdynvc_process_open_channel_response(self, cmd, s); + break; + case CMD_DVC_CLOSE_CHANNEL: + rv = drdynvc_process_close_channel_response(self, cmd, s); + break; + case CMD_DVC_DATA_FIRST: + rv = drdynvc_process_data_first(self, cmd, s); + break; + case CMD_DVC_DATA: + rv = drdynvc_process_data(self, cmd, s); + break; + default: + g_writeln("xrdp_channel_process_drdynvc: got unknown " + "command 0x%x", cmd); + break; + } + //g_writeln("xrdp_channel_process_drdynvc: rv %d", rv); + return rv; +} + +/*****************************************************************************/ /* returns error */ /* This is called from the secure layer to process an incoming non global channel packet. @@ -191,23 +536,319 @@ xrdp_channel_process(struct xrdp_channel *self, struct stream *s, 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) { g_writeln("xrdp_channel_process, channel not found"); return 1; } - + if (channel->disabled) + { + g_writeln("xrdp_channel_process, channel disabled"); + 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); in_uint32_le(s, flags); rv = xrdp_channel_call_callback(self, s, channel_id, length, flags); return rv; } + +/*****************************************************************************/ +/* drdynvc */ +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) + { + free_stream(s); + return 1; + } + phold = s->p; + out_uint8(s, 0x50); /* insert cmd */ + 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 = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST; + channel_id = self->drdynvc_channel_id; + if (xrdp_channel_send(self, s, channel_id, total_data_len, flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + return 0; +} + +/*****************************************************************************/ +int +xrdp_channel_drdynvc_start(struct xrdp_channel *self) +{ + int index; + int count; + struct mcs_channel_item *ci; + struct mcs_channel_item *dci; + + g_writeln("xrdp_channel_drdynvc_start:"); + 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") == 0) + { + dci = ci; + } + } + } + if (dci != NULL) + { + self->drdynvc_channel_id = (dci->chanid - MCS_GLOBAL_CHANNEL) - 1; + xrdp_channel_drdynvc_send_capability_request(self); + } + return 0; +} + +/*****************************************************************************/ +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; + + make_stream(s); + init_stream(s, 8192); + if (xrdp_channel_init(self, s) != 0) + { + free_stream(s); + return 1; + } + cmd_ptr = s->p; + out_uint8(s, 0); + ChId = 1; + while (self->drdynvcs[ChId].status != XRDP_DRDYNVC_STATUS_CLOSED) + { + ChId++; + if (ChId > 255) + { + free_stream(s); + return 1; + } + } + cbChId = drdynvc_insert_uint_124(s, ChId); + name_length = g_strlen(name); + out_uint8a(s, name, name_length + 1); + chan_pri = 0; + cmd_ptr[0] = CMD_DVC_OPEN_CHANNEL | ((chan_pri << 2) & 0x0c) | cbChId; + static_channel_id = self->drdynvc_channel_id; + static_flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST; + s_mark_end(s); + total_data_len = (int) (s->end - cmd_ptr); + if (xrdp_channel_send(self, s, static_channel_id, total_data_len, + static_flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + *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; + return 0; +} + +/*****************************************************************************/ +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 > 255)) + { + return 1; + } + if ((self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN) && + (self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN_SENT)) + { + /* not open */ + return 1; + } + make_stream(s); + init_stream(s, 8192); + if (xrdp_channel_init(self, s) != 0) + { + free_stream(s); + return 1; + } + cmd_ptr = s->p; + out_uint8(s, 0); + ChId = chan_id; + cbChId = drdynvc_insert_uint_124(s, ChId); + cmd_ptr[0] = CMD_DVC_CLOSE_CHANNEL | cbChId; + static_channel_id = self->drdynvc_channel_id; + static_flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST; + s_mark_end(s); + total_data_len = (int) (s->end - cmd_ptr); + if (xrdp_channel_send(self, s, static_channel_id, total_data_len, + static_flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + self->drdynvcs[ChId].status = XRDP_DRDYNVC_STATUS_CLOSE_SENT; + return 0; +} + +/*****************************************************************************/ +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 > 255)) + { + return 1; + } + if (self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN) + { + return 1; + } + if (data_bytes > 1590) + { + return 1; + } + make_stream(s); + init_stream(s, 8192); + if (xrdp_channel_init(self, s) != 0) + { + free_stream(s); + return 1; + } + cmd_ptr = s->p; + out_uint8(s, 0); + ChId = chan_id; + cbChId = drdynvc_insert_uint_124(s, ChId); + cbTotalDataSize = drdynvc_insert_uint_124(s, total_data_bytes); + out_uint8p(s, data, data_bytes); + cmd_ptr[0] = CMD_DVC_DATA_FIRST | (cbTotalDataSize << 2) | cbChId; + static_channel_id = self->drdynvc_channel_id; + static_flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST; + s_mark_end(s); + total_data_len = (int) (s->end - cmd_ptr); + if (xrdp_channel_send(self, s, static_channel_id, total_data_len, + static_flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + return 0; +} + +/*****************************************************************************/ +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 > 255)) + { + return 1; + } + if (self->drdynvcs[chan_id].status != XRDP_DRDYNVC_STATUS_OPEN) + { + return 1; + } + if (data_bytes > 1590) + { + return 1; + } + make_stream(s); + init_stream(s, 8192); + if (xrdp_channel_init(self, s) != 0) + { + free_stream(s); + return 1; + } + cmd_ptr = s->p; + out_uint8(s, 0); + ChId = chan_id; + cbChId = drdynvc_insert_uint_124(s, ChId); + out_uint8p(s, data, data_bytes); + cmd_ptr[0] = CMD_DVC_DATA | cbChId; + static_channel_id = self->drdynvc_channel_id; + static_flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST; + s_mark_end(s); + total_data_len = (int) (s->end - cmd_ptr); + if (xrdp_channel_send(self, s, static_channel_id, total_data_len, + static_flags) != 0) + { + free_stream(s); + return 1; + } + free_stream(s); + return 0; +} |