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:
matt335672
2025-10-13 15:12:45 +01:00
parent d90b9d9316
commit 6a5d858dce
6 changed files with 739 additions and 165 deletions
+182 -94
View File
@@ -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();