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:
@@ -47,6 +47,7 @@ libcommon_la_SOURCES = \
|
|||||||
arch.h \
|
arch.h \
|
||||||
base64.h \
|
base64.h \
|
||||||
base64.c \
|
base64.c \
|
||||||
|
channel_defs.h \
|
||||||
defines.h \
|
defines.h \
|
||||||
fifo.c \
|
fifo.c \
|
||||||
fifo.h \
|
fifo.h \
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2024 Matt Burt, all xrdp contributors
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file common/channel_defs.h
|
||||||
|
* @brief Private xrdp channel definitions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(CHANNEL_DEFS_H)
|
||||||
|
#define CHANNEL_DEFS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel IDs assigned to xrdp private channels. These are chosen to avoid conflicts with
|
||||||
|
* channel IDs returned by other servers.
|
||||||
|
*/
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CHAN_ID_XRDP_BASE = (('x' << 24) | ('r' << 16) | ('d' << 8) | 'p'),
|
||||||
|
CHAN_ID_XRDP_SESSION_INFO = CHAN_ID_XRDP_BASE,
|
||||||
|
// Add further IDs here
|
||||||
|
CHAN_ID_XRDP_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
// Max length of a DVC name. This is taken from the specification of
|
||||||
|
// WTSVirtualChannelOpenEx() which limits the length of a virtual channel
|
||||||
|
// to Windows MAX_PATH
|
||||||
|
#define MAX_DVC_NAME_LEN 260
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information to connect to a channel using xrdpapi
|
||||||
|
*/
|
||||||
|
#define XRDPAPI_CONNECT_PDU_LEN 384 // Connect PDU is always this length
|
||||||
|
#define XRDPAPI_CONNECT_PDU_VERSION 1 // Current connnect PDU version
|
||||||
|
|
||||||
|
struct xrdp_chan_connect
|
||||||
|
{
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t private_chan; // 0 for a standard channel
|
||||||
|
uint32_t flags;
|
||||||
|
char name[MAX_DVC_NAME_LEN + 1]; // null-terminated
|
||||||
|
};
|
||||||
|
|
||||||
|
// Events received on the CHAN_ID_XRDP_SESSION_INFO channel
|
||||||
|
|
||||||
|
// Update on all the session state data held by chansrv
|
||||||
|
//
|
||||||
|
// If more information is needed to be exported by xrdpapi, it can be
|
||||||
|
// added to this event
|
||||||
|
struct xrdp_chan_session_state
|
||||||
|
{
|
||||||
|
uint32_t is_connected; // Is the session connected?
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* CHANNEL_DEFS_H */
|
||||||
@@ -528,6 +528,14 @@ in_utf16_le_terminated_as_utf8_length(struct stream *s);
|
|||||||
(s)->p++; \
|
(s)->p++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
// Platform-endian out_uint32
|
||||||
|
#if defined(B_ENDIAN)
|
||||||
|
#define out_uint32_pe(s, v) out_uint32_be(s, v)
|
||||||
|
#else
|
||||||
|
#define out_uint32_pe(s, v) out_uint32_le(s, v)
|
||||||
|
#endif
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
#define in_uint8p(s, v, n) do \
|
#define in_uint8p(s, v, n) do \
|
||||||
{ \
|
{ \
|
||||||
|
|||||||
+182
-94
@@ -44,14 +44,13 @@
|
|||||||
#include "chansrv_config.h"
|
#include "chansrv_config.h"
|
||||||
#include "xrdp_sockets.h"
|
#include "xrdp_sockets.h"
|
||||||
#include "audin.h"
|
#include "audin.h"
|
||||||
|
#include "channel_defs.h"
|
||||||
|
|
||||||
#include "scp.h"
|
#include "scp.h"
|
||||||
#include "scp_sync.h"
|
#include "scp_sync.h"
|
||||||
|
|
||||||
#include "ms-rdpbcgr.h"
|
#include "ms-rdpbcgr.h"
|
||||||
|
|
||||||
#define MAX_PATH 260
|
|
||||||
|
|
||||||
static struct trans *g_lis_trans = 0;
|
static struct trans *g_lis_trans = 0;
|
||||||
static struct trans *g_con_trans = 0;
|
static struct trans *g_con_trans = 0;
|
||||||
static struct trans *g_api_lis_trans = 0;
|
static struct trans *g_api_lis_trans = 0;
|
||||||
@@ -113,6 +112,12 @@ struct xrdp_api_data
|
|||||||
int chan_id;
|
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
|
struct timeout_obj
|
||||||
{
|
{
|
||||||
tui32 mstime;
|
tui32 mstime;
|
||||||
@@ -343,6 +348,51 @@ send_rail_drawing_orders(char *data, int size)
|
|||||||
return 0;
|
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 */
|
/* returns error */
|
||||||
static int
|
static int
|
||||||
@@ -1065,7 +1115,7 @@ my_api_open_response(int chan_id, int creation_status)
|
|||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
out_uint32_le(s, creation_status);
|
out_uint32_pe(s, creation_status);
|
||||||
s_mark_end(s);
|
s_mark_end(s);
|
||||||
if (trans_write_copy(trans) != 0)
|
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
|
static int
|
||||||
my_api_trans_data_in(struct trans *trans)
|
my_api_trans_data_in(struct trans *trans)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
struct stream *out_s;
|
|
||||||
struct xrdp_api_data *ad;
|
struct xrdp_api_data *ad;
|
||||||
int index;
|
|
||||||
int rv;
|
int rv;
|
||||||
int bytes;
|
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);
|
//LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: extra_flags %d", trans->extra_flags);
|
||||||
rv = 0;
|
rv = 0;
|
||||||
@@ -1164,89 +1300,15 @@ my_api_trans_data_in(struct trans *trans)
|
|||||||
s = trans_get_in_s(trans);
|
s = trans_get_in_s(trans);
|
||||||
if (trans->extra_flags == 0)
|
if (trans->extra_flags == 0)
|
||||||
{
|
{
|
||||||
in_uint32_le(s, bytes);
|
trans->header_size = XRDPAPI_CONNECT_PDU_LEN; // Need more data
|
||||||
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->extra_flags = 1;
|
trans->extra_flags = 1;
|
||||||
}
|
}
|
||||||
else if (trans->extra_flags == 1)
|
else if (trans->extra_flags == 1)
|
||||||
{
|
{
|
||||||
rv = 1;
|
// We've got a complete connection request on the channel */
|
||||||
in_uint32_le(s, channel_name_bytes);
|
handle_xrdpapi_connection_request(trans);
|
||||||
//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;
|
|
||||||
}
|
|
||||||
init_stream(s, 0);
|
init_stream(s, 0);
|
||||||
trans->extra_flags = 2;
|
trans->extra_flags = 2; // Mark the connection phase as complete
|
||||||
trans->header_size = 0;
|
trans->header_size = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1259,8 +1321,16 @@ my_api_trans_data_in(struct trans *trans)
|
|||||||
}
|
}
|
||||||
if (ad->chan_flags == 0)
|
if (ad->chan_flags == 0)
|
||||||
{
|
{
|
||||||
/* SVC */
|
if (ad->chan_id == CHAN_ID_XRDP_SESSION_INFO)
|
||||||
rv = send_channel_data(ad->chan_id, s->data, bytes);
|
{
|
||||||
|
// Ignore data sent to this channel
|
||||||
|
rv = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* SVC */
|
||||||
|
rv = send_channel_data(ad->chan_id, s->data, bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1301,6 +1371,12 @@ my_trans_conn_in(struct trans *trans, struct trans *new_trans)
|
|||||||
g_con_trans = new_trans;
|
g_con_trans = new_trans;
|
||||||
g_con_trans->trans_data_in = my_trans_data_in;
|
g_con_trans->trans_data_in = my_trans_data_in;
|
||||||
g_con_trans->header_size = 8;
|
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 */
|
/* stop listening */
|
||||||
trans_delete(g_lis_trans);
|
trans_delete(g_lis_trans);
|
||||||
g_lis_trans = 0;
|
g_lis_trans = 0;
|
||||||
@@ -1535,6 +1611,7 @@ channel_thread_loop(void *in_val)
|
|||||||
{
|
{
|
||||||
LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: "
|
LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: "
|
||||||
"trans_check_wait_objs error resetting");
|
"trans_check_wait_objs error resetting");
|
||||||
|
|
||||||
clipboard_deinit();
|
clipboard_deinit();
|
||||||
sound_deinit();
|
sound_deinit();
|
||||||
devredir_deinit();
|
devredir_deinit();
|
||||||
@@ -1542,6 +1619,11 @@ channel_thread_loop(void *in_val)
|
|||||||
/* delete g_con_trans */
|
/* delete g_con_trans */
|
||||||
trans_delete(g_con_trans);
|
trans_delete(g_con_trans);
|
||||||
g_con_trans = 0;
|
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 */
|
/* create new listener */
|
||||||
error = setup_listen();
|
error = setup_listen();
|
||||||
|
|
||||||
@@ -1592,6 +1674,12 @@ channel_thread_loop(void *in_val)
|
|||||||
g_lis_trans = 0;
|
g_lis_trans = 0;
|
||||||
trans_delete(g_con_trans);
|
trans_delete(g_con_trans);
|
||||||
g_con_trans = 0;
|
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);
|
trans_delete(g_api_lis_trans);
|
||||||
g_api_lis_trans = 0;
|
g_api_lis_trans = 0;
|
||||||
api_con_trans_list_remove_all();
|
api_con_trans_list_remove_all();
|
||||||
|
|||||||
+336
-71
@@ -37,6 +37,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "xrdp_sockets.h"
|
#include "xrdp_sockets.h"
|
||||||
#include "string_calls.h"
|
#include "string_calls.h"
|
||||||
|
#include "channel_defs.h"
|
||||||
#include "xrdpapi.h"
|
#include "xrdpapi.h"
|
||||||
|
|
||||||
struct wts_obj
|
struct wts_obj
|
||||||
@@ -45,6 +46,17 @@ struct wts_obj
|
|||||||
int display_num;
|
int display_num;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data we store for each server
|
||||||
|
*/
|
||||||
|
struct wts_server
|
||||||
|
{
|
||||||
|
struct wts_obj *info_obj; // Object to get session notifications
|
||||||
|
struct xrdp_chan_session_state session_state; // session state
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wts_server wts_current_server;
|
||||||
|
|
||||||
/* helper functions used by WTSxxx API - do not invoke directly */
|
/* helper functions used by WTSxxx API - do not invoke directly */
|
||||||
static int
|
static int
|
||||||
can_send(int sck, int millis, int restart);
|
can_send(int sck, int millis, int restart);
|
||||||
@@ -54,6 +66,8 @@ static int
|
|||||||
mysend(int sck, const void *adata, int bytes);
|
mysend(int sck, const void *adata, int bytes);
|
||||||
static int
|
static int
|
||||||
myrecv(int sck, void *adata, int bytes);
|
myrecv(int sck, void *adata, int bytes);
|
||||||
|
static int
|
||||||
|
mypeek(int sck, void *adata, int bytes);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_wts(struct wts_obj *wts)
|
free_wts(struct wts_obj *wts)
|
||||||
@@ -68,60 +82,53 @@ free_wts(struct wts_obj *wts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Opens a handle to the server end of a specified virtual channel - this
|
|
||||||
* call is deprecated - use WTSVirtualChannelOpenEx() instead
|
|
||||||
*
|
|
||||||
* @param hServer
|
|
||||||
* @param SessionId - current session ID; *must* be WTS_CURRENT_SERVER_HANDLE
|
|
||||||
* @param pVirtualName - virtual channel name when using SVC
|
|
||||||
* - name of endpoint listener when using DVC
|
|
||||||
*
|
|
||||||
* @return a valid pointer on success, NULL on error
|
|
||||||
******************************************************************************/
|
|
||||||
void *
|
|
||||||
WTSVirtualChannelOpen(void *hServer, unsigned int SessionId,
|
|
||||||
const char *pVirtualName)
|
|
||||||
{
|
|
||||||
if (hServer != WTS_CURRENT_SERVER_HANDLE)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WTSVirtualChannelOpenEx(SessionId, pVirtualName, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Opens a handle to the server end of a specified virtual channel
|
* Opens a handle to the server end of a specified virtual channel
|
||||||
*
|
*
|
||||||
* @param SessionId - current session ID; *must* be WTS_CURRENT_SERVER_HANDLE
|
* @param SessionId - current session ID; *must* be WTS_CURRENT_SESSION
|
||||||
* @param pVirtualName - virtual channel name when using SVC
|
* @param pVirtualName - virtual channel name when using SVC
|
||||||
* - name of endpoint listener when using DVC
|
* - name of endpoint listener when using DVC
|
||||||
* @param flags - type of channel and channel priority if DVC
|
* @param flags - type of channel and channel priority if DVC
|
||||||
|
* @param private_chan - If != 0, this is a private channel defined
|
||||||
|
* in channel_defs.h
|
||||||
|
* @param[out] errcode - Indication for the user of a possible
|
||||||
|
* error. Cannot be defaulted. Is only set on error.
|
||||||
*
|
*
|
||||||
* @return a valid pointer on success, NULL on error
|
* @return a valid pointer on success, NULL on error
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
void *
|
static void *
|
||||||
WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
VirtualChannelOpen(unsigned int SessionId, const char *pVirtualName,
|
||||||
unsigned int flags)
|
unsigned int flags,
|
||||||
|
unsigned int private_chan,
|
||||||
|
enum wts_errcode *errcode)
|
||||||
{
|
{
|
||||||
struct wts_obj *wts;
|
struct wts_obj *wts;
|
||||||
int bytes;
|
int bytes;
|
||||||
unsigned long long1;
|
unsigned long long1;
|
||||||
struct sockaddr_un s;
|
struct sockaddr_un s;
|
||||||
char *connect_data;
|
// Pad the connect data out to a larger size to allow for
|
||||||
int chan_name_bytes;
|
// changes to struct xrdp_chan_connect
|
||||||
|
union
|
||||||
|
{
|
||||||
|
char pad[XRDPAPI_CONNECT_PDU_LEN];
|
||||||
|
struct xrdp_chan_connect connect_data;
|
||||||
|
} cd = {0};
|
||||||
|
|
||||||
|
uint32_t connect_result;
|
||||||
int lerrno;
|
int lerrno;
|
||||||
|
|
||||||
if (SessionId != WTS_CURRENT_SESSION)
|
if (SessionId != WTS_CURRENT_SESSION)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: bad SessionId");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: bad SessionId");
|
||||||
|
*errcode = WTS_E_BAD_SESSION_ID;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
wts = (struct wts_obj *) calloc(1, sizeof(struct wts_obj));
|
wts = (struct wts_obj *) calloc(1, sizeof(struct wts_obj));
|
||||||
if (wts == NULL)
|
if (wts == NULL)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: calloc failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: calloc failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
wts->fd = -1;
|
wts->fd = -1;
|
||||||
@@ -129,6 +136,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
|||||||
if (wts->display_num < 0)
|
if (wts->display_num < 0)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: fatal error; invalid DISPLAY");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: fatal error; invalid DISPLAY");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -137,6 +145,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
|||||||
if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: socket failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: socket failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -168,6 +177,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect failed");
|
||||||
|
*errcode = WTS_E_CHANSRV_NOT_UP;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -177,50 +187,20 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
|||||||
if (!can_send(wts->fd, 500, 1))
|
if (!can_send(wts->fd, 500, 1))
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_send failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_send failed");
|
||||||
|
*errcode = WTS_E_CHANSRV_NOT_UP;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
chan_name_bytes = strlen(pVirtualName);
|
cd.connect_data.version = XRDPAPI_CONNECT_PDU_VERSION;
|
||||||
bytes = 4 + 4 + 4 + chan_name_bytes + 4;
|
cd.connect_data.private_chan = private_chan;
|
||||||
|
cd.connect_data.flags = flags;
|
||||||
|
strlcpy(cd.connect_data.name, pVirtualName, sizeof(cd.connect_data.name));
|
||||||
|
|
||||||
LOG_DEVEL(LOG_LEVEL_DEBUG,
|
if (mysend(wts->fd, &cd, sizeof(cd)) != sizeof(cd))
|
||||||
"WTSVirtualChannelOpenEx: chan_name_bytes %d bytes %d pVirtualName %s",
|
|
||||||
chan_name_bytes, bytes, pVirtualName);
|
|
||||||
|
|
||||||
connect_data = (char *) calloc(bytes, 1);
|
|
||||||
if (connect_data == NULL)
|
|
||||||
{
|
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: calloc failed");
|
|
||||||
free_wts(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;
|
|
||||||
|
|
||||||
LOG_DEVEL(LOG_LEVEL_DEBUG,
|
|
||||||
"WTSVirtualChannelOpenEx: calling mysend with %d bytes", bytes);
|
|
||||||
|
|
||||||
if (mysend(wts->fd, connect_data, bytes) != bytes)
|
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: mysend failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: mysend failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -229,31 +209,75 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
|||||||
if (!can_recv(wts->fd, 500, 1))
|
if (!can_recv(wts->fd, 500, 1))
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_recv failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_recv failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get response */
|
/* get response */
|
||||||
if (myrecv(wts->fd, connect_data, 4) != 4)
|
if (myrecv(wts->fd, &connect_result, sizeof(connect_result)) !=
|
||||||
|
sizeof(connect_result))
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: myrecv failed");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: myrecv failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((connect_data[0] != 0) || (connect_data[1] != 0) ||
|
if (connect_result != 0)
|
||||||
(connect_data[2] != 0) || (connect_data[3] != 0))
|
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect_data not ok");
|
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect_data not ok");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
free_wts(wts);
|
free_wts(wts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(connect_data);
|
|
||||||
|
|
||||||
return wts;
|
return wts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens a handle to the server end of a specified virtual channel - this
|
||||||
|
* call is deprecated - use WTSVirtualChannelOpenEx() instead
|
||||||
|
*
|
||||||
|
* @param hServer - *must* be WTS_CURRENT_SERVER_HANDLE
|
||||||
|
* @param SessionId - current session ID; *must* be WTS_CURRENT_SESSION
|
||||||
|
* @param pVirtualName - virtual channel name when using SVC
|
||||||
|
* - name of endpoint listener when using DVC
|
||||||
|
*
|
||||||
|
* @return a valid pointer on success, NULL on error
|
||||||
|
******************************************************************************/
|
||||||
|
void *
|
||||||
|
WTSVirtualChannelOpen(void *hServer, unsigned int SessionId,
|
||||||
|
const char *pVirtualName)
|
||||||
|
{
|
||||||
|
enum wts_errcode errcode_dummy = WTS_E_NO_ERROR;
|
||||||
|
|
||||||
|
if (hServer != WTS_CURRENT_SERVER_HANDLE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return VirtualChannelOpen(SessionId, pVirtualName, 0, 0, &errcode_dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens a handle to the server end of a specified virtual channel
|
||||||
|
*
|
||||||
|
* @param SessionId - current session ID; *must* be WTS_CURRENT_SESSION
|
||||||
|
* @param pVirtualName - virtual channel name when using SVC
|
||||||
|
* - name of endpoint listener when using DVC
|
||||||
|
* @param flags - type of channel and channel priority if DVC
|
||||||
|
*
|
||||||
|
* @return a valid pointer on success, NULL on error
|
||||||
|
******************************************************************************/
|
||||||
|
void *
|
||||||
|
WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
enum wts_errcode errcode_dummy = WTS_E_NO_ERROR;
|
||||||
|
return VirtualChannelOpen(SessionId, pVirtualName,
|
||||||
|
flags, 0, &errcode_dummy);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prevent receiving SIGPIPE on disconnect using either MSG_NOSIGNAL (Linux)
|
* Prevent receiving SIGPIPE on disconnect using either MSG_NOSIGNAL (Linux)
|
||||||
* or SO_NOSIGPIPE (Mac OS X)
|
* or SO_NOSIGPIPE (Mac OS X)
|
||||||
@@ -322,6 +346,25 @@ myrecv(int sck, void *adata, int bytes)
|
|||||||
return recd;
|
return recd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
static int
|
||||||
|
mypeek(int sck, void *adata, int bytes)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
#if defined(SO_NOSIGPIPE)
|
||||||
|
const int on = 1;
|
||||||
|
setsockopt(sck, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
if (can_recv(sck, 100, 0))
|
||||||
|
{
|
||||||
|
error = recv(sck, adata, bytes, MSG_NOSIGNAL | MSG_PEEK);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* write data to client connection
|
* write data to client connection
|
||||||
*
|
*
|
||||||
@@ -543,3 +586,225 @@ can_recv(int sck, int millis, int restart)
|
|||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
int WTSQuerySessionInformationA(void *hServer,
|
||||||
|
unsigned int SessionId,
|
||||||
|
WTS_INFO_CLASS WTSInfoClass,
|
||||||
|
void *ppBuffer,
|
||||||
|
DWORD *pBytesReturned,
|
||||||
|
enum wts_errcode *errcode)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
enum wts_errcode errcode_dummy;
|
||||||
|
if (errcode == NULL)
|
||||||
|
{
|
||||||
|
errcode = &errcode_dummy;
|
||||||
|
}
|
||||||
|
*errcode = WTS_E_NO_ERROR;
|
||||||
|
|
||||||
|
if (hServer != WTS_CURRENT_SERVER_HANDLE)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSQuerySessionInformationA: bad hServer");
|
||||||
|
*errcode = WTS_E_BAD_SERVER;
|
||||||
|
}
|
||||||
|
else if (SessionId != WTS_CURRENT_SESSION)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSQuerySessionInformationA: bad SessionId");
|
||||||
|
*errcode = WTS_E_BAD_SESSION_ID;
|
||||||
|
}
|
||||||
|
else if (WTSInfoClass != WTSConnectState)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"WTSQuerySessionInformationA: unsupported WTSInfoClass");
|
||||||
|
*errcode = WTS_E_BAD_INFO_CLASS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = 1; // Assume success
|
||||||
|
if (wts_current_server.info_obj == NULL)
|
||||||
|
{
|
||||||
|
// We don't have a current connection for server state events.
|
||||||
|
// Set one up to update our cached values, then tear it down again.
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
rv = WTSRegisterSessionNotificationEx(hServer, &fd, 0, errcode) &&
|
||||||
|
WTSUnRegisterSessionNotificationEx(hServer, fd, errcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv == 1)
|
||||||
|
{
|
||||||
|
*(WTS_CONNECTSTATE_CLASS *)ppBuffer =
|
||||||
|
(wts_current_server.session_state.is_connected)
|
||||||
|
? WTSConnected
|
||||||
|
: WTSDisconnected;
|
||||||
|
if (pBytesReturned != NULL)
|
||||||
|
{
|
||||||
|
*pBytesReturned = sizeof(WTS_CONNECTSTATE_CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
int WTSRegisterSessionNotificationEx(void *hServer,
|
||||||
|
int *fd_ptr,
|
||||||
|
int dwFlags,
|
||||||
|
enum wts_errcode *errcode)
|
||||||
|
{
|
||||||
|
(void)dwFlags; // Unused parameter
|
||||||
|
enum wts_errcode errcode_dummy;
|
||||||
|
if (errcode == NULL)
|
||||||
|
{
|
||||||
|
errcode = &errcode_dummy;
|
||||||
|
}
|
||||||
|
*errcode = WTS_E_NO_ERROR;
|
||||||
|
|
||||||
|
if (hServer != WTS_CURRENT_SERVER_HANDLE)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSRegisterSessionNotificationEx: bad hServer");
|
||||||
|
*errcode = WTS_E_BAD_SERVER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wts_current_server.info_obj == NULL)
|
||||||
|
{
|
||||||
|
// Open a private channel for session info messages
|
||||||
|
// The name and flags args are ignored for xrdp private channels
|
||||||
|
struct wts_obj *wts = (struct wts_obj *)
|
||||||
|
VirtualChannelOpen(WTS_CURRENT_SESSION,
|
||||||
|
"", 0,
|
||||||
|
CHAN_ID_XRDP_SESSION_INFO,
|
||||||
|
errcode);
|
||||||
|
if (wts != NULL)
|
||||||
|
{
|
||||||
|
// Server will pass the current session state now
|
||||||
|
struct xrdp_chan_session_state sess_state;
|
||||||
|
if (!can_recv(wts->fd, 1000, 1))
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"WTSRegisterSessionNotificationEx: can_recv failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
|
free_wts(wts);
|
||||||
|
}
|
||||||
|
/* get server_status */
|
||||||
|
else if (myrecv(wts->fd, &sess_state, sizeof(sess_state)) !=
|
||||||
|
sizeof(sess_state))
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR,
|
||||||
|
"WTSRegisterSessionNotificationEx: myrecv failed");
|
||||||
|
*errcode = WTS_E_RESOURCE_ERROR;
|
||||||
|
free_wts(wts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wts_current_server.info_obj = wts;
|
||||||
|
wts_current_server.session_state = sess_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wts_current_server.info_obj == NULL)
|
||||||
|
{
|
||||||
|
*fd_ptr = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*fd_ptr = wts_current_server.info_obj->fd;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
int WTSUnRegisterSessionNotificationEx(void *hServer,
|
||||||
|
int fd,
|
||||||
|
enum wts_errcode *errcode)
|
||||||
|
{
|
||||||
|
(void)fd; // Unused parameter
|
||||||
|
enum wts_errcode errcode_dummy;
|
||||||
|
if (errcode == NULL)
|
||||||
|
{
|
||||||
|
errcode = &errcode_dummy;
|
||||||
|
}
|
||||||
|
*errcode = WTS_E_NO_ERROR;
|
||||||
|
|
||||||
|
if (hServer != WTS_CURRENT_SERVER_HANDLE)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSUnRegisterSessionNotificationEx: bad hServer");
|
||||||
|
*errcode = WTS_E_BAD_SERVER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_wts(wts_current_server.info_obj);
|
||||||
|
wts_current_server.info_obj = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
int
|
||||||
|
WTSGetDispatchMessage(void *cbdata, WNDPROC wndproc, LRESULT *lResult)
|
||||||
|
{
|
||||||
|
LRESULT result = 0;
|
||||||
|
struct xrdp_chan_session_state new_state;
|
||||||
|
|
||||||
|
/* get response */
|
||||||
|
if (wts_current_server.info_obj == NULL)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSGetDispatchMessage: No notification channel was opened");
|
||||||
|
}
|
||||||
|
else if (!can_recv(wts_current_server.info_obj->fd, 0, 1))
|
||||||
|
{
|
||||||
|
// No message available - nothing to log
|
||||||
|
}
|
||||||
|
else if (mypeek(wts_current_server.info_obj->fd,
|
||||||
|
&new_state, sizeof(new_state)) != sizeof(new_state))
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "WTSGetDispatchMessage: An incomplete message was received");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We've peeked a message. Find a SINGLE difference with our
|
||||||
|
* own state, update our state, and issue a callback. If there
|
||||||
|
* are more differences, the application will call us back and
|
||||||
|
* we can process the next one. When all the differences are
|
||||||
|
* accounted for, we can clear the message from the queue, and
|
||||||
|
* the fd will no longer be readable */
|
||||||
|
UINT msgno = 0; // 0 means 'no message'
|
||||||
|
WPARAM wParam = 0;
|
||||||
|
LPARAM lParam = 0;
|
||||||
|
// Look for a single difference in the state we have, and the
|
||||||
|
// current state from the server
|
||||||
|
struct xrdp_chan_session_state *curr_state;
|
||||||
|
curr_state = &wts_current_server.session_state;
|
||||||
|
|
||||||
|
if (curr_state->is_connected != new_state.is_connected)
|
||||||
|
{
|
||||||
|
curr_state->is_connected = new_state.is_connected;
|
||||||
|
msgno = WM_WTSSESSION_CHANGE;
|
||||||
|
wParam = (new_state.is_connected)
|
||||||
|
? WTS_REMOTE_CONNECT : WTS_REMOTE_DISCONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a difference, activate the callback
|
||||||
|
if (msgno != 0)
|
||||||
|
{
|
||||||
|
*lResult = wndproc(cbdata, msgno, wParam, lParam);
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've exhausted all the differences between the old and
|
||||||
|
// the new state, purge the message from the queue
|
||||||
|
if (curr_state->is_connected == new_state.is_connected &&
|
||||||
|
/* Add further checks here in the future */
|
||||||
|
1)
|
||||||
|
{
|
||||||
|
/* Sanity check on the fd */
|
||||||
|
if (wts_current_server.info_obj->fd >= 0)
|
||||||
|
{
|
||||||
|
(void)myrecv(wts_current_server.info_obj->fd,
|
||||||
|
&new_state, sizeof(new_state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#if !defined(XRDPAPI_H_)
|
#if !defined(XRDPAPI_H_)
|
||||||
#define XRDPAPI_H_
|
#define XRDPAPI_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -41,6 +43,42 @@ extern "C" {
|
|||||||
#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_HIGH 0x00000003
|
#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_HIGH 0x00000003
|
||||||
#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_REAL 0x00000004
|
#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_REAL 0x00000004
|
||||||
|
|
||||||
|
#define WM_WTSSESSION_CHANGE 0x02B1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* codes passed in WPARAM for WM_WTSSESSION_CHANGE
|
||||||
|
* Unlisted codes are not yet implemented.
|
||||||
|
*/
|
||||||
|
#define WTS_REMOTE_CONNECT 0x3
|
||||||
|
#define WTS_REMOTE_DISCONNECT 0x4
|
||||||
|
|
||||||
|
typedef enum _WTS_INFO_CLASS
|
||||||
|
{
|
||||||
|
//WTSInitialProgram, // Not yet implemented
|
||||||
|
//WTSApplicationName, // Not yet implemented
|
||||||
|
//WTSWorkingDirectory, // Not yet implemented
|
||||||
|
//WTSOEMId, // Not yet implemented
|
||||||
|
//WTSSessionId, // Not yet implemented
|
||||||
|
//WTSUserName, // Not yet implemented
|
||||||
|
//WTSWinStationName, // Not yet implemented
|
||||||
|
//WTSDomainName, // Not yet implemented
|
||||||
|
WTSConnectState = 8
|
||||||
|
} WTS_INFO_CLASS;
|
||||||
|
|
||||||
|
typedef enum _WTS_CONNECTSTATE_CLASS
|
||||||
|
{
|
||||||
|
// WTSActive,
|
||||||
|
WTSConnected = 1,
|
||||||
|
// WTSConnectQuery,
|
||||||
|
// WTSShadow,
|
||||||
|
WTSDisconnected = 4,
|
||||||
|
// WTSIdle,
|
||||||
|
// WTSListen,
|
||||||
|
// WTSReset,
|
||||||
|
// WTSDown,
|
||||||
|
// WTSInit
|
||||||
|
} WTS_CONNECTSTATE_CLASS;
|
||||||
|
|
||||||
typedef enum _WTS_VIRTUAL_CLASS
|
typedef enum _WTS_VIRTUAL_CLASS
|
||||||
{
|
{
|
||||||
WTSVirtualClientData,
|
WTSVirtualClientData,
|
||||||
@@ -48,6 +86,35 @@ typedef enum _WTS_VIRTUAL_CLASS
|
|||||||
}
|
}
|
||||||
WTS_VIRTUAL_CLASS;
|
WTS_VIRTUAL_CLASS;
|
||||||
|
|
||||||
|
// Enumerated type for an error code from some calls. This is not
|
||||||
|
// compatible with the Windows API.
|
||||||
|
enum wts_errcode
|
||||||
|
{
|
||||||
|
WTS_E_NO_ERROR = 0,
|
||||||
|
// Retryable errors
|
||||||
|
WTS_E_CHANSRV_NOT_UP,
|
||||||
|
// Fatal errors
|
||||||
|
WTS_E_BAD_SERVER = 32,
|
||||||
|
WTS_E_BAD_SESSION_ID,
|
||||||
|
WTS_E_RESOURCE_ERROR,
|
||||||
|
WTS_E_BAD_INFO_CLASS
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WTS_ERRCODE_FATAL(errcode) ((int)(errcode) >= (int)WTS_E_BAD_SERVER)
|
||||||
|
|
||||||
|
// Win32 basic types
|
||||||
|
// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
||||||
|
typedef uint32_t DWORD;
|
||||||
|
typedef uint32_t UINT;
|
||||||
|
typedef intptr_t UINT_PTR;
|
||||||
|
typedef intptr_t LONG_PTR;
|
||||||
|
typedef UINT_PTR WPARAM;
|
||||||
|
typedef LONG_PTR LPARAM;
|
||||||
|
typedef LONG_PTR LRESULT;
|
||||||
|
|
||||||
|
// WNDPROC emulation for WTSRegisterSessionNotificationEx()
|
||||||
|
typedef LRESULT WNDPROC(void *cbdata, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference:
|
* Reference:
|
||||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383464(v=vs.85).aspx
|
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa383464(v=vs.85).aspx
|
||||||
@@ -73,6 +140,82 @@ int WTSVirtualChannelQuery(void *hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualCla
|
|||||||
|
|
||||||
void WTSFreeMemory(void *pMemory);
|
void WTSFreeMemory(void *pMemory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is similar to, but not the same as the Win32
|
||||||
|
* function of the same name.
|
||||||
|
*
|
||||||
|
* The purpose of it is to allow an application to find out
|
||||||
|
* (rather limited) information about the session
|
||||||
|
*
|
||||||
|
* @param hServer set to WTS_CURRENT_SERVER_HANDLE
|
||||||
|
* @param SessionId current session ID; *must* be WTS_CURRENT_SESSION
|
||||||
|
* @param WTSInfoClass parameter to query
|
||||||
|
* @param ppBuffer pointer for result
|
||||||
|
* @param pBytesReturned size of result
|
||||||
|
* @param[out] errcode Status of operation if false returned. Can be NULL.
|
||||||
|
* @return true for success
|
||||||
|
*/
|
||||||
|
int WTSQuerySessionInformationA(void *hServer,
|
||||||
|
unsigned int SessionId,
|
||||||
|
WTS_INFO_CLASS WTSInfoClass,
|
||||||
|
void *ppBuffer,
|
||||||
|
DWORD *pBytesReturned,
|
||||||
|
enum wts_errcode *errcode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is similar to, but not the same as the Win32
|
||||||
|
* function of the same name.
|
||||||
|
*
|
||||||
|
* The purpose of it is to allow an application to receive
|
||||||
|
* WM_WTSSESSION_CHANGE messages.
|
||||||
|
*
|
||||||
|
* @param hServer set to WTS_CURRENT_SERVER_HANDLE
|
||||||
|
* @param[out] fd_ptr File descriptor to check for notification messages
|
||||||
|
* @param dwFlags ignored
|
||||||
|
* @param[out] errcode Status of operation if false returned. Can be NULL.
|
||||||
|
* @return true for success
|
||||||
|
*
|
||||||
|
* The fd_ptr replaces the hWnd parameter in the Win32 call.
|
||||||
|
*
|
||||||
|
* After a successful call, the location pointed-to by fd_ptr will contain a
|
||||||
|
* file descriptor which the caller can poll for session change messages.
|
||||||
|
* When the file descriptor becomes readable, a call to
|
||||||
|
* WTSGetDispatchMessage() will process a single session change message.
|
||||||
|
*
|
||||||
|
* The caller must do nothing with the returned file descriptor except poll it.
|
||||||
|
*/
|
||||||
|
int WTSRegisterSessionNotificationEx(void *hServer,
|
||||||
|
int *fd_ptr,
|
||||||
|
int dwFlags,
|
||||||
|
enum wts_errcode *errcode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is similar to, but not the same as the Win32
|
||||||
|
* function of the same name.
|
||||||
|
*
|
||||||
|
* The purpose of it is to deallocate resources associated with
|
||||||
|
* WTSRegisterSessionNotificationEx()
|
||||||
|
*
|
||||||
|
* @param hServer set to WTS_CURRENT_SERVER_HANDLE
|
||||||
|
* @param fd File descriptor from WTSRegisterSessionNotificationEx
|
||||||
|
* @param[out] errcode Status of operation if false returned. Can be NULL.
|
||||||
|
* @return true for success
|
||||||
|
*/
|
||||||
|
int WTSUnRegisterSessionNotificationEx(void *hServer,
|
||||||
|
int fd,
|
||||||
|
enum wts_errcode *errcode);
|
||||||
|
|
||||||
|
|
||||||
|
/** Replaces Win32 GetMessage() / DispatchMessage()
|
||||||
|
*
|
||||||
|
* @param cbdata callback data to pass in to WNDPROC
|
||||||
|
* @param wndproc WNDPROC to call
|
||||||
|
* @param[out] lResult Result of WNDPROC if call is successful
|
||||||
|
* @return != 0 if a WNDPROC was successfully called
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
WTSGetDispatchMessage(void *cbdata, WNDPROC wndproc, LRESULT *lResult);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user