Add support for forwarded resize request to VNC module

This feature was added to the RFB community wiki on 2020-12-22. See
commit a6dab64825b2ce4e9b47b8c9541619b94b7d908b in the rfbproto
repository
This commit is contained in:
matt335672
2025-08-21 12:28:10 +01:00
parent 3700867e87
commit 0ff175c85e
4 changed files with 185 additions and 31 deletions
+1
View File
@@ -31,6 +31,7 @@ static const char *eds_status_msg[] =
/* 1 */ "Resize is administratively prohibited", /* 1 */ "Resize is administratively prohibited",
/* 2 */ "Out of resources", /* 2 */ "Out of resources",
/* 3 */ "Invalid screen layout", /* 3 */ "Invalid screen layout",
/* 4 */ "Request forwarded",
/* others */ "Unknown code" /* others */ "Unknown code"
}; };
+12
View File
@@ -80,6 +80,18 @@ enum sec_type
#define RFBPROTO_VER_3_7 MAKE_RFBPROTO_VER(3,7) #define RFBPROTO_VER_3_7 MAKE_RFBPROTO_VER(3,7)
#define RFBPROTO_VER_3_8 MAKE_RFBPROTO_VER(3,8) #define RFBPROTO_VER_3_8 MAKE_RFBPROTO_VER(3,8)
/*
* ExtendedDesktopSize status codes
*/
enum
{
RFB_EDS_NO_ERROR = 0,
RFB_EDS_ADMINISTRATIVELY_PROHIBITED = 1,
RFB_EDS_OUT_OF_RESOURCES = 2,
RFB_EDS_INVALID_SCREEN_LAYOUT = 3,
RFB_EDS_REQUEST_FORWARDED = 4
};
/** /**
* Returns an error string for an ExtendedDesktopSize status code * Returns an error string for an ExtendedDesktopSize status code
*/ */
+165 -31
View File
@@ -38,6 +38,7 @@
#include "vnc_clip.h" #include "vnc_clip.h"
#include "rfb.h" #include "rfb.h"
#include "log.h" #include "log.h"
#include "timers.h"
#include "trans.h" #include "trans.h"
#include "ssl_calls.h" #include "ssl_calls.h"
#include "string_calls.h" #include "string_calls.h"
@@ -53,6 +54,12 @@ enum
MSK_EXTENDED_DESKTOP_SIZE = (1 << 0) MSK_EXTENDED_DESKTOP_SIZE = (1 << 0)
}; };
enum
{
/** Time to wait for a forwarded resize to complete */
FORWARDED_RESIZE_TIMEOUT = 1500 /* milli-seconds */
};
/******************************************************************************/ /******************************************************************************/
int int
lib_send_copy(struct vnc *v, struct stream *s) lib_send_copy(struct vnc *v, struct stream *s)
@@ -309,7 +316,8 @@ send_set_desktop_size(struct vnc *v, const struct vnc_screen_layout *layout)
out_uint32_be(s, layout->s[i].flags); out_uint32_be(s, layout->s[i].flags);
} }
s_mark_end(s); s_mark_end(s);
LOG(LOG_LEVEL_DEBUG, "VNC Sending SetDesktopSize"); LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE: Sending SetDesktopSize %dx%d",
layout->total_width, layout->total_height);
error = lib_send_copy(v, s); error = lib_send_copy(v, s);
free_stream(s); free_stream(s);
@@ -954,7 +962,7 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
{ {
struct vnc_screen_layout layout = {0}; struct vnc_screen_layout layout = {0};
LOG(LOG_LEVEL_DEBUG, LOG(LOG_LEVEL_DEBUG,
"Skipping RFB_ENC_EXTENDED_DESKTOP_SIZE encoding " "VNC_RESIZE: Skipping RFB_ENC_EXTENDED_DESKTOP_SIZE encoding "
"x=%d, y=%d geom=%dx%d", "x=%d, y=%d geom=%dx%d",
x, y, cx, cy); x, y, cx, cy);
error = read_extended_desktop_size_rect(v, &layout); error = read_extended_desktop_size_rect(v, &layout);
@@ -1033,7 +1041,7 @@ find_matching_extended_rect(struct vnc *v,
match(x, y, cx, cy)) match(x, y, cx, cy))
{ {
LOG(LOG_LEVEL_DEBUG, LOG(LOG_LEVEL_DEBUG,
"VNC matched ExtendedDesktopSize rectangle " "VNC_RESIZE: VNC matched ExtendedDesktopSize rectangle "
"x=%d, y=%d geom=%dx%d", "x=%d, y=%d geom=%dx%d",
x, y, cx, cy); x, y, cx, cy);
found = 1; found = 1;
@@ -1149,6 +1157,18 @@ rect_is_reply_to_us(int x, int y, int cx, int cy)
return (x == 1); return (x == 1);
} }
/**************************************************************************//**
* Tests if extended desktop size rect is a general change.
*
* This happens when we are looking for a layout change that the
* VNC server has reported as forwarded to the real desktop.
*/
static int
rect_is_general_change(int x, int y, int cx, int cy)
{
return (x == 0);
}
/**************************************************************************//** /**************************************************************************//**
* Handles the first framebuffer update from the server * Handles the first framebuffer update from the server
* *
@@ -1249,47 +1269,138 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
{ {
if (layout.count > 0) if (layout.count > 0)
{ {
if (response_code == 0) if (response_code == RFB_EDS_REQUEST_FORWARDED)
{ {
LOG(LOG_LEVEL_DEBUG, "VNC server successfully resized"); LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE: VNC server resize forwarded");
log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout); log_screen_layout(LOG_LEVEL_INFO, "ForwardedLayout", &layout);
v->server_layout = layout; v->forward_timer =
timers_oneshot_init(FORWARDED_RESIZE_TIMEOUT);
v->forwarded_layout = layout;
} }
else else
{ {
LOG(LOG_LEVEL_WARNING, // The resize has either succeeded or failed
"VNC server resize failed - error code %d [%s]", if (response_code == RFB_EDS_NO_ERROR)
response_code, {
rfb_get_eds_status_msg(response_code)); LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE:"
// This is awkward. The client has asked for a specific size " VNC server successfully resized");
// which we can't support. log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout);
// v->server_layout = layout;
// Currently we handle this by queueing a resize to our }
// supported size, and continuing with the resize state else
// machine in xrdp_mm.c {
LOG(LOG_LEVEL_WARNING, "Resizing client to server"); LOG(LOG_LEVEL_WARNING,
error = resize_client_to_server(v, 0); "VNC server resize failed - error code %d [%s]",
} response_code,
rfb_get_eds_status_msg(response_code));
// This is awkward. The client has asked for a
// specific size which we can't support.
//
// Currently we handle this by queueing a resize
// to our supported size, and continuing with the
// resize state machine in xrdp_mm.c
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
error = resize_client_to_server(v, 0);
}
if (error == 0) v->resize_status = VRS_DONE;
{ if (error == 0)
// If this resize was requested by the client mid-session {
// (dynamic resize), we need to tell xrdp_mm that // If this resize was requested by the client mid-session
// it's OK to continue with the resize state machine. // (dynamic resize), we need to tell xrdp_mm that
error = v->server_monitor_resize_done(v); // it's OK to continue with the resize state machine.
error = v->server_monitor_resize_done(v);
if (error == 0)
{
error = send_update_request_for_resize_status(v);
}
}
} }
v->resize_status = VRS_DONE;
} }
} }
return error;
}
/**************************************************************************//**
* Looks for the forwarded screen layout in a framebuffer update request
*
* Looks for an ExtendedDesktopSize rectangle following a notification
* from the VNC server that the request has been forwarded to the real
* desktop. See rfbproto/pfbproto#32 for more info.
*
* @param v VNC object
* @return != 0 for error
*/
static int
lib_framebuffer_look_for_forwarded_layout(struct vnc *v)
{
int error;
struct vnc_screen_layout layout = {0};
int x = 0;
int y = 0;
error = find_matching_extended_rect(v,
rect_is_general_change,
&x,
&y,
&layout);
if (error == 0) if (error == 0)
{ {
error = send_update_request_for_resize_status(v); if (layout.count > 0)
{
if (vnc_screen_layouts_equal(&layout, &v->forwarded_layout))
{
LOG(LOG_LEVEL_DEBUG,
"VNC_RESIZE: VNC server forwarded resize complete");
free(v->forward_timer);
v->forward_timer = NULL;
v->server_layout = layout;
error = v->server_monitor_resize_done(v);
v->resize_status = VRS_DONE;
}
else
{
LOG(LOG_LEVEL_DEBUG,
"VNC_RESIZE: Ignored ExtendedDesktopSize %dx%d x=%d y=%d",
layout.total_width, layout.total_height, x, y);
// Delay for a little before we send another request for
// the size
g_sleep(100);
}
error = send_update_request_for_resize_status(v);
}
} }
return error; return error;
} }
/******************************************************************************/
/*
* The VNC server has not actioned a forwarded resize request
*/
static int
forward_timer_expired(struct vnc *v)
{
LOG(LOG_LEVEL_WARNING, "VNC server forwarded resize timed out");
LOG(LOG_LEVEL_DEBUG,
"VNC_RESIZE: VNC server forwarded resize timed out");
free(v->forward_timer);
v->forward_timer = NULL;
v->resize_status = VRS_DONE;
int rv = v->server_monitor_resize_done(v);
if (rv == 0)
{
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
rv = resize_client_to_server(v, 0);
}
return rv;
}
/******************************************************************************/ /******************************************************************************/
static int static int
lib_framebuffer_update(struct vnc *v) lib_framebuffer_update(struct vnc *v)
@@ -1433,6 +1544,9 @@ lib_framebuffer_update(struct vnc *v)
layout.total_height = cy; layout.total_height = cy;
error = read_extended_desktop_size_rect(v, &layout); error = read_extended_desktop_size_rect(v, &layout);
/* If this is a reply to a request from us, x == 1 */ /* If this is a reply to a request from us, x == 1 */
LOG(LOG_LEVEL_DEBUG,
"VNC_RESIZE: Read ExtendedDesktopSize %dx%d x=%d y=%d",
layout.total_width, layout.total_height, x, y);
if (error == 0 && x != 1) if (error == 0 && x != 1)
{ {
if (!vnc_screen_layouts_equal(&v->server_layout, &layout)) if (!vnc_screen_layouts_equal(&v->server_layout, &layout))
@@ -1574,7 +1688,14 @@ lib_mod_process_message(struct vnc *v, struct stream *s)
break; break;
case VRS_WAITING_FOR_RESIZE_CONFIRM: case VRS_WAITING_FOR_RESIZE_CONFIRM:
error = lib_framebuffer_waiting_for_resize_confirm(v); if (v->forward_timer != NULL)
{
error = lib_framebuffer_look_for_forwarded_layout(v);
}
else
{
error = lib_framebuffer_waiting_for_resize_confirm(v);
}
break; break;
default: default:
@@ -2533,6 +2654,10 @@ lib_mod_get_wait_objs(struct vnc *v, tbus *read_objs, int *rcount,
trans_get_wait_objs_rw(v->trans, read_objs, rcount, trans_get_wait_objs_rw(v->trans, read_objs, rcount,
write_objs, wcount, timeout); write_objs, wcount, timeout);
} }
// Update timeout with any active timers
unsigned int now = g_get_elapsed_ms();
timers_oneshot_update_poll(v->forward_timer, now, timeout);
} }
return 0; return 0;
@@ -2550,13 +2675,22 @@ lib_mod_check_wait_objs(struct vnc *v)
{ {
if (v->trans != 0) if (v->trans != 0)
{ {
rv = trans_check_wait_objs(v->trans); if ((rv = trans_check_wait_objs(v->trans)) != 0)
if (rv != 0)
{ {
LOG(LOG_LEVEL_ERROR, "VNC server closed connection"); LOG(LOG_LEVEL_ERROR, "VNC server closed connection");
} }
else
{
// Check timers
unsigned int now = g_get_elapsed_ms();
if (timers_oneshot_get_remaining(v->forward_timer, now) == 0)
{
rv = forward_timer_expired(v);
}
}
} }
} }
return rv; return rv;
} }
+7
View File
@@ -31,6 +31,8 @@
#define CURRENT_MOD_VER 4 #define CURRENT_MOD_VER 4
struct timers_oneshot;
/* Screen used for ExtendedDesktopSize / Set DesktopSize */ /* Screen used for ExtendedDesktopSize / Set DesktopSize */
struct vnc_screen struct vnc_screen
{ {
@@ -188,6 +190,11 @@ struct vnc
struct vnc_screen_layout server_layout; struct vnc_screen_layout server_layout;
enum vnc_resize_status resize_status; enum vnc_resize_status resize_status;
enum vnc_resize_support_status resize_supported; enum vnc_resize_support_status resize_supported;
/* forwarded resize */
// This occurs when the VNC server forwards a resize request
// elsewhere (RFB_EDS_REQUEST_FORWARDED)
struct timers_oneshot *forward_timer;
struct vnc_screen_layout forwarded_layout;
}; };
/* /*