xrdpapi: Add a way to get client connect status
Functions are added to xrdpapi to allows the connection status to be determimed. These functions are modelled on the Windows API functions, but are not compatible with them. In particular, the error handling is different. A way for an application to receive events is also provided. At present, only connect/disconnected events are implemented.
This commit is contained in:
+182
-94
@@ -44,14 +44,13 @@
|
||||
#include "chansrv_config.h"
|
||||
#include "xrdp_sockets.h"
|
||||
#include "audin.h"
|
||||
#include "channel_defs.h"
|
||||
|
||||
#include "scp.h"
|
||||
#include "scp_sync.h"
|
||||
|
||||
#include "ms-rdpbcgr.h"
|
||||
|
||||
#define MAX_PATH 260
|
||||
|
||||
static struct trans *g_lis_trans = 0;
|
||||
static struct trans *g_con_trans = 0;
|
||||
static struct trans *g_api_lis_trans = 0;
|
||||
@@ -113,6 +112,12 @@ struct xrdp_api_data
|
||||
int chan_id;
|
||||
};
|
||||
|
||||
// Session state passed to xrdpapi
|
||||
// Whenever a single member of this struct changes, the whole
|
||||
// block is sent to xrdpapi. This simplifies the interface, but
|
||||
// complicates xrdpapi somewhat
|
||||
static struct xrdp_chan_session_state g_session_state;
|
||||
|
||||
struct timeout_obj
|
||||
{
|
||||
tui32 mstime;
|
||||
@@ -343,6 +348,51 @@ send_rail_drawing_orders(char *data, int size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Sends a session API state event to one listener
|
||||
*/
|
||||
static int
|
||||
xrdpapi_send_session_state_event_single(struct trans *t)
|
||||
{
|
||||
struct stream *s = t->out_s;
|
||||
init_stream(s, (int)sizeof(g_session_state));
|
||||
out_uint8a(s, &g_session_state, sizeof(g_session_state));
|
||||
s_mark_end(s);
|
||||
return trans_write_copy(t);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Sends a session API state event to all listeners (if any)
|
||||
*
|
||||
* This should be called after updating g_session_state. The xrdpapi
|
||||
* module will generate any appropriate 'windows' events from the message
|
||||
*/
|
||||
static void
|
||||
xrdpapi_send_session_state_event_all(void)
|
||||
{
|
||||
int index;
|
||||
struct trans *ltran;
|
||||
struct xrdp_api_data *api_data;
|
||||
|
||||
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 != NULL)
|
||||
{
|
||||
api_data = (struct xrdp_api_data *) (ltran->callback_data);
|
||||
if (api_data != NULL)
|
||||
{
|
||||
if (api_data->chan_id == CHAN_ID_XRDP_SESSION_INFO)
|
||||
{
|
||||
(void)xrdpapi_send_session_state_event_single(ltran);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* returns error */
|
||||
static int
|
||||
@@ -1065,7 +1115,7 @@ my_api_open_response(int chan_id, int creation_status)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
out_uint32_le(s, creation_status);
|
||||
out_uint32_pe(s, creation_status);
|
||||
s_mark_end(s);
|
||||
if (trans_write_copy(trans) != 0)
|
||||
{
|
||||
@@ -1137,26 +1187,112 @@ my_api_data(int chan_id, char *data, int bytes)
|
||||
}
|
||||
|
||||
/*
|
||||
* called when WTSVirtualChannelWrite() is invoked in xrdpapi.c
|
||||
* Handles an incoming channel connect request on an xrdpapi channel
|
||||
*
|
||||
******************************************************************************/
|
||||
static int
|
||||
handle_xrdpapi_connection_request(struct trans *trans)
|
||||
{
|
||||
struct xrdp_api_data *ad = (struct xrdp_api_data *)(trans->callback_data);
|
||||
struct stream *s = trans_get_in_s(trans);
|
||||
int rv = 1; // Assume failure
|
||||
struct xrdp_chan_connect connect_data;
|
||||
|
||||
in_uint8a(s, &connect_data, sizeof(connect_data));
|
||||
connect_data.name[MAX_DVC_NAME_LEN] = '\0';
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: chan_name %s chan_flags 0x%8.8x", connect_data.name, connect_data.flags);
|
||||
if (connect_data.version != XRDPAPI_CONNECT_PDU_VERSION)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
ad->chan_flags = connect_data.flags;
|
||||
|
||||
if (connect_data.flags == 0 || connect_data.private_chan != 0)
|
||||
{
|
||||
/* SVC (or private) */
|
||||
if (connect_data.private_chan >= CHAN_ID_XRDP_BASE &&
|
||||
connect_data.private_chan < CHAN_ID_XRDP_MAX)
|
||||
{
|
||||
/* Valid private channel */
|
||||
ad->chan_id = connect_data.private_chan;
|
||||
ad->chan_flags = 0;
|
||||
rv = 0;
|
||||
}
|
||||
else if (connect_data.private_chan == 0)
|
||||
{
|
||||
/* Check SVC names */
|
||||
int index;
|
||||
for (index = 0; index < g_num_chan_items; index++)
|
||||
{
|
||||
if (g_strcasecmp(g_chan_items[index].name,
|
||||
connect_data.name) == 0)
|
||||
{
|
||||
ad->chan_id = g_chan_items[index].id;
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the status back to the caller */
|
||||
struct stream *out_s = trans_get_out_s(trans, 8192);
|
||||
if (out_s == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
out_uint32_pe(out_s, rv);
|
||||
s_mark_end(out_s);
|
||||
if (trans_write_copy(trans) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Channel-specific processing */
|
||||
if (rv == 0 && connect_data.private_chan != 0)
|
||||
{
|
||||
switch (connect_data.private_chan)
|
||||
{
|
||||
case CHAN_ID_XRDP_SESSION_INFO:
|
||||
{
|
||||
rv = xrdpapi_send_session_state_event_single(trans);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* DVS */
|
||||
struct chansrv_drdynvc_procs procs;
|
||||
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(connect_data.name, ad->chan_flags,
|
||||
&procs, &(ad->chan_id));
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "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;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* called when VirtualChannelOpen() is invoked in xrdpapi.c, and later,
|
||||
* when the channel is written to
|
||||
*
|
||||
******************************************************************************/
|
||||
static int
|
||||
my_api_trans_data_in(struct trans *trans)
|
||||
{
|
||||
struct stream *s;
|
||||
struct stream *out_s;
|
||||
struct xrdp_api_data *ad;
|
||||
int index;
|
||||
int rv;
|
||||
int bytes;
|
||||
int ver;
|
||||
struct chansrv_drdynvc_procs procs;
|
||||
/*
|
||||
* Name is limited to CHANNEL_NAME_LEN for an SVC, or MAX_PATH
|
||||
* bytes for a DVC
|
||||
*/
|
||||
char chan_name[MAX(CHANNEL_NAME_LEN, MAX_PATH) + 1];
|
||||
unsigned int channel_name_bytes;
|
||||
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: extra_flags %d", trans->extra_flags);
|
||||
rv = 0;
|
||||
@@ -1164,89 +1300,15 @@ my_api_trans_data_in(struct trans *trans)
|
||||
s = trans_get_in_s(trans);
|
||||
if (trans->extra_flags == 0)
|
||||
{
|
||||
in_uint32_le(s, bytes);
|
||||
in_uint32_le(s, ver);
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: bytes %d ver %d", bytes, ver);
|
||||
if (ver != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
trans->header_size = bytes;
|
||||
trans->header_size = XRDPAPI_CONNECT_PDU_LEN; // Need more data
|
||||
trans->extra_flags = 1;
|
||||
}
|
||||
else if (trans->extra_flags == 1)
|
||||
{
|
||||
rv = 1;
|
||||
in_uint32_le(s, channel_name_bytes);
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: channel_name_bytes %d", channel_name_bytes);
|
||||
if (channel_name_bytes > (sizeof(chan_name) - 1))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
in_uint8a(s, chan_name, channel_name_bytes);
|
||||
chan_name[channel_name_bytes] = '\0';
|
||||
|
||||
in_uint32_le(s, ad->chan_flags);
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: chan_name %s chan_flags 0x%8.8x", chan_name, ad->chan_flags);
|
||||
if (ad->chan_flags == 0)
|
||||
{
|
||||
/* SVC */
|
||||
for (index = 0; index < g_num_chan_items; index++)
|
||||
{
|
||||
if (g_strcasecmp(g_chan_items[index].name, chan_name) == 0)
|
||||
{
|
||||
ad->chan_id = g_chan_items[index].id;
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
/* 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
|
||||
{
|
||||
/* 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));
|
||||
//LOG_DEVEL(LOG_LEVEL_DEBUG, "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;
|
||||
}
|
||||
// We've got a complete connection request on the channel */
|
||||
handle_xrdpapi_connection_request(trans);
|
||||
init_stream(s, 0);
|
||||
trans->extra_flags = 2;
|
||||
trans->extra_flags = 2; // Mark the connection phase as complete
|
||||
trans->header_size = 0;
|
||||
}
|
||||
else
|
||||
@@ -1259,8 +1321,16 @@ my_api_trans_data_in(struct trans *trans)
|
||||
}
|
||||
if (ad->chan_flags == 0)
|
||||
{
|
||||
/* SVC */
|
||||
rv = send_channel_data(ad->chan_id, s->data, bytes);
|
||||
if (ad->chan_id == CHAN_ID_XRDP_SESSION_INFO)
|
||||
{
|
||||
// Ignore data sent to this channel
|
||||
rv = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SVC */
|
||||
rv = send_channel_data(ad->chan_id, s->data, bytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1301,6 +1371,12 @@ my_trans_conn_in(struct trans *trans, struct trans *new_trans)
|
||||
g_con_trans = new_trans;
|
||||
g_con_trans->trans_data_in = my_trans_data_in;
|
||||
g_con_trans->header_size = 8;
|
||||
|
||||
/* Tell any xrdpapi listeners the session is connected. The
|
||||
* previous state is guaranteed to be 'disconnected' at this point */
|
||||
g_session_state.is_connected = 1;
|
||||
xrdpapi_send_session_state_event_all();
|
||||
|
||||
/* stop listening */
|
||||
trans_delete(g_lis_trans);
|
||||
g_lis_trans = 0;
|
||||
@@ -1535,6 +1611,7 @@ channel_thread_loop(void *in_val)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: "
|
||||
"trans_check_wait_objs error resetting");
|
||||
|
||||
clipboard_deinit();
|
||||
sound_deinit();
|
||||
devredir_deinit();
|
||||
@@ -1542,6 +1619,11 @@ channel_thread_loop(void *in_val)
|
||||
/* delete g_con_trans */
|
||||
trans_delete(g_con_trans);
|
||||
g_con_trans = 0;
|
||||
/* Tell any xrdpapi listeners the session is
|
||||
* disconnected. The previous state is guaranteed
|
||||
* to be 'connected' at this point */
|
||||
g_session_state.is_connected = 0;
|
||||
(void)xrdpapi_send_session_state_event_all();
|
||||
/* create new listener */
|
||||
error = setup_listen();
|
||||
|
||||
@@ -1592,6 +1674,12 @@ channel_thread_loop(void *in_val)
|
||||
g_lis_trans = 0;
|
||||
trans_delete(g_con_trans);
|
||||
g_con_trans = 0;
|
||||
/* Tell any xrdpapi listeners the session is disconnected */
|
||||
if (g_session_state.is_connected)
|
||||
{
|
||||
g_session_state.is_connected = 0;
|
||||
(void)xrdpapi_send_session_state_event_all();
|
||||
}
|
||||
trans_delete(g_api_lis_trans);
|
||||
g_api_lis_trans = 0;
|
||||
api_con_trans_list_remove_all();
|
||||
|
||||
Reference in New Issue
Block a user