Merge pull request #3640 from matt335672/xrdpapi_connect_status
xrdpapi: Add a way to monitor connect/disconnect events
This commit is contained in:
@@ -47,6 +47,7 @@ libcommon_la_SOURCES = \
|
||||
arch.h \
|
||||
base64.h \
|
||||
base64.c \
|
||||
channel_defs.h \
|
||||
defines.h \
|
||||
fifo.c \
|
||||
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++; \
|
||||
} 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 \
|
||||
{ \
|
||||
|
||||
+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();
|
||||
|
||||
+13
-2
@@ -1,5 +1,6 @@
|
||||
EXTRA_DIST = \
|
||||
simple.c \
|
||||
connectmon.c \
|
||||
vrplayer.c \
|
||||
vrplayer.mk
|
||||
|
||||
@@ -17,8 +18,18 @@ libxrdpapi_la_SOURCES = \
|
||||
libxrdpapi_la_LIBADD = \
|
||||
$(top_builddir)/common/libcommon.la
|
||||
|
||||
# Build the 'simple' example program, so it's added to the CI
|
||||
noinst_PROGRAMS = xrdp-xrdpapi-simple
|
||||
# Build the 'simple' example programs, so they are added to the CI
|
||||
noinst_PROGRAMS = \
|
||||
xrdp-xrdpapi-connectmon \
|
||||
xrdp-xrdpapi-simple
|
||||
|
||||
xrdp_xrdpapi_connectmon_SOURCES = \
|
||||
connectmon.c
|
||||
|
||||
# If you change this, update the standalone build instructions in connectmon.c
|
||||
xrdp_xrdpapi_connectmon_LDADD = \
|
||||
libxrdpapi.la \
|
||||
$(top_builddir)/common/libcommon.la
|
||||
|
||||
xrdp_xrdpapi_simple_SOURCES = \
|
||||
simple.c
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Jay Sorg 2012-2013
|
||||
* Copyright (C) Laxmikant Rashinkar 2012-2013
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* sample program to demonstrate use of xrdpapi connection monitoring
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* build instructions:
|
||||
* gcc connectmon.c -o connectmon \
|
||||
* -I.. -I../common -L./.libs -L../common/.libs \
|
||||
* -DHAVE_CONFIG_H -lxrdpapi -lcommon
|
||||
*/
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config_ac.h>
|
||||
#endif
|
||||
|
||||
#include "xrdpapi.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
/******************************************************************************/
|
||||
static void
|
||||
print_current_connection_state(void)
|
||||
{
|
||||
WTS_CONNECTSTATE_CLASS connect_state;
|
||||
char buff[64];
|
||||
const char *statestr = "unavailable";
|
||||
if (WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE,
|
||||
WTS_CURRENT_SESSION,
|
||||
WTSConnectState,
|
||||
&connect_state, NULL, NULL))
|
||||
{
|
||||
if (connect_state == WTSConnected)
|
||||
{
|
||||
statestr = "connected";
|
||||
}
|
||||
else if (connect_state == WTSDisconnected)
|
||||
{
|
||||
statestr = "disconnected";
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buff, sizeof(buff),
|
||||
"unrecognised val %d", (int)connect_state);
|
||||
statestr = buff;
|
||||
}
|
||||
}
|
||||
printf("Current connect state is %s\n", statestr);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
static LRESULT
|
||||
callback (void *cbdata, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
LRESULT result = 1;
|
||||
int *countptr = (int *)cbdata;
|
||||
|
||||
switch (wParam)
|
||||
{
|
||||
case WTS_REMOTE_CONNECT:
|
||||
++*countptr;
|
||||
printf("State change %d : Connect to session\n", *countptr);
|
||||
print_current_connection_state();
|
||||
break;
|
||||
|
||||
case WTS_REMOTE_DISCONNECT:
|
||||
++*countptr;
|
||||
printf("State change %d : Disconnect from session\n", *countptr);
|
||||
print_current_connection_state();
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("** Unexpected callback reason %ld\n", (long)wParam);
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int result = 0;
|
||||
int changes = 10;
|
||||
int fd;
|
||||
struct log_config *lc;
|
||||
|
||||
if ((lc = log_config_init_for_console(LOG_LEVEL_DEBUG, NULL)) != NULL)
|
||||
{
|
||||
log_start_from_param(lc);
|
||||
}
|
||||
|
||||
if (argc > 1 && atoi(argv[1]) > 0)
|
||||
{
|
||||
changes = atoi(argv[1]);
|
||||
}
|
||||
printf("Connection monitor : exiting after %d state changes\n", changes);
|
||||
|
||||
print_current_connection_state();
|
||||
|
||||
|
||||
if (!WTSRegisterSessionNotificationEx(WTS_CURRENT_SERVER_HANDLE, &fd, 0, NULL))
|
||||
{
|
||||
result = 1; // Error occurred (should be logged)
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = 0;
|
||||
while (count < changes && result == 0)
|
||||
{
|
||||
struct pollfd pollarg =
|
||||
{
|
||||
.fd = fd,
|
||||
.events = (POLLIN | POLLERR),
|
||||
.revents = 0
|
||||
};
|
||||
int pollstat;
|
||||
LRESULT cbresult;
|
||||
|
||||
if ((pollstat = poll(&pollarg, 1, -1)) < 0)
|
||||
{
|
||||
printf("** Error from poll() : %s\n", strerror(errno));
|
||||
result = 1;
|
||||
}
|
||||
else if (pollstat == 0)
|
||||
{
|
||||
printf("** Timeout from poll() !?\n");
|
||||
result = 1;
|
||||
}
|
||||
else if ((pollarg.revents & POLLERR))
|
||||
{
|
||||
printf("** File descriptor was closed\n");
|
||||
result = 1;
|
||||
}
|
||||
else if (!WTSGetDispatchMessage(&count, callback, &cbresult))
|
||||
{
|
||||
printf("** Unexpected faiure to dispatch message\n");
|
||||
result = 1;
|
||||
}
|
||||
else if (!cbresult)
|
||||
{
|
||||
printf("** Callback failed\n");
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
|
||||
(void)WTSUnRegisterSessionNotificationEx(WTS_CURRENT_SERVER_HANDLE,
|
||||
fd, NULL);
|
||||
}
|
||||
|
||||
if (lc != NULL)
|
||||
{
|
||||
log_config_free(lc);
|
||||
log_end();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
+1
-1
@@ -158,7 +158,7 @@ run_echo_test(void)
|
||||
/* validate the echo */
|
||||
for (i = 0; i < bytes_read; i++, rd_ptr++)
|
||||
{
|
||||
if (*rd_ptr != (unsigned char) in_buf[i])
|
||||
if (*rd_ptr != in_buf[i])
|
||||
{
|
||||
printf("### data mismatch: expected 0x%x got 0x%x\n",
|
||||
(unsigned char) *rd_ptr, (unsigned char) in_buf[i]);
|
||||
|
||||
+336
-69
@@ -37,6 +37,7 @@
|
||||
#include "log.h"
|
||||
#include "xrdp_sockets.h"
|
||||
#include "string_calls.h"
|
||||
#include "channel_defs.h"
|
||||
#include "xrdpapi.h"
|
||||
|
||||
struct wts_obj
|
||||
@@ -45,6 +46,17 @@ struct wts_obj
|
||||
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 */
|
||||
static int
|
||||
can_send(int sck, int millis, int restart);
|
||||
@@ -54,6 +66,8 @@ static int
|
||||
mysend(int sck, const void *adata, int bytes);
|
||||
static int
|
||||
myrecv(int sck, void *adata, int bytes);
|
||||
static int
|
||||
mypeek(int sck, void *adata, int bytes);
|
||||
|
||||
static void
|
||||
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
|
||||
*
|
||||
* @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
|
||||
* - name of endpoint listener when using 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
|
||||
******************************************************************************/
|
||||
void *
|
||||
WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
unsigned int flags)
|
||||
static void *
|
||||
VirtualChannelOpen(unsigned int SessionId, const char *pVirtualName,
|
||||
unsigned int flags,
|
||||
unsigned int private_chan,
|
||||
enum wts_errcode *errcode)
|
||||
{
|
||||
struct wts_obj *wts;
|
||||
int bytes;
|
||||
unsigned long long1;
|
||||
struct sockaddr_un s;
|
||||
char *connect_data;
|
||||
int chan_name_bytes;
|
||||
// Pad the connect data out to a larger size to allow for
|
||||
// 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;
|
||||
|
||||
if (SessionId != WTS_CURRENT_SESSION)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: bad SessionId");
|
||||
*errcode = WTS_E_BAD_SESSION_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
wts = (struct wts_obj *) calloc(1, sizeof(struct wts_obj));
|
||||
if (wts == NULL)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: calloc failed");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
wts->fd = -1;
|
||||
@@ -129,6 +136,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
if (wts->display_num < 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: fatal error; invalid DISPLAY");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
@@ -137,6 +145,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: socket failed");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
@@ -168,6 +177,7 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
else
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect failed");
|
||||
*errcode = WTS_E_CHANSRV_NOT_UP;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
@@ -177,50 +187,20 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
if (!can_send(wts->fd, 500, 1))
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_send failed");
|
||||
*errcode = WTS_E_CHANSRV_NOT_UP;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chan_name_bytes = strlen(pVirtualName);
|
||||
bytes = 4 + 4 + 4 + chan_name_bytes + 4;
|
||||
cd.connect_data.version = XRDPAPI_CONNECT_PDU_VERSION;
|
||||
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,
|
||||
"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)
|
||||
if (mysend(wts->fd, &cd, sizeof(cd)) != sizeof(cd))
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: mysend failed");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
@@ -229,22 +209,25 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
if (!can_recv(wts->fd, 500, 1))
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: can_recv failed");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((connect_data[0] != 0) || (connect_data[1] != 0) ||
|
||||
(connect_data[2] != 0) || (connect_data[3] != 0))
|
||||
if (connect_result != 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "WTSVirtualChannelOpenEx: connect_data not ok");
|
||||
*errcode = WTS_E_RESOURCE_ERROR;
|
||||
free_wts(wts);
|
||||
return NULL;
|
||||
}
|
||||
@@ -252,6 +235,49 @@ WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName,
|
||||
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)
|
||||
* or SO_NOSIGPIPE (Mac OS X)
|
||||
@@ -320,6 +346,25 @@ myrecv(int sck, void *adata, int bytes)
|
||||
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
|
||||
*
|
||||
@@ -541,3 +586,225 @@ can_recv(int sck, int millis, int restart)
|
||||
|
||||
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_)
|
||||
#define XRDPAPI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -41,6 +43,42 @@ extern "C" {
|
||||
#define WTS_CHANNEL_OPTION_DYNAMIC_PRI_HIGH 0x00000003
|
||||
#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
|
||||
{
|
||||
WTSVirtualClientData,
|
||||
@@ -48,6 +86,35 @@ typedef enum _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:
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user