diff --git a/libxrdp/libxrdp.c b/libxrdp/libxrdp.c index 99da37f8..2720fab2 100644 --- a/libxrdp/libxrdp.c +++ b/libxrdp/libxrdp.c @@ -1263,6 +1263,93 @@ libxrdp_send_to_channel(struct xrdp_session *session, int channel_id, return 0; } +/*****************************************************************************/ +int +libxrdp_disable_channel(struct xrdp_session *session, int channel_id, + int is_disabled) +{ + struct xrdp_rdp *rdp; + struct xrdp_mcs *mcs; + struct mcs_channel_item *channel_item; + + rdp = (struct xrdp_rdp *) (session->rdp); + mcs = rdp->sec_layer->mcs_layer; + if (mcs->channel_list == NULL) + { + return 1; + } + channel_item = (struct mcs_channel_item *) + list_get_item(mcs->channel_list, channel_id); + if (channel_item == NULL) + { + return 1; + } + channel_item->disabled = is_disabled; + return 1; +} + +/*****************************************************************************/ +int +libxrdp_drdynvc_open(struct xrdp_session *session, const char *name, + int flags, struct xrdp_drdynvc_procs *procs, + int *chan_id) +{ + struct xrdp_rdp *rdp; + struct xrdp_sec *sec; + struct xrdp_channel *chan; + + rdp = (struct xrdp_rdp *) (session->rdp); + sec = rdp->sec_layer; + chan = sec->chan_layer; + return xrdp_channel_drdynvc_open(chan, name, flags, procs, chan_id); +} + +/*****************************************************************************/ +int +libxrdp_drdynvc_close(struct xrdp_session *session, int chan_id) +{ + struct xrdp_rdp *rdp; + struct xrdp_sec *sec; + struct xrdp_channel *chan; + + rdp = (struct xrdp_rdp *) (session->rdp); + sec = rdp->sec_layer; + chan = sec->chan_layer; + return xrdp_channel_drdynvc_close(chan, chan_id); +} + +/*****************************************************************************/ +int +libxrdp_drdynvc_data_first(struct xrdp_session *session, int chan_id, + const char *data, int data_bytes, + int total_data_bytes) +{ + struct xrdp_rdp *rdp; + struct xrdp_sec *sec; + struct xrdp_channel *chan; + + rdp = (struct xrdp_rdp *) (session->rdp); + sec = rdp->sec_layer; + chan = sec->chan_layer; + return xrdp_channel_drdynvc_data_first(chan, chan_id, data, data_bytes, + total_data_bytes); +} + +/*****************************************************************************/ +int +libxrdp_drdynvc_data(struct xrdp_session *session, int chan_id, + const char *data, int data_bytes) +{ + struct xrdp_rdp *rdp; + struct xrdp_sec *sec; + struct xrdp_channel *chan; + + rdp = (struct xrdp_rdp *) (session->rdp); + sec = rdp->sec_layer; + chan = sec->chan_layer; + return xrdp_channel_drdynvc_data(chan, chan_id, data, data_bytes); +} + /*****************************************************************************/ int EXPORT_CC libxrdp_orders_send_brush(struct xrdp_session *session, diff --git a/libxrdp/libxrdp.h b/libxrdp/libxrdp.h index 0ac5f635..98c8f030 100644 --- a/libxrdp/libxrdp.h +++ b/libxrdp/libxrdp.h @@ -51,6 +51,8 @@ struct mcs_channel_item char name[16]; int flags; int chanid; + int disabled; + int pad0; }; /* mcs */ @@ -128,11 +130,27 @@ struct xrdp_sec int is_security_header_present; /* boolean */ }; +struct xrdp_drdynvc +{ + int chan_id; + int status; /* see XRDP_DRDYNVC_STATUS_* */ + int flags; + int pad0; + int (*open_response)(intptr_t id, int chan_id, int creation_status); + int (*close_response)(intptr_t id, int chan_id); + int (*data_first)(intptr_t id, int chan_id, char *data, int bytes, int total_bytes); + int (*data)(intptr_t id, int chan_id, char *data, int bytes); +}; + /* channel */ struct xrdp_channel { struct xrdp_sec *sec_layer; struct xrdp_mcs *mcs_layer; + int drdynvc_channel_id; + int drdynvc_state; + struct stream *s; + struct xrdp_drdynvc drdynvcs[256]; }; /* rdp */ @@ -285,7 +303,6 @@ struct xrdp_mppc_enc tui16 *hash_table; }; - int compress_rdp(struct xrdp_mppc_enc *enc, tui8 *srcData, int len); struct xrdp_mppc_enc * @@ -553,6 +570,21 @@ xrdp_channel_send(struct xrdp_channel *self, struct stream *s, int channel_id, int xrdp_channel_process(struct xrdp_channel *self, struct stream *s, int chanid); +int +xrdp_channel_drdynvc_start(struct xrdp_channel *self); +int +xrdp_channel_drdynvc_open(struct xrdp_channel *self, const char *name, + int flags, struct xrdp_drdynvc_procs *procs, + int *chan_id); +int +xrdp_channel_drdynvc_close(struct xrdp_channel *self, int chan_id); +int +xrdp_channel_drdynvc_data_first(struct xrdp_channel *self, int chan_id, + const char *data, int data_bytes, + int total_data_bytes); +int +xrdp_channel_drdynvc_data(struct xrdp_channel *self, int chan_id, + const char *data, int data_bytes); /* xrdp_fastpath.c */ struct xrdp_fastpath * diff --git a/libxrdp/libxrdpinc.h b/libxrdp/libxrdpinc.h index 38bd4b34..c236bec9 100644 --- a/libxrdp/libxrdpinc.h +++ b/libxrdp/libxrdpinc.h @@ -76,6 +76,14 @@ struct xrdp_session struct source_info si; }; +struct xrdp_drdynvc_procs +{ + int (*open_response)(intptr_t id, int chan_id, int creation_status); + int (*close_response)(intptr_t id, int chan_id); + int (*data_first)(intptr_t id, int chan_id, char *data, int bytes, int total_bytes); + int (*data)(intptr_t id, int chan_id, char *data, int bytes); +}; + struct xrdp_session * libxrdp_init(tbus id, struct trans *trans); int @@ -195,6 +203,22 @@ libxrdp_send_to_channel(struct xrdp_session *session, int channel_id, char *data, int data_len, int total_data_len, int flags); int +libxrdp_disable_channel(struct xrdp_session *session, int channel_id, + int is_disabled); +int +libxrdp_drdynvc_open(struct xrdp_session *session, const char *name, + int flags, struct xrdp_drdynvc_procs *procs, + int *chan_id); +int +libxrdp_drdynvc_close(struct xrdp_session *session, int chan_id); +int +libxrdp_drdynvc_data_first(struct xrdp_session *session, int chan_id, + const char *data, int data_bytes, + int total_data_bytes); +int +libxrdp_drdynvc_data(struct xrdp_session *session, int chan_id, + const char *data, int data_bytes); +int libxrdp_orders_send_brush(struct xrdp_session *session, int width, int height, int bpp, int type, int size, char *data, int cache_id); 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); @@ -175,6 +193,333 @@ xrdp_channel_call_callback(struct xrdp_channel *self, struct stream *s, return rv; } +/*****************************************************************************/ +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 @@ -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; +} diff --git a/libxrdp/xrdp_rdp.c b/libxrdp/xrdp_rdp.c index 099cec47..494d23e8 100644 --- a/libxrdp/xrdp_rdp.c +++ b/libxrdp/xrdp_rdp.c @@ -1125,6 +1125,7 @@ xrdp_rdp_process_data_font(struct xrdp_rdp *self, struct stream *s) g_writeln("yeah, up_and_running"); DEBUG(("up_and_running set")); xrdp_rdp_send_data_update_sync(self); + xrdp_channel_drdynvc_start(self->sec_layer->chan_layer); } DEBUG(("out xrdp_rdp_process_data_font")); diff --git a/libxrdp/xrdp_sec.c b/libxrdp/xrdp_sec.c index 0436edf2..2fbad018 100644 --- a/libxrdp/xrdp_sec.c +++ b/libxrdp/xrdp_sec.c @@ -1840,57 +1840,54 @@ xrdp_sec_process_mcs_data_channels(struct xrdp_sec *self, struct stream *s) { int num_channels; int index; - struct xrdp_client_info *client_info = (struct xrdp_client_info *)NULL; + struct xrdp_client_info *client_info; + struct mcs_channel_item *channel_item; client_info = &(self->rdp_layer->client_info); - - - DEBUG(("processing channels, channels_allowed is %d", client_info->channels_allowed)); - + DEBUG(("processing channels, channels_allowed is %d", + client_info->channels_allowed)); /* this is an option set in xrdp.ini */ - if (client_info->channels_allowed != 1) /* are channels on? */ + if (client_info->channels_allowed == 0) /* are channels on? */ { - g_writeln("xrdp_sec_process_mcs_data_channels: all channels are disabled by configuration"); + log_message(LOG_LEVEL_INFO, "all channels are disabled by " + "configuration"); return 0; } - if (!s_check_rem(s, 4)) { return 1; } - in_uint32_le(s, num_channels); - if (num_channels > 31) { return 1; } - for (index = 0; index < num_channels; index++) { - struct mcs_channel_item *channel_item; - - channel_item = (struct mcs_channel_item *) - g_malloc(sizeof(struct mcs_channel_item), 1); + channel_item = g_new0(struct mcs_channel_item, 1); if (!s_check_rem(s, 12)) { g_free(channel_item); return 1; } in_uint8a(s, channel_item->name, 8); - if (g_strlen(channel_item->name) == 0) + in_uint32_le(s, channel_item->flags); + if (g_strlen(channel_item->name) > 0) + { + channel_item->chanid = MCS_GLOBAL_CHANNEL + (index + 1); + log_message(LOG_LEVEL_INFO, "adding channel item name %s chan_id " + "%d flags 0x%8.8x", channel_item->name, + channel_item->chanid, channel_item->flags); + list_add_item(self->mcs_layer->channel_list, + (intptr_t) channel_item); + DEBUG(("got channel flags %8.8x name %s", channel_item->flags, + channel_item->name)); + } + else { - log_message(LOG_LEVEL_WARNING, "xrdp_sec_process_mcs_data_channels: got an empty channel name, ignoring it"); g_free(channel_item); - continue; } - in_uint32_le(s, channel_item->flags); - channel_item->chanid = MCS_GLOBAL_CHANNEL + (index + 1); - list_add_item(self->mcs_layer->channel_list, (tintptr) channel_item); - DEBUG(("got channel flags %8.8x name %s", channel_item->flags, - channel_item->name)); } - return 0; } diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index 35273283..05007757 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -56,8 +56,6 @@ xrdp_chansrv_SOURCES = \ clipboard_file.h \ devredir.c \ devredir.h \ - drdynvc.c \ - drdynvc.h \ fifo.c \ fifo.h \ irp.c \ diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 6ec05e53..7a0de556 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -36,7 +36,6 @@ #include "rail.h" #include "xcommon.h" #include "chansrv_fuse.h" -#include "drdynvc.h" #include "xrdp_sockets.h" static struct trans *g_lis_trans = 0; @@ -49,25 +48,17 @@ static int g_cliprdr_index = -1; static int g_rdpsnd_index = -1; static int g_rdpdr_index = -1; static int g_rail_index = -1; -static int g_drdynvc_index = -1; - -/* state info for dynamic virtual channels */ -static struct xrdp_api_data *g_dvc_channels[MAX_DVC_CHANNELS]; static tbus g_term_event = 0; static tbus g_thread_done_event = 0; static int g_use_unix_socket = 0; -static const unsigned char g_xrdpapi_magic[12] = -{ 0x78, 0x32, 0x10, 0x67, 0x00, 0x92, 0x30, 0x56, 0xff, 0xd8, 0xa9, 0x1f }; - int g_display_num = 0; int g_cliprdr_chan_id = -1; /* cliprdr */ int g_rdpsnd_chan_id = -1; /* rdpsnd */ int g_rdpdr_chan_id = -1; /* rdpdr */ int g_rail_chan_id = -1; /* rail */ -int g_drdynvc_chan_id = -1; /* drdynvc */ char *g_exec_name; tbus g_exec_event; @@ -76,10 +67,36 @@ tbus g_exec_sem; int g_exec_pid = 0; #define ARRAYSIZE(x) (sizeof(x)/sizeof(*(x))) +/* max total channel bytes size */ +#define MAX_CHANNEL_BYTES (1 * 1024 * 1024 * 1024) /* 1 GB */ +#define MAX_CHANNEL_FRAG_BYTES 1600 + +#define CHANSRV_DRDYNVC_STATUS_CLOSED 0 +#define CHANSRV_DRDYNVC_STATUS_OPEN_SENT 1 +#define CHANSRV_DRDYNVC_STATUS_OPEN 2 +#define CHANSRV_DRDYNVC_STATUS_CLOSE_SENT 3 + +struct chansrv_drdynvc +{ + int chan_id; + int status; /* see CHANSRV_DRDYNVC_STATUS_* */ + int flags; + int pad0; + int (*open_response)(int chan_id, int creation_status); + int (*close_response)(int chan_id); + int (*data_first)(int chan_id, char *data, int bytes, int total_bytes); + int (*data)(int chan_id, char *data, int bytes); + struct trans *xrdp_api_trans; +}; -/* each time we create a DVC we need a unique DVC channel id */ -/* this variable gets bumped up once per DVC we create */ -tui32 g_dvc_chan_id = 100; +static struct chansrv_drdynvc g_drdynvcs[256]; + +/* data in struct trans::callback_data */ +struct xrdp_api_data +{ + int chan_flags; + int chan_id; +}; struct timeout_obj { @@ -226,151 +243,60 @@ g_is_term(void) } /*****************************************************************************/ -/* add data to chan_item, on its way to the client */ +/* send data to a static virtual channel + size can be > MAX_CHANNEL_FRAG_BYTES, in which case, > 1 message + will be sent*/ /* returns error */ -static int -add_data_to_chan_item(struct chan_item *chan_item, char *data, int size) -{ - struct stream *s; - struct chan_out_data *cod; - - make_stream(s); - init_stream(s, size); - g_memcpy(s->data, data, size); - s->end = s->data + size; - cod = (struct chan_out_data *)g_malloc(sizeof(struct chan_out_data), 1); - cod->s = s; - - if (chan_item->tail == 0) - { - chan_item->tail = cod; - chan_item->head = cod; - } - else - { - chan_item->tail->next = cod; - chan_item->tail = cod; - } - - return 0; -} - -/*****************************************************************************/ -/* returns error */ -static int -send_data_from_chan_item(struct chan_item *chan_item) +int +send_channel_data(int chan_id, const char *data, int size) { - struct stream *s; - struct chan_out_data *cod; - int bytes_left; - int size; + int sending_bytes; int chan_flags; int error; + int total_size; + struct stream *s; - if (chan_item->head == 0) - { - return 0; - } - - cod = chan_item->head; - bytes_left = (int)(cod->s->end - cod->s->p); - size = MIN(1600, bytes_left); - chan_flags = 0; - - if (cod->s->p == cod->s->data) - { - chan_flags |= 1; /* first */ - } - - if (cod->s->p + size >= cod->s->end) - { - chan_flags |= 2; /* last */ - } - - s = trans_get_out_s(g_con_trans, 8192); - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8 + 2 + 2 + 2 + 4 + size); /* size */ - out_uint32_le(s, 8); /* msg id */ - out_uint32_le(s, 8 + 2 + 2 + 2 + 4 + size); /* size */ - out_uint16_le(s, chan_item->id); - out_uint16_le(s, chan_flags); - out_uint16_le(s, size); - out_uint32_le(s, cod->s->size); - out_uint8a(s, cod->s->p, size); - s_mark_end(s); - LOGM((LOG_LEVEL_DEBUG, "chansrv::send_data_from_chan_item: -- " - "size %d chan_flags 0x%8.8x", size, chan_flags)); - - error = trans_write_copy(g_con_trans); - if (error != 0) + if ((chan_id < 0) || (chan_id > 31) || + (data == NULL) || + (size < 1) || (size > MAX_CHANNEL_BYTES)) { + /* bad param */ return 1; } - - cod->s->p += size; - - if (cod->s->p >= cod->s->end) + total_size = size; + chan_flags = 1; /* first */ + while (size > 0) { - free_stream(cod->s); - chan_item->head = chan_item->head->next; - - if (chan_item->head == 0) + sending_bytes = MIN(MAX_CHANNEL_FRAG_BYTES, size); + if (sending_bytes >= size) { - chan_item->tail = 0; + chan_flags |= 2; /* last */ } - - g_free(cod); - } - - return 0; -} - -/*****************************************************************************/ -/* returns error */ -static int -check_chan_items(void) -{ - int index; - - for (index = 0; index < g_num_chan_items; index++) - { - if (g_chan_items[index].head != 0) + s = trans_get_out_s(g_con_trans, 26 + sending_bytes); + if (s == NULL) { - send_data_from_chan_item(g_chan_items + index); + return 2; } - } - - return 0; -} - -/*****************************************************************************/ -/* returns error */ -int -send_channel_data(int chan_id, char *data, int size) -{ - int index; - - //g_writeln("send_channel_data chan_id %d size %d", chan_id, size); - - LOGM((LOG_LEVEL_DEBUG, "chansrv::send_channel_data: size %d", size)); - - if (chan_id == -1) - { - g_writeln("send_channel_data: error, chan_id is -1"); - return 1; - } - - for (index = 0; index < g_num_chan_items; index++) - { - if (g_chan_items[index].id == chan_id) + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 26 + sending_bytes); + out_uint32_le(s, 8); /* msg id */ + out_uint32_le(s, 18 + sending_bytes); + out_uint16_le(s, chan_id); + out_uint16_le(s, chan_flags); + out_uint16_le(s, sending_bytes); + out_uint32_le(s, total_size); + out_uint8a(s, data, sending_bytes); + s_mark_end(s); + size -= sending_bytes; + data += sending_bytes; + error = trans_write_copy(g_con_trans); + if (error != 0) { - add_data_to_chan_item(g_chan_items + index, data, size); - check_chan_items(); - return 0; + return 3; } + chan_flags = 0; } - - return 1; + return 0; } /*****************************************************************************/ @@ -398,84 +324,6 @@ send_rail_drawing_orders(char* data, int size) return 0; } -/*****************************************************************************/ -/* returns error */ -static int -send_init_response_message(void) -{ - struct stream *s = (struct stream *)NULL; - - LOGM((LOG_LEVEL_INFO, "send_init_response_message:")); - s = trans_get_out_s(g_con_trans, 8192); - - if (s == 0) - { - return 1; - } - - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8); /* size */ - out_uint32_le(s, 2); /* msg id */ - out_uint32_le(s, 8); /* size */ - s_mark_end(s); - return trans_write_copy(g_con_trans); -} - -/*****************************************************************************/ -/* returns error */ -static int -send_channel_setup_response_message(void) -{ - struct stream *s = (struct stream *)NULL; - - LOGM((LOG_LEVEL_DEBUG, "send_channel_setup_response_message:")); - s = trans_get_out_s(g_con_trans, 8192); - - if (s == 0) - { - return 1; - } - - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8); /* size */ - out_uint32_le(s, 4); /* msg id */ - out_uint32_le(s, 8); /* size */ - s_mark_end(s); - return trans_write_copy(g_con_trans); -} - -/*****************************************************************************/ -/* returns error */ -static int -send_channel_data_response_message(void) -{ - struct stream *s = (struct stream *)NULL; - - LOGM((LOG_LEVEL_DEBUG, "send_channel_data_response_message:")); - s = trans_get_out_s(g_con_trans, 8192); - - if (s == 0) - { - return 1; - } - - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8); /* size */ - out_uint32_le(s, 6); /* msg id */ - out_uint32_le(s, 8); /* size */ - s_mark_end(s); - return trans_write_copy(g_con_trans); -} - -/*****************************************************************************/ -/* returns error */ -static int -process_message_init(struct stream *s) -{ - LOGM((LOG_LEVEL_DEBUG, "process_message_init:")); - return send_init_response_message(); -} - /*****************************************************************************/ /* returns error */ static int @@ -485,8 +333,6 @@ process_message_channel_setup(struct stream *s) int index; int rv; struct chan_item *ci; - struct chan_out_data *cod; - struct chan_out_data *old_cod; g_num_chan_items = 0; g_cliprdr_index = -1; @@ -497,7 +343,6 @@ process_message_channel_setup(struct stream *s) g_rdpsnd_chan_id = -1; g_rdpdr_chan_id = -1; g_rail_chan_id = -1; - g_drdynvc_chan_id = -1; LOGM((LOG_LEVEL_DEBUG, "process_message_channel_setup:")); in_uint16_le(s, num_chans); LOGM((LOG_LEVEL_DEBUG, "process_message_channel_setup: num_chans %d", @@ -509,25 +354,6 @@ process_message_channel_setup(struct stream *s) g_memset(ci->name, 0, sizeof(ci->name)); in_uint8a(s, ci->name, 8); in_uint16_le(s, ci->id); - /* there might be leftover data from last session after reconnecting - so free it */ - if (ci->head != 0) - { - cod = ci->head; - while (1) - { - free_stream(cod->s); - old_cod = cod; - cod = cod->next; - g_free(old_cod); - if (ci->tail == old_cod) - { - break; - } - } - } - ci->head = 0; - ci->tail = 0; in_uint16_le(s, ci->flags); LOGM((LOG_LEVEL_DEBUG, "process_message_channel_setup: chan name '%s' " "id %d flags %8.8x", ci->name, ci->id, ci->flags)); @@ -556,11 +382,6 @@ process_message_channel_setup(struct stream *s) g_rail_index = g_num_chan_items; g_rail_chan_id = ci->id; } - else if (g_strcasecmp(ci->name, "drdynvc") == 0) - { - g_drdynvc_index = g_num_chan_items; // LK_TODO use this - g_drdynvc_chan_id = ci->id; // LK_TODO use this - } else { LOG(10, ("other %s", ci->name)); @@ -569,7 +390,7 @@ process_message_channel_setup(struct stream *s) g_num_chan_items++; } - rv = send_channel_setup_response_message(); + rv = 0; if (g_cliprdr_index >= 0) { @@ -593,12 +414,6 @@ process_message_channel_setup(struct stream *s) rail_init(); } - if (g_drdynvc_index >= 0) - { - g_memset(&g_dvc_channels[0], 0, sizeof(g_dvc_channels)); - drdynvc_init(); - } - return rv; } @@ -607,12 +422,13 @@ process_message_channel_setup(struct stream *s) static int process_message_channel_data(struct stream *s) { - int chan_id = 0; - int chan_flags = 0; - int rv = 0; - int length = 0; - int total_length = 0; + int chan_id; + int chan_flags; + int rv; + int length; + int total_length; int index; + int found; struct stream *ls; struct trans *ltran; struct xrdp_api_data *api_data; @@ -624,7 +440,7 @@ process_message_channel_data(struct stream *s) LOGM((LOG_LEVEL_DEBUG, "process_message_channel_data: chan_id %d " "chan_flags %d", chan_id, chan_flags)); LOG(10, ("process_message_channel_data")); - rv = send_channel_data_response_message(); + rv = 0; if (rv == 0) { @@ -644,22 +460,20 @@ process_message_channel_data(struct stream *s) { rv = rail_data_in(s, chan_id, chan_flags, length, total_length); } - else if (chan_id == g_drdynvc_chan_id) - { - rv = drdynvc_data_in(s, chan_id, chan_flags, length, total_length); - } - else if (g_api_con_trans_list != 0) + else { + found = 0; for (index = 0; index < g_api_con_trans_list->count; index++) { ltran = (struct trans *) list_get_item(g_api_con_trans_list, index); - if (ltran != 0) + if (ltran != NULL) { api_data = (struct xrdp_api_data *) (ltran->callback_data); - if (api_data != 0) + if (api_data != NULL) { if (api_data->chan_id == chan_id) { + found = 1; ls = ltran->out_s; if (chan_flags & 1) /* first */ { @@ -669,25 +483,333 @@ process_message_channel_data(struct stream *s) if (chan_flags & 2) /* last */ { s_mark_end(ls); - rv = trans_force_write(ltran); + rv = trans_write_copy(ltran); } break; } } } } + if (found == 0) + { + LOG(0, ("process_message_channel_data: not found channel %d", chan_id)); + } } + } return rv; } /*****************************************************************************/ /* returns error */ +/* open response from client */ static int -process_message_channel_data_response(struct stream *s) +process_message_drdynvc_open_response(struct stream *s) { - LOG(10, ("process_message_channel_data_response:")); - check_chan_items(); + struct chansrv_drdynvc *drdynvc; + int chan_id; + int creation_status; + + LOG(10, ("process_message_drdynvc_open_response:")); + if (!s_check_rem(s, 8)) + { + return 1; + } + in_uint32_le(s, chan_id); + in_uint32_le(s, creation_status); + if ((chan_id < 0) || (chan_id > 255)) + { + return 1; + } + drdynvc = g_drdynvcs + chan_id; + if (drdynvc->status != CHANSRV_DRDYNVC_STATUS_OPEN_SENT) + { + g_writeln("process_message_drdynvc_open_response: status not right"); + return 1; + } + if (creation_status == 0) + { + drdynvc->status = CHANSRV_DRDYNVC_STATUS_OPEN; + } + else + { + drdynvc->status = CHANSRV_DRDYNVC_STATUS_CLOSED; + } + if (drdynvc->open_response != NULL) + { + if (drdynvc->open_response(chan_id, creation_status) != 0) + { + return 1; + } + } + return 0; +} + +/*****************************************************************************/ +/* returns error */ +/* close response from client */ +static int +process_message_drdynvc_close_response(struct stream *s) +{ + struct chansrv_drdynvc *drdynvc; + int chan_id; + + LOG(10, ("process_message_drdynvc_close_response:")); + if (!s_check_rem(s, 4)) + { + return 1; + } + in_uint32_le(s, chan_id); + if ((chan_id < 0) || (chan_id > 255)) + { + return 1; + } + drdynvc = g_drdynvcs + chan_id; + if (drdynvc->status != CHANSRV_DRDYNVC_STATUS_CLOSE_SENT) + { + g_writeln("process_message_drdynvc_close_response: status not right"); + return 0; + } + drdynvc->status = CHANSRV_DRDYNVC_STATUS_CLOSED; + if (drdynvc->close_response != NULL) + { + if (drdynvc->close_response(chan_id) != 0) + { + return 1; + } + } + return 0; +} + +/*****************************************************************************/ +/* returns error */ +/* data from client */ +static int +process_message_drdynvc_data_first(struct stream *s) +{ + struct chansrv_drdynvc *drdynvc; + int chan_id; + int bytes; + int total_bytes; + char *data; + + LOG(10, ("process_message_drdynvc_data_first:")); + if (!s_check_rem(s, 12)) + { + return 1; + } + in_uint32_le(s, chan_id); + in_uint32_le(s, bytes); + in_uint32_le(s, total_bytes); + if (!s_check_rem(s, bytes)) + { + return 1; + } + in_uint8p(s, data, bytes); + if ((chan_id < 0) || (chan_id > 255)) + { + return 1; + } + drdynvc = g_drdynvcs + chan_id; + if (drdynvc->data_first != NULL) + { + if (drdynvc->data_first(chan_id, data, bytes, total_bytes) != 0) + { + return 1; + } + } + return 0; +} + +/*****************************************************************************/ +/* returns error */ +/* data from client */ +static int +process_message_drdynvc_data(struct stream *s) +{ + struct chansrv_drdynvc *drdynvc; + int chan_id; + int bytes; + char *data; + + LOG(10, ("process_message_drdynvc_data:")); + if (!s_check_rem(s, 8)) + { + return 1; + } + in_uint32_le(s, chan_id); + in_uint32_le(s, bytes); + if (!s_check_rem(s, bytes)) + { + return 1; + } + in_uint8p(s, data, bytes); + drdynvc = g_drdynvcs + chan_id; + if (drdynvc->data != NULL) + { + if (drdynvc->data(chan_id, data, bytes) != 0) + { + return 1; + } + } + return 0; +} + +/*****************************************************************************/ +/* open call from chansrv */ +int +chansrv_drdynvc_open(const char *name, int flags, + struct chansrv_drdynvc_procs *procs, int *chan_id) +{ + struct stream *s; + int name_bytes; + int lchan_id; + int error; + + lchan_id = 1; + while (g_drdynvcs[lchan_id].status != CHANSRV_DRDYNVC_STATUS_CLOSED) + { + lchan_id++; + if (lchan_id > 255) + { + return 1; + } + } + s = trans_get_out_s(g_con_trans, 8192); + if (s == NULL) + { + return 1; + } + name_bytes = g_strlen(name); + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 8 + 8 + 4 + name_bytes + 4 + 4); + out_uint32_le(s, 12); /* msg id */ + out_uint32_le(s, 8 + 4 + name_bytes + 4 + 4); + out_uint32_le(s, name_bytes); + out_uint8a(s, name, name_bytes); + out_uint32_le(s, flags); + out_uint32_le(s, lchan_id); + s_mark_end(s); + error = trans_write_copy(g_con_trans); + if (error == 0) + { + if (chan_id != NULL) + { + *chan_id = lchan_id; + g_drdynvcs[lchan_id].open_response = procs->open_response; + g_drdynvcs[lchan_id].close_response = procs->close_response; + g_drdynvcs[lchan_id].data_first = procs->data_first; + g_drdynvcs[lchan_id].data = procs->data; + g_drdynvcs[lchan_id].status = CHANSRV_DRDYNVC_STATUS_OPEN_SENT; + + } + } + return error; +} + +/*****************************************************************************/ +/* close call from chansrv */ +int +chansrv_drdynvc_close(int chan_id) +{ + struct stream *s; + int error; + + s = trans_get_out_s(g_con_trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 20); + out_uint32_le(s, 14); /* msg id */ + out_uint32_le(s, 12); + out_uint32_le(s, chan_id); + s_mark_end(s); + error = trans_write_copy(g_con_trans); + g_drdynvcs[chan_id].status = CHANSRV_DRDYNVC_STATUS_CLOSE_SENT; + return error; +} + +/*****************************************************************************/ +int +chansrv_drdynvc_data_first(int chan_id, const char *data, int data_bytes, + int total_data_bytes) +{ + struct stream *s; + int error; + + //g_writeln("chansrv_drdynvc_data_first: data_bytes %d total_data_bytes %d", + // data_bytes, total_data_bytes); + s = trans_get_out_s(g_con_trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 28 + data_bytes); + out_uint32_le(s, 16); /* msg id */ + out_uint32_le(s, 20 + data_bytes); + out_uint32_le(s, chan_id); + out_uint32_le(s, data_bytes); + out_uint32_le(s, total_data_bytes); + out_uint8a(s, data, data_bytes); + s_mark_end(s); + error = trans_write_copy(g_con_trans); + return error; +} + +/*****************************************************************************/ +int +chansrv_drdynvc_data(int chan_id, const char *data, int data_bytes) +{ + struct stream *s; + int error; + + //g_writeln("chansrv_drdynvc_data: data_bytes %d", data_bytes); + s = trans_get_out_s(g_con_trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 24 + data_bytes); + out_uint32_le(s, 18); /* msg id */ + out_uint32_le(s, 16 + data_bytes); + out_uint32_le(s, chan_id); + out_uint32_le(s, data_bytes); + out_uint8a(s, data, data_bytes); + s_mark_end(s); + error = trans_write_copy(g_con_trans); + return error; +} + +/*****************************************************************************/ +int +chansrv_drdynvc_send_data(int chan_id, const char *data, int data_bytes) +{ + int this_send_bytes; + + //g_writeln("chansrv_drdynvc_send_data: data_bytes %d", data_bytes); + if (data_bytes > 1590) + { + if (chansrv_drdynvc_data_first(chan_id, data, 1590, data_bytes) != 0) + { + return 1; + } + data_bytes -= 1590; + data += 1590; + } + while (data_bytes > 0) + { + this_send_bytes = MIN(1590, data_bytes); + if (chansrv_drdynvc_data(chan_id, data, this_send_bytes) != 0) + { + return 1; + } + data_bytes -= this_send_bytes; + data += this_send_bytes; + } return 0; } @@ -706,85 +828,200 @@ process_message(void) { return 1; } - - s = trans_get_in_s(g_con_trans); - - if (s == 0) + + s = trans_get_in_s(g_con_trans); + + if (s == 0) + { + return 1; + } + + rv = 0; + + while (s_check_rem(s, 8)) + { + next_msg = s->p; + in_uint32_le(s, id); + in_uint32_le(s, size); + next_msg += size; + + switch (id) + { + case 3: /* channel setup */ + rv = process_message_channel_setup(s); + break; + case 5: /* channel data */ + rv = process_message_channel_data(s); + break; + case 13: /* drdynvc open response */ + rv = process_message_drdynvc_open_response(s); + break; + case 15: /* drdynvc close response */ + rv = process_message_drdynvc_close_response(s); + break; + case 17: /* drdynvc data first */ + rv = process_message_drdynvc_data_first(s); + break; + case 19: /* drdynvc data */ + rv = process_message_drdynvc_data(s); + break; + default: + LOGM((LOG_LEVEL_ERROR, "process_message: unknown msg %d", id)); + break; + } + + if (rv != 0) + { + g_writeln("process_message: error rv %d id %d", rv, id); + rv = 0; + } + + s->p = next_msg; + } + + return rv; +} + +/*****************************************************************************/ +/* returns error */ +int +my_trans_data_in(struct trans *trans) +{ + struct stream *s = (struct stream *)NULL; + int size = 0; + int error = 0; + + if (trans == 0) + { + return 0; + } + + if (trans != g_con_trans) + { + return 1; + } + + LOGM((LOG_LEVEL_DEBUG, "my_trans_data_in:")); + s = trans_get_in_s(trans); + in_uint8s(s, 4); /* id */ + in_uint32_le(s, size); + error = trans_force_read(trans, size - 8); + + if (error == 0) + { + /* here, the entire message block is read in, process it */ + error = process_message(); + if (error == 0) + { + } + else + { + g_writeln("my_trans_data_in: process_message failed"); + } + } + else + { + g_writeln("my_trans_data_in: trans_force_read failed"); + } + + return error; +} + +/*****************************************************************************/ +struct trans * +get_api_trans_from_chan_id(int chan_id) +{ + return g_drdynvcs[chan_id].xrdp_api_trans; +} + +/*****************************************************************************/ +static int +my_api_open_response(int chan_id, int creation_status) +{ + struct trans *trans; + struct stream *s; + + //g_writeln("my_api_open_response: chan_id %d creation_status %d", + // chan_id, creation_status); + trans = get_api_trans_from_chan_id(chan_id); + if (trans == NULL) + { + return 1; + } + s = trans_get_out_s(trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, creation_status); + s_mark_end(s); + if (trans_write_copy(trans) != 0) + { + return 1; + } + return 0; +} + +/*****************************************************************************/ +static int +my_api_close_response(int chan_id) +{ + //g_writeln("my_api_close_response:"); + return 0; +} + +/*****************************************************************************/ +static int +my_api_data_first(int chan_id, char *data, int bytes, int total_bytes) +{ + struct trans *trans; + struct stream *s; + + //g_writeln("my_api_data_first: bytes %d total_bytes %d", bytes, total_bytes); + trans = get_api_trans_from_chan_id(chan_id); + if (trans == NULL) + { + return 1; + } + s = trans_get_out_s(trans, bytes); + if (s == NULL) { return 1; } - - rv = 0; - - while (s_check_rem(s, 8)) + out_uint8a(s, data, bytes); + s_mark_end(s); + if (trans_write_copy(trans) != 0) { - next_msg = s->p; - in_uint32_le(s, id); - in_uint32_le(s, size); - next_msg += size; - - switch (id) - { - case 1: /* init */ - rv = process_message_init(s); - break; - case 3: /* channel setup */ - rv = process_message_channel_setup(s); - break; - case 5: /* channel data */ - rv = process_message_channel_data(s); - break; - case 7: /* channel data response */ - rv = process_message_channel_data_response(s); - break; - default: - LOGM((LOG_LEVEL_ERROR, "process_message: unknown msg %d", id)); - break; - } - - if (rv != 0) - { - break; - } - - s->p = next_msg; + return 1; } - - return rv; + return 0; } /*****************************************************************************/ -/* returns error */ -int -my_trans_data_in(struct trans *trans) +static int +my_api_data(int chan_id, char *data, int bytes) { - struct stream *s = (struct stream *)NULL; - int size = 0; - int error = 0; + struct trans *trans; + struct stream *s; - if (trans == 0) + //g_writeln("my_api_data: bytes %d", bytes); + trans = get_api_trans_from_chan_id(chan_id); + if (trans == NULL) { - return 0; + return 1; } - - if (trans != g_con_trans) + s = trans_get_out_s(trans, bytes); + if (s == NULL) { return 1; } - - LOGM((LOG_LEVEL_DEBUG, "my_trans_data_in:")); - s = trans_get_in_s(trans); - in_uint8s(s, 4); /* id */ - in_uint32_le(s, size); - error = trans_force_read(trans, size - 8); - - if (error == 0) + out_uint8a(s, data, bytes); + s_mark_end(s); + if (trans_write_copy(trans) != 0) { - /* here, the entire message block is read in, process it */ - error = process_message(); + return 1; } - - return error; + return 0; } /* @@ -794,94 +1031,130 @@ my_trans_data_in(struct trans *trans) int my_api_trans_data_in(struct trans *trans) { - struct stream *s; - int bytes_read; - int i32; + struct stream *s; + struct stream *out_s; struct xrdp_api_data *ad; + int index; + int rv; + int bytes; + int ver; + int channel_name_bytes; + struct chansrv_drdynvc_procs procs; + char *chan_name; - //g_writeln("my_api_trans_data_in:"); - - LOG(10, ("my_api_trans_data_in:")); - - if (trans == 0) - { - return 0; - } - - if (g_api_con_trans_list != 0) + //g_writeln("my_api_trans_data_in: extra_flags %d", trans->extra_flags); + rv = 0; + ad = (struct xrdp_api_data *) (trans->callback_data); + s = trans_get_in_s(trans); + if (trans->extra_flags == 0) { - if (list_index_of(g_api_con_trans_list, (tintptr) trans) == -1) + in_uint32_le(s, bytes); + in_uint32_le(s, ver); + //g_writeln("my_api_trans_data_in: bytes %d ver %d", bytes, ver); + if (ver != 0) { return 1; } + trans->header_size = bytes; + trans->extra_flags = 1; } - - LOGM((LOG_LEVEL_DEBUG, "my_api_trans_data_in:")); - - s = trans_get_in_s(trans); - bytes_read = g_tcp_recv(trans->sck, s->data, 16, 0); - if (bytes_read == 16) + else if (trans->extra_flags == 1) { - if (g_memcmp(s->data, g_xrdpapi_magic, 12) == 0) + rv = 1; + in_uint32_le(s, channel_name_bytes); + //g_writeln("my_api_trans_data_in: channel_name_bytes %d", channel_name_bytes); + chan_name = g_new0(char, channel_name_bytes + 1); + if (chan_name == NULL) { - in_uint8s(s, 12); - in_uint32_le(s, bytes_read); - init_stream(s, bytes_read); - if (trans_force_read(trans, bytes_read)) - log_message(LOG_LEVEL_ERROR, "chansrv.c: error reading from transport"); + return 1; } - else if (g_tcp_select(trans->sck, 0) & 1) + in_uint8a(s, chan_name, channel_name_bytes); + in_uint32_le(s, ad->chan_flags); + //g_writeln("my_api_trans_data_in: chan_name %s chan_flags 0x%8.8x", chan_name, ad->chan_flags); + if (ad->chan_flags == 0) { - i32 = bytes_read; - bytes_read = g_tcp_recv(trans->sck, s->data + bytes_read, - 8192 * 4 - bytes_read, 0); - if (bytes_read > 0) + /* SVC */ + for (index = 0; index < g_num_chan_items; index++) { - bytes_read += i32; + if (g_strcasecmp(g_chan_items[index].name, chan_name) == 0) + { + ad->chan_id = g_chan_items[index].id; + rv = 0; + break; + } } - } - } - - //g_writeln("bytes_read %d", bytes_read); - - if (bytes_read > 0) - { - LOG(10, ("my_api_trans_data_in: got data %d", bytes_read)); - ad = (struct xrdp_api_data *) trans->callback_data; - - if (ad->dvc_chan_id < 0) - { - /* writing data to a static virtual channel */ - if (send_channel_data(ad->chan_id, s->data, bytes_read) != 0) + if (rv == 0) + { + /* open ok */ + out_s = trans_get_out_s(trans, 8192); + if (out_s == NULL) + { + return 1; + } + out_uint32_le(out_s, 0); + s_mark_end(out_s); + if (trans_write_copy(trans) != 0) + { + return 1; + } + } + else { - LOG(0, ("my_api_trans_data_in: send_channel_data failed")); + /* open failed */ + out_s = trans_get_out_s(trans, 8192); + if (out_s == NULL) + { + return 1; + } + out_uint32_le(out_s, 1); + s_mark_end(out_s); + if (trans_write_copy(trans) != 0) + { + return 1; + } } } else { - /* writing data to a dynamic virtual channel */ - drdynvc_write_data(ad->dvc_chan_id, s->data, bytes_read); + /* DVS */ + g_memset(&procs, 0, sizeof(procs)); + procs.open_response = my_api_open_response; + procs.close_response = my_api_close_response; + procs.data_first = my_api_data_first; + procs.data = my_api_data; + rv = chansrv_drdynvc_open(chan_name, ad->chan_flags, + &procs, &(ad->chan_id)); + //g_writeln("my_api_trans_data_in: chansrv_drdynvc_open rv %d " + // "chan_id %d", rv, ad->chan_id); + g_drdynvcs[ad->chan_id].xrdp_api_trans = trans; } + g_free(chan_name); + init_stream(s, 0); + trans->extra_flags = 2; + trans->header_size = 0; } else { - ad = (struct xrdp_api_data *) (trans->callback_data); - if ((ad != NULL) && (ad->dvc_chan_id > 0)) + bytes = g_sck_recv(trans->sck, s->data, s->size, 0); + if (bytes < 1) { - /* WTSVirtualChannelClose() was invoked, or connection dropped */ - LOG(10, ("my_api_trans_data_in: g_tcp_recv failed or disconnected for DVC")); - ad->transp = NULL; - ad->is_connected = 0; - remove_struct_with_chan_id(ad->dvc_chan_id); + //g_writeln("my_api_trans_data_in: disconnect"); + return 1; + } + if (ad->chan_flags == 0) + { + /* SVC */ + rv = send_channel_data(ad->chan_id, s->data, bytes); } else { - LOG(10, ("my_api_trans_data_in: g_tcp_recv failed or disconnected for SVC")); + /* DVS */ + //g_writeln("my_api_trans_data_in: s->size %d bytes %d", s->size, bytes); + rv = chansrv_drdynvc_send_data(ad->chan_id, s->data, bytes); } - return 1; + init_stream(s, 0); } - - return 0; + return rv; } /*****************************************************************************/ @@ -926,92 +1199,24 @@ int my_api_trans_conn_in(struct trans *trans, struct trans *new_trans) { struct xrdp_api_data *ad; - struct stream *s; - int error; - int index; - char chan_pri; - if ((trans == 0) || (trans != g_api_lis_trans) || (new_trans == 0)) + //g_writeln("my_api_trans_conn_in:"); + if ((trans == NULL) || (trans != g_api_lis_trans) || (new_trans == NULL)) { + g_writeln("my_api_trans_conn_in: error"); return 1; } - - LOGM((LOG_LEVEL_DEBUG, "my_api_trans_conn_in:")); - LOG(10, ("my_api_trans_conn_in: got incoming")); - - s = trans_get_in_s(new_trans); - s->end = s->data; - - error = trans_force_read(new_trans, 64); - - if (error != 0) + new_trans->trans_data_in = my_api_trans_data_in; + new_trans->header_size = 8; + new_trans->no_stream_init_on_data_in = 1; + ad = g_new0(struct xrdp_api_data, 1); + if (ad == NULL) { - LOG(0, ("my_api_trans_conn_in: trans_force_read failed")); - trans_delete(new_trans); + g_writeln("my_api_trans_conn_in: error"); return 1; } - - s->end = s->data; - - ad = (struct xrdp_api_data *) g_malloc(sizeof(struct xrdp_api_data), 1); - g_memcpy(ad->header, s->data, 64); - - ad->flags = GGET_UINT32(ad->header, 16); - ad->chan_id = -1; - ad->dvc_chan_id = -1; - - if (ad->flags > 0) - { - /* opening a dynamic virtual channel */ - - if ((index = find_empty_slot_in_dvc_channels()) < 0) - { - /* exceeded MAX_DVC_CHANNELS */ - LOG(0, ("my_api_trans_conn_in: MAX_DVC_CHANNELS reached; giving up!")) - g_free(ad); - trans_delete(new_trans); - return 1; - } - - g_dvc_channels[index] = ad; - chan_pri = 4 - ad->flags; - ad->dvc_chan_id = g_dvc_chan_id++; - ad->is_connected = 0; - ad->transp = new_trans; - drdynvc_send_open_channel_request(chan_pri, ad->dvc_chan_id, ad->header); - } - else - { - /* opening a static virtual channel */ - - for (index = 0; index < g_num_chan_items; index++) - { - LOG(10, ("my_api_trans_conn_in: %s %s", ad->header, - g_chan_items[index].name)); - - if (g_strcasecmp(ad->header, g_chan_items[index].name) == 0) - { - LOG(10, ("my_api_trans_conn_in: found it at %d", index)); - ad->chan_id = g_chan_items[index].id; - break; - } - } - if (index == g_num_chan_items) - { - g_writeln("did not find SVC named %s", ad->header); - } - } - new_trans->callback_data = ad; - - if (g_api_con_trans_list == 0) - { - g_api_con_trans_list = list_create(); - } - new_trans->trans_data_in = my_api_trans_data_in; - new_trans->header_size = 0; - list_add_item(g_api_con_trans_list, (tintptr) new_trans); - + list_add_item(g_api_con_trans_list, (intptr_t) new_trans); return 0; } @@ -1076,6 +1281,96 @@ setup_api_listen(void) return 0; } +/*****************************************************************************/ +static int +api_con_trans_list_get_wait_objs_rw(intptr_t *robjs, int *rcount, + intptr_t *wobjs, int *wcount, + int *timeout) +{ + int api_con_index; + struct trans *ltran; + + for (api_con_index = g_api_con_trans_list->count - 1; + api_con_index >= 0; + api_con_index--) + { + ltran = (struct trans *) + list_get_item(g_api_con_trans_list, api_con_index); + if (ltran != NULL) + { + trans_get_wait_objs_rw(ltran, robjs, rcount, wobjs, wcount, + timeout); + } + } + return 0; +} + +/*****************************************************************************/ +static int +api_con_trans_list_check_wait_objs(void) +{ + int api_con_index; + int drdynvc_index; + struct trans *ltran; + struct xrdp_api_data *ad; + + for (api_con_index = g_api_con_trans_list->count - 1; + api_con_index >= 0; + api_con_index--) + { + ltran = (struct trans *) + list_get_item(g_api_con_trans_list, api_con_index); + if (ltran != NULL) + { + if (trans_check_wait_objs(ltran) != 0) + { + /* disconnect */ + list_remove_item(g_api_con_trans_list, api_con_index); + ad = (struct xrdp_api_data *) (ltran->callback_data); + if (ad->chan_flags != 0) + { + chansrv_drdynvc_close(ad->chan_id); + } + for (drdynvc_index = 0; + drdynvc_index < (int) ARRAYSIZE(g_drdynvcs); + drdynvc_index++) + { + if (g_drdynvcs[drdynvc_index].xrdp_api_trans == ltran) + { + g_drdynvcs[drdynvc_index].xrdp_api_trans = NULL; + } + } + g_free(ad); + trans_delete(ltran); + } + } + } + return 0; +} + +/*****************************************************************************/ +static int +api_con_trans_list_remove_all(void) +{ + int api_con_index; + struct trans *ltran; + + for (api_con_index = g_api_con_trans_list->count - 1; + api_con_index >= 0; + api_con_index--) + { + ltran = (struct trans *) + list_get_item(g_api_con_trans_list, api_con_index); + if (ltran != NULL) + { + list_remove_item(g_api_con_trans_list, api_con_index); + g_free(ltran->callback_data); + trans_delete(ltran); + } + } + return 0; +} + /*****************************************************************************/ THREAD_RV THREAD_CC channel_thread_loop(void *in_val) @@ -1086,12 +1381,11 @@ channel_thread_loop(void *in_val) int num_wobjs; int timeout; int error; - int index; THREAD_RV rv; - struct trans *ltran; LOGM((LOG_LEVEL_INFO, "channel_thread_loop: thread start")); rv = 0; + g_api_con_trans_list = list_create(); setup_api_listen(); error = setup_listen(); @@ -1105,6 +1399,7 @@ channel_thread_loop(void *in_val) trans_get_wait_objs(g_lis_trans, objs, &num_objs); trans_get_wait_objs(g_api_lis_trans, objs, &num_objs); + //g_writeln("timeout %d", timeout); while (g_obj_wait(objs, num_objs, wobjs, num_wobjs, timeout) == 0) { check_timeout(); @@ -1157,24 +1452,8 @@ channel_thread_loop(void *in_val) LOG(0, ("channel_thread_loop: trans_check_wait_objs failed")); } } - - if (g_api_con_trans_list != 0) - { - for (index = g_api_con_trans_list->count - 1; index >= 0; index--) - { - ltran = (struct trans *) list_get_item(g_api_con_trans_list, index); - if (ltran != 0) - { - if (trans_check_wait_objs(ltran) != 0) - { - list_remove_item(g_api_con_trans_list, index); - g_free(ltran->callback_data); - trans_delete(ltran); - } - } - } - } - + /* check the wait_objs in g_api_con_trans_list */ + api_con_trans_list_check_wait_objs(); xcommon_check_wait_objs(); sound_check_wait_objs(); dev_redir_check_wait_objs(); @@ -1184,23 +1463,16 @@ channel_thread_loop(void *in_val) num_wobjs = 0; objs[num_objs] = g_term_event; num_objs++; - trans_get_wait_objs(g_lis_trans, objs, &num_objs); + trans_get_wait_objs_rw(g_lis_trans, objs, &num_objs, + wobjs, &num_wobjs, &timeout); trans_get_wait_objs_rw(g_con_trans, objs, &num_objs, wobjs, &num_wobjs, &timeout); - trans_get_wait_objs(g_api_lis_trans, objs, &num_objs); - - if (g_api_con_trans_list != 0) - { - for (index = g_api_con_trans_list->count - 1; index >= 0; index--) - { - ltran = (struct trans *) list_get_item(g_api_con_trans_list, index); - if (ltran != 0) - { - trans_get_wait_objs(ltran, objs, &num_objs); - } - } - } - + trans_get_wait_objs_rw(g_api_lis_trans, objs, &num_objs, + wobjs, &num_wobjs, &timeout); + /* get the wait_objs from in g_api_con_trans_list */ + api_con_trans_list_get_wait_objs_rw(objs, &num_objs, + wobjs, &num_wobjs, + &timeout); xcommon_get_wait_objs(objs, &num_objs, &timeout); sound_get_wait_objs(objs, &num_objs, &timeout); dev_redir_get_wait_objs(objs, &num_objs, &timeout); @@ -1215,20 +1487,8 @@ channel_thread_loop(void *in_val) g_con_trans = 0; trans_delete(g_api_lis_trans); g_api_lis_trans = 0; - if (g_api_con_trans_list != 0) - { - for (index = g_api_con_trans_list->count - 1; index >= 0; index--) - { - ltran = (struct trans *) list_get_item(g_api_con_trans_list, index); - if (ltran != 0) - { - list_remove_item(g_api_con_trans_list, index); - g_free(ltran->callback_data); - trans_delete(ltran); - } - } - list_delete(g_api_con_trans_list); - } + api_con_trans_list_remove_all(); + list_delete(g_api_con_trans_list); LOGM((LOG_LEVEL_INFO, "channel_thread_loop: thread stop")); g_set_wait_obj(g_thread_done_event); return rv; @@ -1551,6 +1811,8 @@ main(int argc, char **argv) g_file_delete(log_file); } + g_memset(g_drdynvcs, 0, sizeof(g_drdynvcs)); + logconfig.log_file = log_file; logconfig.fd = -1; logconfig.log_level = log_level; @@ -1649,66 +1911,3 @@ main(int argc, char **argv) return 0; } -/* - * return unused slot in dvc_channels[] - * - * @return unused slot index on success, -1 on failure - ******************************************************************************/ -int -find_empty_slot_in_dvc_channels(void) -{ - int i; - - for (i = 0; i < MAX_DVC_CHANNELS; i++) - { - if (g_dvc_channels[i] == NULL) - { - return i; - } - } - - return -1; -} - -/* - * return struct xrdp_api_data that contains specified dvc_chan_id - * - * @param dvc_chan_id channel id to look for - * - * @return xrdp_api_data struct containing dvc_chan_id or NULL on failure - ******************************************************************************/ -struct xrdp_api_data * -struct_from_dvc_chan_id(tui32 dvc_chan_id) -{ - int i; - - for (i = 0; i < MAX_DVC_CHANNELS; i++) - { - if (g_dvc_channels[i] != NULL && - g_dvc_channels[i]->dvc_chan_id >= 0 && - (tui32) g_dvc_channels[i]->dvc_chan_id == dvc_chan_id) - { - return g_dvc_channels[i]; - } - } - - return NULL; -} - -int -remove_struct_with_chan_id(tui32 dvc_chan_id) -{ - int i; - - for (i = 0; i < MAX_DVC_CHANNELS; i++) - { - if (g_dvc_channels[i] != NULL && - g_dvc_channels[i]->dvc_chan_id >= 0 && - (tui32) g_dvc_channels[i]->dvc_chan_id == dvc_chan_id) - { - g_dvc_channels[i] = NULL; - return 0; - } - } - return -1; -} diff --git a/sesman/chansrv/chansrv.h b/sesman/chansrv/chansrv.h index e75810c8..e1a0e34f 100644 --- a/sesman/chansrv/chansrv.h +++ b/sesman/chansrv/chansrv.h @@ -24,46 +24,20 @@ #include "parse.h" #include "log.h" -#define MAX_DVC_CHANNELS 32 - -struct chan_out_data -{ - struct stream *s; - struct chan_out_data *next; -}; - struct chan_item { int id; int flags; char name[16]; - struct chan_out_data *head; - struct chan_out_data *tail; -}; - -/* data in struct trans::callback_data */ -struct xrdp_api_data -{ - int chan_id; - char header[64]; - int flags; - - /* for dynamic virtual channels */ - struct trans *transp; - int dvc_chan_id; - int is_connected; }; int g_is_term(void); -int send_channel_data(int chan_id, char *data, int size); +int send_channel_data(int chan_id, const char *data, int size); int send_rail_drawing_orders(char* data, int size); int main_cleanup(void); int add_timeout(int msoffset, void (*callback)(void* data), void* data); -int find_empty_slot_in_dvc_channels(void); -struct xrdp_api_data * struct_from_dvc_chan_id(tui32 dvc_chan_id); -int remove_struct_with_chan_id(tui32 dvc_chan_id); #define LOG_LEVEL 5 @@ -97,4 +71,25 @@ int remove_struct_with_chan_id(tui32 dvc_chan_id); ((GGET_UINT16(_ptr, (_offset) + 2)) << 16) #endif +struct chansrv_drdynvc_procs +{ + int (*open_response)(int chan_id, int creation_status); + int (*close_response)(int chan_id); + int (*data_first)(int chan_id, char *data, int bytes, int total_bytes); + int (*data)(int chan_id, char *data, int bytes); +}; + +int +chansrv_drdynvc_open(const char *name, int flags, + struct chansrv_drdynvc_procs *procs, int *chan_id); +int +chansrv_drdynvc_close(int chan_id); +int +chansrv_drdynvc_data_first(int chan_id, const char *data, int data_bytes, + int total_data_bytes); +int +chansrv_drdynvc_data(int chan_id, const char *data, int data_bytes); +int +chansrv_drdynvc_send_data(int chan_id, const char *data, int data_bytes); + #endif diff --git a/sesman/chansrv/drdynvc.c b/sesman/chansrv/drdynvc.c deleted file mode 100644 index c02bdadb..00000000 --- a/sesman/chansrv/drdynvc.c +++ /dev/null @@ -1,531 +0,0 @@ -/** - * xrdp: A Remote Desktop Protocol server. - * - * 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. - * 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. - */ - -#if defined(HAVE_CONFIG_H) -#include -#endif - -#include "drdynvc.h" - -extern int g_drdynvc_chan_id; /* in chansrv.c */ -int g_drdynvc_inited = 0; - -static int drdynvc_send_capability_request(uint16_t version); -static int drdynvc_process_capability_response(struct stream* s, unsigned char cmd); -static int drdynvc_process_open_channel_response(struct stream *s, unsigned char cmd); -static int drdynvc_process_close_channel_response(struct stream *s, unsigned char cmd); -static int drdynvc_process_data_first(struct stream* s, unsigned char cmd); -static int drdynvc_process_data(struct stream* s, unsigned char cmd); -static int drdynvc_insert_uint_124(struct stream *s, uint32_t val); -static int drdynvc_get_chan_id(struct stream *s, char cmd, uint32_t *chan_id_p); - -/** - * bring up dynamic virtual channel - * - * @return 0 on success, -1 on response - ******************************************************************************/ -int -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 -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 -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) && (cap_version != 3)) - { - LOG(0, ("drdynvc_process_capability_response: incompatible DVC " - "version %d detected", cap_version)); - return -1; - } - - LOG(0, ("drdynvc_process_capability_response: DVC version %d selected", - cap_version)); - 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 -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 -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 -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 -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 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 -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 Len; - - drdynvc_get_chan_id(s, cmd, &chan_id); - - Len = (cmd >> 2) & 0x03; - - /* skip data_len */ - if (Len == 0) - { - in_uint8s(s, 1); - } - else if (Len == 1) - { - in_uint8s(s, 2); - } - else - { - in_uint8s(s, 4); - } - - 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 -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 chan_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 -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 -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 -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; -} diff --git a/sesman/chansrv/drdynvc.h b/sesman/chansrv/drdynvc.h deleted file mode 100644 index cd6a5fca..00000000 --- a/sesman/chansrv/drdynvc.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * xrdp: A Remote Desktop Protocol server. - * - * 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. - * 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. - */ - -#ifndef _DRDYNVC_H_ -#define _DRDYNVC_H_ - -#include -#include -#include -#include - -#include "arch.h" -#include "chansrv.h" -#include "xcommon.h" -#include "log.h" -#include "os_calls.h" -#include "trans.h" - -/* move this to tsmf.c */ -#define TSMF_CHAN_ID 0x1000 - -/* get number of bytes in stream before s->p */ -#define stream_length_before_p(s) (int) ((s)->p - (s)->data) - -/* get number of bytes in stream after s->p */ -#define stream_length_after_p(s) (int) ((s)->end - (s)->p) - -#define rewind_stream(s) do \ -{ \ - (s)->p = (s)->data; \ - (s)->end = (s)->data; \ -} while (0) - -/* max number of bytes we can send in one pkt */ -#define MAX_PDU_SIZE 1600 - -/* commands used to manage dynamic virtual channels */ -#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 - -int drdynvc_init(void); -int drdynvc_send_open_channel_request(int chan_pri, unsigned int chan_id, - char *chan_name); -int drdynvc_send_close_channel_request(unsigned int chan_id); -int drdynvc_write_data(uint32_t chan_id, char *data, int data_size); -int drdynvc_data_in(struct stream* s, int chan_id, int chan_flags, - int length, int total_length); - -#endif diff --git a/xrdp/xrdp.h b/xrdp/xrdp.h index 65e5c4f6..5ed46a3a 100644 --- a/xrdp/xrdp.h +++ b/xrdp/xrdp.h @@ -138,6 +138,8 @@ int callback(intptr_t id, int msg, intptr_t param1, intptr_t param2, intptr_t param3, intptr_t param4); int +xrdp_wm_drdynvc_up(intptr_t id); +int xrdp_wm_delete_all_children(struct xrdp_wm* self); int xrdp_wm_show_log(struct xrdp_wm *self); @@ -367,6 +369,8 @@ xrdp_bitmap_compress(char* in_data, int width, int height, int e); /* xrdp_mm.c */ +int +xrdp_mm_drdynvc_up(struct xrdp_mm* self); struct xrdp_mm* xrdp_mm_create(struct xrdp_wm* owner); void diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 4c716d46..7d204b5b 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -679,71 +679,28 @@ xrdp_mm_trans_send_channel_setup(struct xrdp_mm *self, struct trans *trans) return trans_force_write(trans); } -/*****************************************************************************/ -/* returns error */ -static int -xrdp_mm_trans_send_channel_data_response(struct xrdp_mm *self, - struct trans *trans) -{ - struct stream *s; - - s = trans_get_out_s(trans, 8192); - - if (s == 0) - { - return 1; - } - - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8); /* size */ - out_uint32_le(s, 7); /* msg id */ - out_uint32_le(s, 8); /* size */ - s_mark_end(s); - return trans_force_write(trans); -} - -/*****************************************************************************/ -/* returns error - init is done, sent channel setup */ -static int -xrdp_mm_trans_process_init_response(struct xrdp_mm *self, struct trans *trans) -{ - return xrdp_mm_trans_send_channel_setup(self, trans); -} - /*****************************************************************************/ /* returns error data coming in from the channel handler, send it to the client */ static int -xrdp_mm_trans_process_channel_data(struct xrdp_mm *self, struct trans *trans) +xrdp_mm_trans_process_channel_data(struct xrdp_mm *self, struct stream *s) { - struct stream *s; int size; int total_size; int chan_id; int chan_flags; int rv; - s = trans_get_in_s(trans); - - if (s == 0) - { - return 1; - } - in_uint16_le(s, chan_id); in_uint16_le(s, chan_flags); in_uint16_le(s, size); in_uint32_le(s, total_size); - rv = xrdp_mm_trans_send_channel_data_response(self, trans); + rv = 0; if (rv == 0) { - if (is_channel_allowed(self->wm, chan_id)) - { - rv = libxrdp_send_to_channel(self->wm->session, chan_id, s->p, size, total_size, - chan_flags); - } + rv = libxrdp_send_to_channel(self->wm->session, chan_id, s->p, size, total_size, + chan_flags); } return rv; @@ -970,12 +927,11 @@ xrdp_mm_process_rail_update_window_text(struct xrdp_mm* self, struct stream* s) { int size; int flags; - int rv = 0; + int rv; int window_id; struct rail_window_state_order rwso; g_writeln("xrdp_mm_process_rail_update_window_text:"); - in_uint32_le(s, window_id); in_uint32_le(s, flags); g_writeln(" update window title info: 0x%8.8x", window_id); @@ -1006,17 +962,12 @@ xrdp_mm_process_rail_update_window_text(struct xrdp_mm* self, struct stream* s) /* returns error process alternate secondary drawing orders for rail channel */ static int -xrdp_mm_process_rail_drawing_orders(struct xrdp_mm* self, struct trans* trans) +xrdp_mm_process_rail_drawing_orders(struct xrdp_mm* self, struct stream *s) { - struct stream* s; int order_type; - int rv = 0; + int rv; - s = trans_get_in_s(trans); - if (s == 0) - { - return 1; - } + rv = 0; in_uint32_le(s, order_type); switch(order_type) @@ -1040,6 +991,302 @@ xrdp_mm_process_rail_drawing_orders(struct xrdp_mm* self, struct trans* trans) return rv; } +/******************************************************************************/ +int +xrdp_mm_drdynvc_up(struct xrdp_mm* self) +{ + LLOGLN(0, ("xrdp_mm_drdynvc_up:")); + return 0; +} + +/*****************************************************************************/ +/* open response from client going to channel server */ +static int +xrdp_mm_drdynvc_open_response(intptr_t id, int chan_id, int creation_status) +{ + struct trans *trans; + struct stream *s; + struct xrdp_wm* wm; + struct xrdp_process *pro; + int chansrv_chan_id; + + LLOGLN(10, ("xrdp_mm_drdynvc_open_response: chan_id %d creation_status %d", + chan_id, creation_status)); + pro = (struct xrdp_process *) id; + wm = pro->wm; + trans = wm->mm->chan_trans; + s = trans_get_out_s(trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 24); /* size */ + out_uint32_le(s, 13); /* msg id */ + out_uint32_le(s, 16); /* size */ + chansrv_chan_id = wm->mm->xr2cr_cid_map[chan_id]; + out_uint32_le(s, chansrv_chan_id); + out_uint32_le(s, creation_status); /* status */ + s_mark_end(s); + return trans_write_copy(trans); +} + +/*****************************************************************************/ +/* close response from client going to channel server */ +static int +xrdp_mm_drdynvc_close_response(intptr_t id, int chan_id) +{ + struct trans *trans; + struct stream *s; + struct xrdp_wm* wm; + struct xrdp_process *pro; + int chansrv_chan_id; + + pro = (struct xrdp_process *) id; + wm = pro->wm; + trans = wm->mm->chan_trans; + s = trans_get_out_s(trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 20); /* size */ + out_uint32_le(s, 15); /* msg id */ + out_uint32_le(s, 12); /* size */ + chansrv_chan_id = wm->mm->xr2cr_cid_map[chan_id]; + out_uint32_le(s, chansrv_chan_id); + s_mark_end(s); + return trans_write_copy(trans); +} + +/*****************************************************************************/ +/* part data from client going to channel server */ +static int +xrdp_mm_drdynvc_data_first(intptr_t id, int chan_id, char *data, + int bytes, int total_bytes) +{ + struct trans *trans; + struct stream *s; + struct xrdp_wm* wm; + struct xrdp_process *pro; + int chansrv_chan_id; + + pro = (struct xrdp_process *) id; + wm = pro->wm; + trans = wm->mm->chan_trans; + s = trans_get_out_s(trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 8 + 8 + 4 + 4 + 4 + bytes); + out_uint32_le(s, 17); /* msg id */ + out_uint32_le(s, 8 + 4 + 4 + 4 + bytes); + chansrv_chan_id = wm->mm->xr2cr_cid_map[chan_id]; + out_uint32_le(s, chansrv_chan_id); + out_uint32_le(s, bytes); + out_uint32_le(s, total_bytes); + out_uint8a(s, data, bytes); + s_mark_end(s); + return trans_write_copy(trans); +} + +/*****************************************************************************/ +/* data from client going to channel server */ +static int +xrdp_mm_drdynvc_data(intptr_t id, int chan_id, char *data, int bytes) +{ + struct trans *trans; + struct stream *s; + struct xrdp_wm* wm; + struct xrdp_process *pro; + int chansrv_chan_id; + + pro = (struct xrdp_process *) id; + wm = pro->wm; + trans = wm->mm->chan_trans; + s = trans_get_out_s(trans, 8192); + if (s == NULL) + { + return 1; + } + out_uint32_le(s, 0); /* version */ + out_uint32_le(s, 8 + 8 + 4 + 4 + bytes); + out_uint32_le(s, 19); /* msg id */ + out_uint32_le(s, 8 + 4 + 4 + bytes); + chansrv_chan_id = wm->mm->xr2cr_cid_map[chan_id]; + out_uint32_le(s, chansrv_chan_id); + out_uint32_le(s, bytes); + out_uint8a(s, data, bytes); + s_mark_end(s); + return trans_write_copy(trans); +} + +/*****************************************************************************/ +/* open message from channel server going to client */ +static int +xrdp_mm_trans_process_drdynvc_channel_open(struct xrdp_mm* self, + struct stream *s) +{ + int name_bytes; + int flags; + int error; + int chan_id; + int chansrv_chan_id; + char *name; + struct xrdp_drdynvc_procs procs; + + if (!s_check_rem(s, 2)) + { + return 1; + } + in_uint32_le(s, name_bytes); + if ((name_bytes < 1) || (name_bytes > 1024)) + { + return 1; + } + name = g_new(char, name_bytes + 1); + if (name == NULL) + { + return 1; + } + if (!s_check_rem(s, name_bytes)) + { + g_free(name); + return 1; + } + in_uint8a(s, name, name_bytes); + name[name_bytes] = 0; + if (!s_check_rem(s, 8)) + { + g_free(name); + return 1; + } + in_uint32_le(s, flags); + in_uint32_le(s, chansrv_chan_id); + if (flags == 0) + { + /* open static channel, not supported */ + g_free(name); + return 1; + } + else + { + /* dynamic channel */ + g_memset(&procs, 0, sizeof(procs)); + procs.open_response = xrdp_mm_drdynvc_open_response; + procs.close_response = xrdp_mm_drdynvc_close_response; + procs.data_first = xrdp_mm_drdynvc_data_first; + procs.data = xrdp_mm_drdynvc_data; + chan_id = 0; + error = libxrdp_drdynvc_open(self->wm->session, name, flags, &procs, + &chan_id); + if (error != 0) + { + g_free(name); + return 1; + } + self->xr2cr_cid_map[chan_id] = chansrv_chan_id; + self->cs2xr_cid_map[chansrv_chan_id] = chan_id; + } + g_free(name); + return 0; +} + +/*****************************************************************************/ +/* close message from channel server going to client */ +static int +xrdp_mm_trans_process_drdynvc_channel_close(struct xrdp_mm* self, + struct stream *s) +{ + int chansrv_chan_id; + int chan_id; + int error; + + if (!s_check_rem(s, 4)) + { + return 1; + } + in_uint32_le(s, chansrv_chan_id); + chan_id = self->cs2xr_cid_map[chansrv_chan_id]; + /* close dynamic channel */ + error = libxrdp_drdynvc_close(self->wm->session, chan_id); + if (error != 0) + { + return 1; + } + return 0; +} + +/*****************************************************************************/ +/* data from channel server going to client */ +static int +xrdp_mm_trans_process_drdynvc_data_first(struct xrdp_mm* self, + struct stream *s) +{ + int chansrv_chan_id; + int chan_id; + int error; + int data_bytes; + int total_bytes; + char *data; + + if (!s_check_rem(s, 12)) + { + return 1; + } + in_uint32_le(s, chansrv_chan_id); + in_uint32_le(s, data_bytes); + in_uint32_le(s, total_bytes); + if ((!s_check_rem(s, data_bytes))) + { + return 1; + } + in_uint8p(s, data, data_bytes); + chan_id = self->cs2xr_cid_map[chansrv_chan_id]; + error = libxrdp_drdynvc_data_first(self->wm->session, chan_id, data, + data_bytes, total_bytes); + if (error != 0) + { + return 1; + } + return 0; +} + +/*****************************************************************************/ +/* data from channel server going to client */ +static int +xrdp_mm_trans_process_drdynvc_data(struct xrdp_mm* self, + struct stream *s) +{ + int chansrv_chan_id; + int chan_id; + int error; + int data_bytes; + char *data; + + if (!s_check_rem(s, 8)) + { + return 1; + } + in_uint32_le(s, chansrv_chan_id); + in_uint32_le(s, data_bytes); + if ((!s_check_rem(s, data_bytes))) + { + return 1; + } + in_uint8p(s, data, data_bytes); + chan_id = self->cs2xr_cid_map[chansrv_chan_id]; + error = libxrdp_drdynvc_data(self->wm->session, chan_id, data, data_bytes); + if (error != 0) + { + return 1; + } + return 0; +} + /*****************************************************************************/ /* returns error process a message for the channel handler */ @@ -1051,6 +1298,7 @@ xrdp_mm_chan_process_msg(struct xrdp_mm *self, struct trans *trans, int id; int size; char *next_msg; + char *s_end; rv = 0; @@ -1059,31 +1307,47 @@ xrdp_mm_chan_process_msg(struct xrdp_mm *self, struct trans *trans, next_msg = s->p; in_uint32_le(s, id); in_uint32_le(s, size); + if (size < 8) + { + return 1; + } + if (!s_check_rem(s, size - 8)) + { + return 1; + } next_msg += size; - + s_end = s->end; + s->end = next_msg; + LLOGLN(10, ("xrdp_mm_chan_process_msg: got msg id %d", id)); switch (id) { - case 2: /* channel init response */ - rv = xrdp_mm_trans_process_init_response(self, trans); + case 8: /* channel data */ + rv = xrdp_mm_trans_process_channel_data(self, s); break; - case 4: /* channel setup response */ + case 10: /* rail alternate secondary drawing orders */ + rv = xrdp_mm_process_rail_drawing_orders(self, s); break; - case 6: /* channel data response */ + case 12: + rv = xrdp_mm_trans_process_drdynvc_channel_open(self, s); break; - case 8: /* channel data */ - rv = xrdp_mm_trans_process_channel_data(self, trans); + case 14: + rv = xrdp_mm_trans_process_drdynvc_channel_close(self, s); break; - case 10: /* rail alternate secondary drawing orders */ - rv = xrdp_mm_process_rail_drawing_orders(self, trans); + case 16: + rv = xrdp_mm_trans_process_drdynvc_data_first(self, s); + break; + case 18: + rv = xrdp_mm_trans_process_drdynvc_data(self, s); break; default: log_message(LOG_LEVEL_ERROR,"xrdp_mm_chan_process_msg: unknown id %d", id); break; } - + s->end = s_end; if (rv != 0) { - break; + LLOGLN(0, ("xrdp_mm_chan_process_msg: error rv %d id %d", rv, id)); + rv = 0; } s->p = next_msg; @@ -1116,40 +1380,28 @@ xrdp_mm_chan_data_in(struct trans *trans) return 1; } - in_uint8s(s, 4); /* id */ - in_uint32_le(s, size); - error = trans_force_read(trans, size - 8); - - if (error == 0) + if (trans->extra_flags == 0) { - /* here, the entire message block is read in, process it */ - error = xrdp_mm_chan_process_msg(self, trans, s); + in_uint8s(s, 4); /* id */ + in_uint32_le(s, size); + LLOGLN(10, ("xrdp_mm_chan_data_in: got header, size %d", size)); + if (size > 8) + { + self->chan_trans->header_size = size; + trans->extra_flags = 1; + return 0; + } } - + /* here, the entire message block is read in, process it */ + error = xrdp_mm_chan_process_msg(self, trans, s); + self->chan_trans->header_size = 8; + trans->extra_flags = 0; + init_stream(s, 0); + LLOGLN(10, ("xrdp_mm_chan_data_in: got whole message, reset for " + "next header")); return error; } -/*****************************************************************************/ -static int -xrdp_mm_chan_send_init(struct xrdp_mm *self) -{ - struct stream *s; - - s = trans_get_out_s(self->chan_trans, 8192); - - if (s == 0) - { - return 1; - } - - out_uint32_le(s, 0); /* version */ - out_uint32_le(s, 8 + 8); /* size */ - out_uint32_le(s, 1); /* msg id */ - out_uint32_le(s, 8); /* size */ - s_mark_end(s); - return trans_force_write(self->chan_trans); -} - /*****************************************************************************/ /* connect to chansrv */ static int @@ -1177,6 +1429,8 @@ xrdp_mm_connect_chansrv(struct xrdp_mm *self, const char *ip, const char *port) self->chan_trans->trans_data_in = xrdp_mm_chan_data_in; self->chan_trans->header_size = 8; self->chan_trans->callback_data = self; + self->chan_trans->no_stream_init_on_data_in = 1; + self->chan_trans->extra_flags = 0; /* try to connect up to 4 times */ for (index = 0; index < 4; index++) @@ -1200,10 +1454,10 @@ xrdp_mm_connect_chansrv(struct xrdp_mm *self, const char *ip, const char *port) if (self->chan_trans_up) { - if (xrdp_mm_chan_send_init(self) != 0) + if (xrdp_mm_trans_send_channel_setup(self, self->chan_trans) != 0) { log_message(LOG_LEVEL_ERROR,"xrdp_mm_connect_chansrv: error in " - "xrdp_mm_chan_send_init"); + "xrdp_mm_trans_send_channel_setup"); } else { @@ -1227,6 +1481,47 @@ static void cleanup_sesman_connection(struct xrdp_mm *self) } } +/*****************************************************************************/ +/* does the section in xrdp.ini has any channel.*=true | false */ +static int +xrdp_mm_update_allowed_channels(struct xrdp_mm *self) +{ + int index; + int count; + int chan_id; + int disabled; + const char *name; + const char *value; + const char *chan_name; + struct xrdp_session *session; + + session = self->wm->session; + count = self->login_names->count; + for (index = 0; index < count; index++) + { + name = (const char *) list_get_item(self->login_names, index); + if (g_strncasecmp(name, "channel.", 8) == 0) + { + value = (const char *) list_get_item(self->login_values, index); + chan_name = name + 8; + chan_id = libxrdp_get_channel_id(session, chan_name); + disabled = !g_text2bool(value); + libxrdp_disable_channel(session, chan_id, disabled); + if (disabled) + { + g_writeln("xrdp_mm_update_allowed_channels: channel %s " + "channel id %d is disabled", chan_name, chan_id); + } + else + { + g_writeln("xrdp_mm_update_allowed_channels: channel %s " + "channel id %d is allowed", chan_name, chan_id); + } + } + } + return 0; +} + /*****************************************************************************/ static int xrdp_mm_process_login_response(struct xrdp_mm *self, struct stream *s) @@ -1271,7 +1566,7 @@ xrdp_mm_process_login_response(struct xrdp_mm *self, struct stream *s) { g_snprintf(port, 255, "%d", 7200 + display); } - + xrdp_mm_update_allowed_channels(self); xrdp_mm_connect_chansrv(self, ip, port); } } @@ -2068,7 +2363,8 @@ xrdp_mm_get_wait_objs(struct xrdp_mm *self, if ((self->chan_trans != 0) && self->chan_trans_up) { - trans_get_wait_objs(self->chan_trans, read_objs, rcount); + trans_get_wait_objs_rw(self->chan_trans, read_objs, rcount, + write_objs, wcount, timeout); } if (self->mod != 0) @@ -2975,220 +3271,6 @@ server_reset(struct xrdp_mod *mod, int width, int height, int bpp) return 0; } -/* read the channel section of the ini file into lists - * return 1 on success 0 on failure */ -int read_allowed_channel_names(struct list *names, struct list *values) -{ - int fd; - int ret = 0; - char cfg_file[256]; - - g_snprintf(cfg_file, 255, "%s/xrdp.ini", XRDP_CFG_PATH); - fd = g_file_open(cfg_file); - - if (fd != -1) - { - names->auto_free = 1; - values->auto_free = 1; - - /* all values in this section can be valid channel names */ - if (file_read_section(fd, "channels", names, values) == 0) - { - ret = 1; - } - else - { - log_message(LOG_LEVEL_ERROR,"Failure reading channel section of configuration"); - } - - g_file_close(fd); - } - return ret; -} - -/* internal function return -1 if name is not in list - * otherwise return the index 0->count-1*/ -int -find_name_in_lists(char *inName, struct list *names) -{ - int reply = -1; /*means not in the list*/ - int index; - char *name; - - for (index = 0; index < names->count; index++) - { - name = (char *)list_get_item(names, index); - if ( (name != 0) && (g_strncasecmp(name, inName, MAX_CHANNEL_NAME) == 0) ) - { - reply = index; - break; /* stop loop - item found*/ - } - } - - return reply; -} - -#define CHANNEL_NAME_PREFIX "channel." -/* update the channel lists from connection specific overrides - * return 1 on success 0 on failure */ -int update_allowed_channel_names(struct xrdp_wm *wm, struct list *names, struct list *values) -{ - int ret = 1; - int index; - int oldindex; - char *val; - char *name; - //wm->mm->login_names,wm->mm->login_values - for (index = 0; index < wm->mm->login_names->count; index++) - { - name = (char *)list_get_item(wm->mm->login_names, index); - if ( (name != 0) && (g_strncmp( name, CHANNEL_NAME_PREFIX, g_strlen(CHANNEL_NAME_PREFIX)) == 0 ) ) - { - name += g_strlen(CHANNEL_NAME_PREFIX); - // locate and remove from list - oldindex = find_name_in_lists(name, names); - if (oldindex >= 0) - { - list_remove_item(names, oldindex); - list_remove_item(values, oldindex); - } - val = (char *)list_get_item(wm->mm->login_values, index); - // (re)add to lists - list_add_item(names, (tbus)g_strdup(name)); - list_add_item(values, (tbus)g_strdup(val)); - } - } - return ret; -} - -/* internal function return 1 if name is in list of channels - * and if the value is allowed */ -int -is_channel_enabled(char *inName, struct list *names, struct list *values) -{ - int reply = 0; /*means not in the list*/ - int index; - char *val; - - index = find_name_in_lists(inName, names); - if ( index >= 0 ) - { - val = (char *)list_get_item(values, index); - reply = g_text2bool(val); - if (reply == 0) - { - log_message(LOG_LEVEL_INFO,"This channel is disabled: %s", inName); - } - } - else - { - log_message(LOG_LEVEL_INFO,"This channel is disabled (not in List): %s", inName); - } - - return reply; -} -/* internal function only used once per session - * creates the list of allowed channels and store the information - * in wm struct */ -void init_channel_allowed(struct xrdp_wm *wm) -{ - int error; - int i; - char channelname[MAX_CHANNEL_NAME]; - int index = 0; - int allowindex = 0; - struct list *names; - struct list *values; - - /* first reset allowedchannels */ - for (i = 0; i < MAX_NR_CHANNELS; i++) - { - /* 0 is a valid channel so we use -1 to mark the index as unused */ - wm->allowedchannels[i] = -1; - } - - names = list_create(); - values = list_create(); - /* You can override the list of allowed channels individually for each - * session type. */ - if ( read_allowed_channel_names(names, values) - && update_allowed_channel_names(wm, names, values) ) - { - do - { - /* libxrdp_query_channel return 1 on error*/ - error = libxrdp_query_channel(wm->session, index, channelname, NULL); - - if (error == 0) - { - /* examples of channel names: rdpdr ; rdpsnd ; drdynvc ; cliprdr */ - if (is_channel_enabled(channelname, names, values)) - { - log_message(LOG_LEVEL_INFO,"The following channel is allowed: %s (%d)", channelname, index); - wm->allowedchannels[allowindex] = index; - allowindex++; - - if (allowindex >= MAX_NR_CHANNELS) - { - log_message(LOG_LEVEL_ALWAYS,"Programming error in is_channel_allowed"); - error = 1; /* end loop */ - } - } - else - { - log_message(LOG_LEVEL_INFO,"The following channel is not allowed: %s (%d)", channelname, index); - } - - index++; - } - } - while ((error == 0) && (index < MAX_NR_CHANNELS)); - } - else - { - log_message(LOG_LEVEL_ERROR,"Error reading channel section in inifile"); - } - - list_delete(names); - list_delete(values); -} - -/*****************************************************************************/ -/* This function returns 1 if the channelID is allowed by rule set - * returns 0 if not allowed */ -int is_channel_allowed(struct xrdp_wm *wm, int channel_id) -{ - int i; - int reply = 0; /* not allowed */ - - /* The first time each client is using this function we have to - * define the list of allowed channels */ - if (wm->allowedinitialized == 0) - { - init_channel_allowed(wm); - log_message(LOG_LEVEL_DEBUG,"The allow channel list now initialized for this session"); - wm->allowedinitialized = 1; - } - - for (i = 0; i < MAX_NR_CHANNELS; i++) - { - if (channel_id == wm->allowedchannels[i]) - { - /*g_writeln("Channel allowed: %d",channel_id);*/ - reply = 1; /*channel allowed*/ - break; - } - else if (wm->allowedchannels[i] == -1) - { - /* We are in the unused space of the allowedchannels list - * We can end the loop */ - break; - } - } - - return reply; -} - /*****************************************************************************/ /*return 0 if the index is not found*/ int @@ -3235,25 +3317,18 @@ server_send_to_channel(struct xrdp_mod *mod, int channel_id, wm = (struct xrdp_wm *)(mod->wm); - if (is_channel_allowed(wm, channel_id)) - { - if (wm->mm->usechansrv) - { - /* - * Xvnc backend reaches here - * should not return 1 as this case is not an error - */ - return 0; - } - - /* vnc proxy mode reaches here */ - return libxrdp_send_to_channel(wm->session, channel_id, data, data_len, - total_data_len, flags); - } - else + if (wm->mm->usechansrv) { - return 1; + /* + * Xvnc backend reaches here + * should not return 1 as this case is not an error + */ + return 0; } + + /* vnc proxy mode reaches here */ + return libxrdp_send_to_channel(wm->session, channel_id, data, data_len, + total_data_len, flags); } /*****************************************************************************/ diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index e317f584..7e416125 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -295,6 +295,8 @@ struct xrdp_mm int delete_chan_trans; /* boolean set when done with channel connection */ int usechansrv; /* true if chansrvport is set in xrdp.ini or using sesman */ struct xrdp_encoder *encoder; + int cs2xr_cid_map[256]; + int xr2cr_cid_map[256]; }; struct xrdp_key_info @@ -376,8 +378,6 @@ struct xrdp_wm struct xrdp_bitmap* target_surface; /* either screen or os surface */ int current_surface_index; int hints; - int allowedchannels[MAX_NR_CHANNELS]; - int allowedinitialized ; char pamerrortxt[256]; /* configuration derived from xrdp.ini */ diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c index 8b26718d..fd7b13b8 100644 --- a/xrdp/xrdp_wm.c +++ b/xrdp/xrdp_wm.c @@ -27,6 +27,18 @@ #include "xrdp.h" #include "log.h" +#define LLOG_LEVEL 1 +#define LLOGLN(_level, _args) \ + do \ + { \ + if (_level < LLOG_LEVEL) \ + { \ + g_write("xrdp:xrdp_wm [%10.10u]: ", g_time3()); \ + g_writeln _args ; \ + } \ + } \ + while (0) + /*****************************************************************************/ struct xrdp_wm * xrdp_wm_create(struct xrdp_process *owner, @@ -563,6 +575,69 @@ xrdp_wm_init(struct xrdp_wm *self) load_xrdp_config(self->xrdp_config, self->screen->bpp); + /* global channels allow */ + names = list_create(); + names->auto_free = 1; + values = list_create(); + values->auto_free = 1; + g_snprintf(cfg_file, 255, "%s/xrdp.ini", XRDP_CFG_PATH); + if (file_by_name_read_section(cfg_file, "Channels", names, values) == 0) + { + int error; + int ii; + int chan_id; + int chan_flags; + int disabled; + char chan_name[16]; + + ii = 0; + error = libxrdp_query_channel(self->session, ii, chan_name, + &chan_flags); + while (error == 0) + { + r = NULL; + for (index = 0; index < names->count; index++) + { + q = (char *) list_get_item(names, index); + if (g_strcasecmp(q, chan_name) == 0) + { + r = (char *) list_get_item(values, index); + break; + } + } + if (r == NULL) + { + /* not found, disable the channel */ + chan_id = libxrdp_get_channel_id(self->session, chan_name); + libxrdp_disable_channel(self->session, chan_id, 1); + g_writeln("xrdp_wm_init: channel %s channel id %d is " + "disabled", chan_name, chan_id); + } + else + { + /* found */ + chan_id = libxrdp_get_channel_id(self->session, q); + disabled = !g_text2bool(r); + libxrdp_disable_channel(self->session, chan_id, disabled); + if (disabled) + { + g_writeln("xrdp_wm_init: channel %s channel id %d is " + "disabled", chan_name, chan_id); + } + else + { + g_writeln("xrdp_wm_init: channel %s channel id %d is " + "allowed", chan_name, chan_id); + } + } + ii++; + error = libxrdp_query_channel(self->session, ii, chan_name, + &chan_flags); + } + } + list_delete(names); + list_delete(values); + xrdp_wm_load_static_colors_plus(self, autorun_name); xrdp_wm_load_static_pointers(self); self->screen->bg_color = self->xrdp_config->cfg_globals.ls_top_window_bg_color; @@ -1758,27 +1833,21 @@ xrdp_wm_process_channel_data(struct xrdp_wm *self, tbus param3, tbus param4) { int rv; - int chanid ; rv = 1; if (self->mm->mod != 0) { - chanid = LOWORD(param1); - - if (is_channel_allowed(self, chanid)) + if (self->mm->usechansrv) { - if (self->mm->usechansrv) - { - rv = xrdp_mm_process_channel_data(self->mm, param1, param2, - param3, param4); - } - else + rv = xrdp_mm_process_channel_data(self->mm, param1, param2, + param3, param4); + } + else + { + if (self->mm->mod->mod_event != 0) { - if (self->mm->mod->mod_event != 0) - { - rv = self->mm->mod->mod_event(self->mm->mod, 0x5555, param1, param2, - param3, param4); - } + rv = self->mm->mod->mod_event(self->mm->mod, 0x5555, param1, param2, + param3, param4); } } } @@ -1844,6 +1913,9 @@ callback(intptr_t id, int msg, intptr_t param1, intptr_t param2, //g_writeln("callback: frame ack %d", param1); xrdp_mm_frame_ack(wm->mm, param1); break; + case 0x5558: + xrdp_mm_drdynvc_up(wm->mm); + break; } return rv; } diff --git a/xrdpapi/xrdpapi.c b/xrdpapi/xrdpapi.c index 320052ab..a9017aff 100644 --- a/xrdpapi/xrdpapi.c +++ b/xrdpapi/xrdpapi.c @@ -44,24 +44,21 @@ struct wts_obj { - int fd; - int status; - char name[9]; - char dname[128]; - int display_num; - uint32_t flags; + int fd; + int display_num; }; /* helper functions used by WTSxxx API - do not invoke directly */ -static int get_display_num_from_display(char *display_text); -static int send_init(struct wts_obj *wts); -static int can_send(int sck, int millis); -static int can_recv(int sck, int millis); - -static const unsigned char g_xrdpapi_magic[12] = -{ - 0x78, 0x32, 0x10, 0x67, 0x00, 0x92, 0x30, 0x56, 0xff, 0xd8, 0xa9, 0x1f -}; +static int +get_display_num_from_display(char *display_text); +static int +can_send(int sck, int millis); +static int +can_recv(int sck, int millis); +static int +mysend(int sck, const void* adata, int bytes); +static int +myrecv(int sck, void* adata, int bytes); /* * Opens a handle to the server end of a specified virtual channel - this @@ -100,22 +97,27 @@ void * WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, unsigned int flags) { - struct wts_obj *wts; - char *display_text; - int bytes; - unsigned long llong; - struct sockaddr_un s; + struct wts_obj *wts; + char *display_text; + int bytes; + unsigned long long1; + struct sockaddr_un s; + char *connect_data; + int chan_name_bytes; + int lerrno; if (SessionId != WTS_CURRENT_SESSION) { LLOGLN(0, ("WTSVirtualChannelOpenEx: bad SessionId")); return 0; } - wts = (struct wts_obj *) calloc(1, sizeof(struct wts_obj)); - + if (wts == NULL) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: calloc failed")); + return 0; + } wts->fd = -1; - wts->flags = flags; display_text = getenv("DISPLAY"); if (display_text != 0) @@ -133,16 +135,17 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, /* we use unix domain socket to communicate with chansrv */ if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + LLOGLN(0, ("WTSVirtualChannelOpenEx: socket failed")); free(wts); return NULL; } /* set non blocking */ - llong = fcntl(wts->fd, F_GETFL); - llong = llong | O_NONBLOCK; - if (fcntl(wts->fd, F_SETFL, llong) < 0) + long1 = fcntl(wts->fd, F_GETFL); + long1 = long1 | O_NONBLOCK; + if (fcntl(wts->fd, F_SETFL, long1) < 0) { - LLOGLN(10, ("WTSVirtualChannelOpenEx: set non-block mode failed")); + LLOGLN(0, ("WTSVirtualChannelOpenEx: set non-block mode failed")); } /* connect to chansrv session */ @@ -153,17 +156,93 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, s.sun_path[bytes - 1] = 0; bytes = sizeof(struct sockaddr_un); - if (connect(wts->fd, (struct sockaddr *) &s, bytes) == 0) + if (connect(wts->fd, (struct sockaddr *) &s, bytes) < 0) { - LLOGLN(10, ("WTSVirtualChannelOpenEx: connected ok, name %s", pVirtualName)); - strncpy(wts->name, pVirtualName, 8); - - /* wait for connection to complete and send init */ - if (send_init(wts) == 0) + lerrno = errno; + if ((lerrno == EWOULDBLOCK) || (lerrno == EAGAIN) || + (lerrno == EINPROGRESS)) { - /* all ok */ - wts->status = 1; + /* ok */ } + else + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: connect failed")); + free(wts); + return NULL; + } + } + + /* wait for connection to complete */ + if (!can_send(wts->fd, 500)) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: can_send failed")); + free(wts); + return NULL; + } + + chan_name_bytes = strlen(pVirtualName); + bytes = 4 + 4 + 4 + chan_name_bytes + 4; + + LLOGLN(10, ("WTSVirtualChannelOpenEx: chan_name_bytes %d bytes %d pVirtualName %s", chan_name_bytes, bytes, pVirtualName)); + + connect_data = (char *) calloc(bytes, 1); + if (connect_data == NULL) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: calloc failed")); + free(wts); + return NULL; + } + + connect_data[0] = (bytes >> 0) & 0xFF; + connect_data[1] = (bytes >> 8) & 0xFF; + connect_data[2] = (bytes >> 16) & 0xFF; + connect_data[3] = (bytes >> 24) & 0xFF; + + /* version here(4-7), just leave 0 */ + + connect_data[8] = (chan_name_bytes >> 0) & 0xFF; + connect_data[9] = (chan_name_bytes >> 8) & 0xFF; + connect_data[10] = (chan_name_bytes >> 16) & 0xFF; + connect_data[11] = (chan_name_bytes >> 24) & 0xFF; + + memcpy(connect_data + 12, pVirtualName, chan_name_bytes); + + connect_data[4 + 4 + 4 + chan_name_bytes + 0] = (flags >> 0) & 0xFF; + connect_data[4 + 4 + 4 + chan_name_bytes + 1] = (flags >> 8) & 0xFF; + connect_data[4 + 4 + 4 + chan_name_bytes + 2] = (flags >> 16) & 0xFF; + connect_data[4 + 4 + 4 + chan_name_bytes + 3] = (flags >> 24) & 0xFF; + + LLOGLN(10, ("WTSVirtualChannelOpenEx: calling mysend with %d bytes", bytes)); + + if (mysend(wts->fd, connect_data, bytes) != bytes) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: mysend failed")); + free(wts); + return NULL; + } + LLOGLN(10, ("WTSVirtualChannelOpenEx: sent ok")); + + if (!can_recv(wts->fd, 500)) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: can_recv failed")); + free(wts); + return NULL; + } + + /* get response */ + if (myrecv(wts->fd, connect_data, 4) != 4) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: myrecv failed")); + free(wts); + return NULL; + } + + if ((connect_data[0] != 0) || (connect_data[1] != 0) || + (connect_data[2] != 0) || (connect_data[3] != 0)) + { + LLOGLN(0, ("WTSVirtualChannelOpenEx: connect_data not ok")); + free(wts); + return NULL; } return wts; @@ -179,18 +258,18 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, /*****************************************************************************/ static int -mysend(int sck, const void* adata, int bytes) +mysend(int sck, const void *adata, int bytes) { int sent; int error; - const char* data; + const char *data; #if defined(SO_NOSIGPIPE) const int on = 1; setsockopt(sck, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); #endif - data = (const char*)adata; + data = (const char *) adata; sent = 0; while (sent < bytes) { @@ -207,6 +286,36 @@ mysend(int sck, const void* adata, int bytes) return sent; } +/*****************************************************************************/ +static int +myrecv(int sck, void *adata, int bytes) +{ + int recd; + int error; + char *data; + +#if defined(SO_NOSIGPIPE) + const int on = 1; + setsockopt(sck, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); +#endif + + data = (char *) adata; + recd = 0; + while (recd < bytes) + { + if (can_recv(sck, 100)) + { + error = recv(sck, data + recd, bytes - recd, MSG_NOSIGNAL); + if (error < 1) + { + return -1; + } + recd += error; + } + } + return recd; +} + /* * write data to client connection * @@ -217,8 +326,7 @@ WTSVirtualChannelWrite(void *hChannelHandle, const char *Buffer, unsigned int Length, unsigned int *pBytesWritten) { struct wts_obj *wts; - int rv; - int header[4]; + int rv; wts = (struct wts_obj *) hChannelHandle; @@ -230,29 +338,12 @@ WTSVirtualChannelWrite(void *hChannelHandle, const char *Buffer, return 0; } - if (wts->status != 1) - { - LLOGLN(10, ("WTSVirtualChannelWrite: wts->status != 1")); - return 0; - } - if (!can_send(wts->fd, 0)) { return 1; /* can't write now, ok to try again */ } - rv = 0; - memcpy(header, g_xrdpapi_magic, 12); - header[3] = Length; - if (mysend(wts->fd, header, 16) == 16) - { - rv = mysend(wts->fd, Buffer, Length); - } - else - { - LLOGLN(0, ("WTSVirtualChannelWrite: header write failed")); - return 0; - } + rv = mysend(wts->fd, Buffer, Length); LLOGLN(10, ("WTSVirtualChannelWrite: mysend() returned %d", rv)); @@ -263,14 +354,6 @@ WTSVirtualChannelWrite(void *hChannelHandle, const char *Buffer, return 1; } -#if 0 /* coverity: this is dead code */ - /* error, but is it ok to try again? */ - if ((rv == EWOULDBLOCK) || (rv == EAGAIN) || (rv == EINPROGRESS)) - { - return 0; /* failed to send, but should try again */ - } -#endif - /* fatal error */ return 0; } @@ -296,11 +379,6 @@ WTSVirtualChannelRead(void *hChannelHandle, unsigned int TimeOut, return 0; } - if (wts->status != 1) - { - return 0; - } - if (can_recv(wts->fd, TimeOut)) { rv = recv(wts->fd, Buffer, BufferSize, 0); @@ -341,7 +419,7 @@ WTSVirtualChannelClose(void *hChannelHandle) wts = (struct wts_obj *)hChannelHandle; - if (wts == 0) + if (wts == NULL) { return 0; } @@ -364,12 +442,7 @@ WTSVirtualChannelQuery(void *hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, wts = (struct wts_obj *)hChannelHandle; - if (wts == 0) - { - return 0; - } - - if (wts->status != 1) + if (wts == NULL) { return 0; } @@ -378,6 +451,10 @@ WTSVirtualChannelQuery(void *hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, { *pBytesReturned = 4; *ppBuffer = malloc(4); + if (*ppBuffer == NULL) + { + return 0; + } memcpy(*ppBuffer, &(wts->fd), 4); } @@ -388,7 +465,7 @@ WTSVirtualChannelQuery(void *hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, void WTSFreeMemory(void *pMemory) { - if (pMemory != 0) + if (pMemory != NULL) { free(pMemory); } @@ -452,39 +529,6 @@ can_recv(int sck, int millis) return 0; } -/*****************************************************************************/ -static int -send_init(struct wts_obj *wts) -{ - char initmsg[64]; - - memset(initmsg, 0, 64); - - /* insert channel name */ - strncpy(initmsg, wts->name, 8); - - /* insert open mode flags */ - initmsg[16] = (wts->flags >> 0) & 0xff; - initmsg[17] = (wts->flags >> 8) & 0xff; - initmsg[18] = (wts->flags >> 16) & 0xff; - initmsg[19] = (wts->flags >> 24) & 0xff; - - if (!can_send(wts->fd, 500)) - { - LLOGLN(10, ("send_init: send() will block!")); - return 1; - } - - if (send(wts->fd, initmsg, 64, 0) != 64) - { - LLOGLN(10, ("send_init: send() failed!")); - return 1; - } - - LLOGLN(10, ("send_init: sent ok!")); - return 0; -} - /*****************************************************************************/ static int get_display_num_from_display(char *display_text) @@ -513,7 +557,6 @@ get_display_num_from_display(char *display_text) disp[disp_index] = display_text[index]; disp_index++; } - index++; }