Skip to content

Commit 971bb63

Browse files
committed
Add support for forwarded resize request to VNC module
1 parent ee79b2d commit 971bb63

File tree

4 files changed

+184
-30
lines changed

4 files changed

+184
-30
lines changed

vnc/rfb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static const char *eds_status_msg[] =
3131
/* 1 */ "Resize is administratively prohibited",
3232
/* 2 */ "Out of resources",
3333
/* 3 */ "Invalid screen layout",
34+
/* 4 */ "Request forwarded",
3435
/* others */ "Unknown code"
3536
};
3637

vnc/rfb.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ enum sec_type
8080
#define RFBPROTO_VER_3_7 MAKE_RFBPROTO_VER(3,7)
8181
#define RFBPROTO_VER_3_8 MAKE_RFBPROTO_VER(3,8)
8282

83+
/*
84+
* ExtendedDesktopSize status codes
85+
*/
86+
enum
87+
{
88+
RFB_EDS_NO_ERROR = 0,
89+
RFB_EDS_ADMINISTRATIVELY_PROHIBITED = 1,
90+
RFB_EDS_OUT_OF_RESOURCES = 2,
91+
RFB_EDS_INVALID_SCREEN_LAYOUT = 3,
92+
RFB_EDS_REQUEST_FORWARDED = 4
93+
};
94+
8395
/**
8496
* Returns an error string for an ExtendedDesktopSize status code
8597
*/

vnc/vnc.c

Lines changed: 164 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "vnc_clip.h"
3939
#include "rfb.h"
4040
#include "log.h"
41+
#include "timers.h"
4142
#include "trans.h"
4243
#include "ssl_calls.h"
4344
#include "string_calls.h"
@@ -53,6 +54,12 @@ enum
5354
MSK_EXTENDED_DESKTOP_SIZE = (1 << 0)
5455
};
5556

57+
enum
58+
{
59+
/** Time to wait for a forwarded resize to complete */
60+
FORWARDED_RESIZE_TIMEOUT = 1500 /* milli-seconds */
61+
};
62+
5663
/******************************************************************************/
5764
int
5865
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)
309316
out_uint32_be(s, layout->s[i].flags);
310317
}
311318
s_mark_end(s);
312-
LOG(LOG_LEVEL_DEBUG, "VNC Sending SetDesktopSize");
319+
LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE: Sending SetDesktopSize %dx%d",
320+
layout->total_width, layout->total_height);
313321
error = lib_send_copy(v, s);
314322
free_stream(s);
315323

@@ -954,7 +962,7 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
954962
{
955963
struct vnc_screen_layout layout = {0};
956964
LOG(LOG_LEVEL_DEBUG,
957-
"Skipping RFB_ENC_EXTENDED_DESKTOP_SIZE encoding "
965+
"VNC_RESIZE: Skipping RFB_ENC_EXTENDED_DESKTOP_SIZE encoding "
958966
"x=%d, y=%d geom=%dx%d",
959967
x, y, cx, cy);
960968
error = read_extended_desktop_size_rect(v, &layout);
@@ -1033,7 +1041,7 @@ find_matching_extended_rect(struct vnc *v,
10331041
match(x, y, cx, cy))
10341042
{
10351043
LOG(LOG_LEVEL_DEBUG,
1036-
"VNC matched ExtendedDesktopSize rectangle "
1044+
"VNC_RESIZE: VNC matched ExtendedDesktopSize rectangle "
10371045
"x=%d, y=%d geom=%dx%d",
10381046
x, y, cx, cy);
10391047
found = 1;
@@ -1149,6 +1157,18 @@ rect_is_reply_to_us(int x, int y, int cx, int cy)
11491157
return (x == 1);
11501158
}
11511159

1160+
/**************************************************************************//**
1161+
* Tests if extended desktop size rect is a general change.
1162+
*
1163+
* This happens when we are looking for a layout change that the
1164+
* VNC server has reported as forwarded to the real desktop.
1165+
*/
1166+
static int
1167+
rect_is_general_change(int x, int y, int cx, int cy)
1168+
{
1169+
return (x == 0);
1170+
}
1171+
11521172
/**************************************************************************//**
11531173
* Handles the first framebuffer update from the server
11541174
*
@@ -1249,45 +1269,136 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
12491269
{
12501270
if (layout.count > 0)
12511271
{
1252-
if (response_code == 0)
1272+
if (response_code == RFB_EDS_REQUEST_FORWARDED)
12531273
{
1254-
LOG(LOG_LEVEL_DEBUG, "VNC server successfully resized");
1255-
log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout);
1256-
v->server_layout = layout;
1274+
LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE: VNC server resize forwarded");
1275+
log_screen_layout(LOG_LEVEL_INFO, "ForwardedLayout", &layout);
1276+
v->forward_timer =
1277+
timers_oneshot_init(FORWARDED_RESIZE_TIMEOUT);
1278+
v->forwarded_layout = layout;
12571279
}
12581280
else
12591281
{
1260-
LOG(LOG_LEVEL_WARNING,
1261-
"VNC server resize failed - error code %d [%s]",
1262-
response_code,
1263-
rfb_get_eds_status_msg(response_code));
1264-
// This is awkward. The client has asked for a specific size
1265-
// which we can't support.
1266-
//
1267-
// Currently we handle this by queueing a resize to our
1268-
// supported size, and continuing with the resize state
1269-
// machine in xrdp_mm.c
1270-
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
1271-
error = resize_client_to_server(v, 0);
1282+
// The resize has either succeeded or failed
1283+
if (response_code == RFB_EDS_NO_ERROR)
1284+
{
1285+
LOG(LOG_LEVEL_DEBUG, "VNC_RESIZE:"
1286+
" VNC server successfully resized");
1287+
log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout);
1288+
v->server_layout = layout;
1289+
}
1290+
else
1291+
{
1292+
LOG(LOG_LEVEL_WARNING,
1293+
"VNC server resize failed - error code %d [%s]",
1294+
response_code,
1295+
rfb_get_eds_status_msg(response_code));
1296+
// This is awkward. The client has asked for a
1297+
// specific size which we can't support.
1298+
//
1299+
// Currently we handle this by queueing a resize
1300+
// to our supported size, and continuing with the
1301+
// resize state machine in xrdp_mm.c
1302+
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
1303+
error = resize_client_to_server(v, 0);
1304+
}
1305+
1306+
v->resize_status = VRS_DONE;
1307+
if (error == 0)
1308+
{
1309+
// If this resize was requested by the client mid-session
1310+
// (dynamic resize), we need to tell xrdp_mm that
1311+
// it's OK to continue with the resize state machine.
1312+
error = v->server_monitor_resize_done(v);
1313+
if (error == 0)
1314+
{
1315+
error = send_update_request_for_resize_status(v);
1316+
}
1317+
}
12721318
}
1319+
}
1320+
}
12731321

1274-
if (error == 0)
1322+
1323+
return error;
1324+
}
1325+
1326+
/**************************************************************************//**
1327+
* Looks for the forwarded screen layout in a framebuffer update request
1328+
*
1329+
* Looks for an ExtendedDesktopSize rectangle following a notification
1330+
* from the VNC server that the request has been forwarded to the real
1331+
* desktop. See rfbproto/pfbproto#32 for more info.
1332+
*
1333+
* @param v VNC object
1334+
* @return != 0 for error
1335+
*/
1336+
static int
1337+
lib_framebuffer_look_for_forwarded_layout(struct vnc *v)
1338+
{
1339+
int error;
1340+
struct vnc_screen_layout layout = {0};
1341+
int x = 0;
1342+
int y = 0;
1343+
1344+
error = find_matching_extended_rect(v,
1345+
rect_is_general_change,
1346+
&x,
1347+
&y,
1348+
&layout);
1349+
if (error == 0)
1350+
{
1351+
if (layout.count > 0)
1352+
{
1353+
if (vnc_screen_layouts_equal(&layout, &v->forwarded_layout))
12751354
{
1276-
// If this resize was requested by the client mid-session
1277-
// (dynamic resize), we need to tell xrdp_mm that
1278-
// it's OK to continue with the resize state machine.
1355+
LOG(LOG_LEVEL_DEBUG,
1356+
"VNC_RESIZE: VNC server forwarded resize complete");
1357+
free(v->forward_timer);
1358+
v->forward_timer = NULL;
1359+
v->server_layout = layout;
12791360
error = v->server_monitor_resize_done(v);
1361+
v->resize_status = VRS_DONE;
12801362
}
1281-
v->resize_status = VRS_DONE;
1363+
else
1364+
{
1365+
LOG(LOG_LEVEL_DEBUG,
1366+
"VNC_RESIZE: Ignored ExtendedDesktopSize %dx%d x=%d y=%d",
1367+
layout.total_width, layout.total_height, x, y);
1368+
// Delay for a little before we send another request for
1369+
// the size
1370+
g_sleep(100);
1371+
}
1372+
1373+
error = send_update_request_for_resize_status(v);
12821374
}
12831375
}
12841376

1285-
if (error == 0)
1377+
return error;
1378+
}
1379+
1380+
/******************************************************************************/
1381+
/*
1382+
* The VNC server has not actioned a forwarded resize request
1383+
*/
1384+
static int
1385+
forward_timer_expired(struct vnc *v)
1386+
{
1387+
LOG(LOG_LEVEL_WARNING, "VNC server forwarded resize timed out");
1388+
LOG(LOG_LEVEL_DEBUG,
1389+
"VNC_RESIZE: VNC server forwarded resize timed out");
1390+
free(v->forward_timer);
1391+
v->forward_timer = NULL;
1392+
v->resize_status = VRS_DONE;
1393+
1394+
int rv = v->server_monitor_resize_done(v);
1395+
if (rv == 0)
12861396
{
1287-
error = send_update_request_for_resize_status(v);
1397+
LOG(LOG_LEVEL_WARNING, "Resizing client to server");
1398+
rv = resize_client_to_server(v, 0);
12881399
}
12891400

1290-
return error;
1401+
return rv;
12911402
}
12921403

12931404
/******************************************************************************/
@@ -1433,6 +1544,9 @@ lib_framebuffer_update(struct vnc *v)
14331544
layout.total_height = cy;
14341545
error = read_extended_desktop_size_rect(v, &layout);
14351546
/* If this is a reply to a request from us, x == 1 */
1547+
LOG(LOG_LEVEL_DEBUG,
1548+
"VNC_RESIZE: Read ExtendedDesktopSize %dx%d x=%d y=%d",
1549+
layout.total_width, layout.total_height, x, y);
14361550
if (error == 0 && x != 1)
14371551
{
14381552
if (!vnc_screen_layouts_equal(&v->server_layout, &layout))
@@ -1574,7 +1688,14 @@ lib_mod_process_message(struct vnc *v, struct stream *s)
15741688
break;
15751689

15761690
case VRS_WAITING_FOR_RESIZE_CONFIRM:
1577-
error = lib_framebuffer_waiting_for_resize_confirm(v);
1691+
if (v->forward_timer != NULL)
1692+
{
1693+
error = lib_framebuffer_look_for_forwarded_layout(v);
1694+
}
1695+
else
1696+
{
1697+
error = lib_framebuffer_waiting_for_resize_confirm(v);
1698+
}
15781699
break;
15791700

15801701
default:
@@ -2533,6 +2654,10 @@ lib_mod_get_wait_objs(struct vnc *v, tbus *read_objs, int *rcount,
25332654
trans_get_wait_objs_rw(v->trans, read_objs, rcount,
25342655
write_objs, wcount, timeout);
25352656
}
2657+
2658+
// Update timeout with any active timers
2659+
unsigned int now = g_get_elapsed_ms();
2660+
timers_oneshot_update_poll(v->forward_timer, now, timeout);
25362661
}
25372662

25382663
return 0;
@@ -2550,13 +2675,22 @@ lib_mod_check_wait_objs(struct vnc *v)
25502675
{
25512676
if (v->trans != 0)
25522677
{
2553-
rv = trans_check_wait_objs(v->trans);
2554-
if (rv != 0)
2678+
if ((rv = trans_check_wait_objs(v->trans)) != 0)
25552679
{
25562680
LOG(LOG_LEVEL_ERROR, "VNC server closed connection");
25572681
}
2682+
else
2683+
{
2684+
// Check timers
2685+
unsigned int now = g_get_elapsed_ms();
2686+
if (timers_oneshot_get_remaining(v->forward_timer, now) == 0)
2687+
{
2688+
rv = forward_timer_expired(v);
2689+
}
2690+
}
25582691
}
25592692
}
2693+
25602694
return rv;
25612695
}
25622696

vnc/vnc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
#define CURRENT_MOD_VER 4
3333

34+
struct timers_oneshot;
35+
3436
/* Screen used for ExtendedDesktopSize / Set DesktopSize */
3537
struct vnc_screen
3638
{
@@ -188,6 +190,11 @@ struct vnc
188190
struct vnc_screen_layout server_layout;
189191
enum vnc_resize_status resize_status;
190192
enum vnc_resize_support_status resize_supported;
193+
/* forwarded resize */
194+
// This occurs when the VNC server forwards a resize request
195+
// elsewhere (RFB_EDS_REQUEST_FORWARDED)
196+
struct timers_oneshot *forward_timer;
197+
struct vnc_screen_layout forwarded_layout;
191198
};
192199

193200
/*

0 commit comments

Comments
 (0)