From ae8c2ef67cce56477cee7bc8f6617d6380827497 Mon Sep 17 00:00:00 2001 From: GilbsFr24 Date: Fri, 2 Feb 2018 15:29:57 +0300 Subject: [PATCH 1/3] Get RDP protocol flags. --- constants.h | 9 +++++++++ iso.c | 35 ++++++++++++++++++++++------------- rdesktop.c | 2 ++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/constants.h b/constants.h index 512a35e6..56f1beb5 100644 --- a/constants.h +++ b/constants.h @@ -95,6 +95,15 @@ enum RDP_NEG_FAILURE_CODE SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 6 }; +enum RDP_NEG_REQ_FLAGS +{ + EXTENDED_CLIENT_DATA_SUPPORTED = 1, + DYNVC_GFX_PROTOCOL_SUPPORTED = 2, + NEGRSP_FLAG_RESERVED = 4, + RESTRICTED_ADMIN_MODE_SUPPORTED = 8, + REDIRECTED_AUTHENTICATION_MODE_SUPPORTED = 0x10 +}; + /* MCS PDU codes */ enum MCS_PDU_TYPE { diff --git a/iso.c b/iso.c index 278f32a2..635c0ed9 100644 --- a/iso.c +++ b/iso.c @@ -26,13 +26,12 @@ extern RD_BOOL g_encryption_initial; extern RDP_VERSION g_rdp_version; extern RD_BOOL g_use_password_as_pin; -static RD_BOOL g_negotiate_rdp_protocol = True; - extern char *g_sc_csp_name; extern char *g_sc_reader_name; extern char *g_sc_card_name; extern char *g_sc_container_name; +extern RD_BOOL g_extended_data_supported; /* Send a self-contained ISO PDU */ static void @@ -62,7 +61,7 @@ iso_send_connection_request(char *username, uint32 neg_proto) STREAM s; int length = 30 + strlen(username); - if (g_rdp_version >= RDP_V5 && g_negotiate_rdp_protocol) + if (g_rdp_version >= RDP_V5) length += 8; s = tcp_init(length); @@ -83,7 +82,7 @@ iso_send_connection_request(char *username, uint32 neg_proto) out_uint8(s, 0x0d); /* cookie termination string: CR+LF */ out_uint8(s, 0x0a); - if (g_rdp_version >= RDP_V5 && g_negotiate_rdp_protocol) + if (g_rdp_version >= RDP_V5) { /* optional RDP protocol negotiation request for RDPv5 */ out_uint8(s, RDP_NEG_REQ); @@ -223,7 +222,7 @@ iso_connect(char *server, char *username, char *domain, char *password, RD_BOOL is_fastpath; uint8 fastpath_hdr; - g_negotiate_rdp_protocol = True; + RD_BOOL ok_to_reconnect = False; neg_proto = PROTOCOL_SSL; @@ -242,7 +241,7 @@ iso_connect(char *server, char *username, char *domain, char *password, logger(Core, Verbose, "Connecting to server using SSL..."); retry: - *selected_protocol = PROTOCOL_RDP; + *selected_protocol = neg_proto; code = 0; if (!tcp_connect(server)) @@ -267,17 +266,16 @@ iso_connect(char *server, char *username, char *domain, char *password, const char *reason = NULL; uint8 type = 0; + uint8 flags = 0; uint32 data = 0; in_uint8(s, type); - in_uint8s(s, 1); /* skip flags */ + in_uint8(s, flags); /* skip flags */ in_uint8s(s, 2); /* skip length */ in_uint32(s, data); if (type == RDP_NEG_FAILURE) { - RD_BOOL retry_without_neg = False; - switch (data) { case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER: @@ -285,11 +283,11 @@ iso_connect(char *server, char *username, char *domain, char *password, break; case SSL_NOT_ALLOWED_BY_SERVER: reason = "SSL not allowed by server"; - retry_without_neg = True; + ok_to_reconnect = True; break; case SSL_CERT_NOT_ON_SERVER: reason = "no valid authentication certificate on server"; - retry_without_neg = True; + ok_to_reconnect = True; break; case INCONSISTENT_FLAGS: reason = "inconsistent negotiation flags"; @@ -306,7 +304,7 @@ iso_connect(char *server, char *username, char *domain, char *password, tcp_disconnect(); - if (retry_without_neg) + if (ok_to_reconnect) { if (reason != NULL) { @@ -316,7 +314,9 @@ iso_connect(char *server, char *username, char *domain, char *password, } logger(Core, Notice, "Retrying with plain RDP."); - g_negotiate_rdp_protocol = False; + + neg_proto = PROTOCOL_RDP; + goto retry; } @@ -332,6 +332,15 @@ iso_connect(char *server, char *username, char *domain, char *password, return False; } + if (flags & EXTENDED_CLIENT_DATA_SUPPORTED) { + g_extended_data_supported = True; + logger(Protocol, Debug, "Server supports Extended Client Data"); + } + else { + g_extended_data_supported = False; + logger(Protocol, Debug, "Server does not support Extended Client Data"); + } + /* handle negotiation response */ if (data == PROTOCOL_SSL) { diff --git a/rdesktop.c b/rdesktop.c index e633c21d..2d6eb0ae 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -144,6 +144,8 @@ RD_BOOL g_pending_resize = False; RD_BOOL g_pending_resize_defer = True; struct timeval g_pending_resize_defer_timer = {0}; +RD_BOOL g_extended_data_supported = False; + #ifdef WITH_RDPSND RD_BOOL g_rdpsnd = False; #endif From ac985439221954474d42c21b0108c266e21601b7 Mon Sep 17 00:00:00 2001 From: GilbsFr24 Date: Sun, 4 Feb 2018 15:00:03 +0300 Subject: [PATCH 2/3] Add basic MultiMonitor support --- configure.ac | 10 ++++ constants.h | 1 + iso.c | 6 +++ rdesktop.c | 4 ++ rdesktop.h | 2 + secure.c | 36 ++++++++++++++ types.h | 10 ++++ xwin.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 203 insertions(+) diff --git a/configure.ac b/configure.ac index 6c9fe41f..0095e0db 100644 --- a/configure.ac +++ b/configure.ac @@ -173,6 +173,16 @@ if test x"$HAVE_XRANDR" = "x1"; then AC_DEFINE(HAVE_XRANDR) fi +# xinerama +if test -n "$PKG_CONFIG"; then + PKG_CHECK_MODULES(XINERAMA, xinerama, [HAVE_XINERAMA=1], [HAVE_XINERAMA=0]) +fi +if test x"$HAVE_XINERAMA" = "x1"; then + CFLAGS="$CFLAGS $XINERAMA_CFLAGS" + LIBS="$LIBS $XINERAMA_LIBS" + AC_DEFINE(HAVE_XINERAMA) +fi + # Xcursor if test -n "$PKG_CONFIG"; then PKG_CHECK_MODULES(XCURSOR, xcursor, [HAVE_XCURSOR=1], [HAVE_XCURSOR=0]) diff --git a/constants.h b/constants.h index 56f1beb5..95857587 100644 --- a/constants.h +++ b/constants.h @@ -177,6 +177,7 @@ enum MCS_DPU_REASON #define CS_SECURITY 0xc002 #define CS_NET 0xc003 #define CS_CLUSTER 0xc004 +#define CS_MONITOR 0xc005 #define SEC_TAG_PUBKEY 0x0006 #define SEC_TAG_KEYSIG 0x0008 diff --git a/iso.c b/iso.c index 635c0ed9..c5a7c52b 100644 --- a/iso.c +++ b/iso.c @@ -31,6 +31,7 @@ extern char *g_sc_reader_name; extern char *g_sc_card_name; extern char *g_sc_container_name; +extern int g_num_monitors; extern RD_BOOL g_extended_data_supported; /* Send a self-contained ISO PDU */ @@ -341,6 +342,11 @@ iso_connect(char *server, char *username, char *domain, char *password, logger(Protocol, Debug, "Server does not support Extended Client Data"); } + if ((g_num_monitors > 1) && !g_extended_data_supported) { + logger(Protocol, Warning, "Got more than 1 monitor but server does not support Extended Client Data"); + g_num_monitors = 1; + } + /* handle negotiation response */ if (data == PROTOCOL_SSL) { diff --git a/rdesktop.c b/rdesktop.c index 2d6eb0ae..83f3c834 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -146,6 +146,10 @@ struct timeval g_pending_resize_defer_timer = {0}; RD_BOOL g_extended_data_supported = False; +/* According to MS-RDPBCGR 2.2.1.3.6 max value for monitorCount is 16 */ +int g_num_monitors = 0; +RDP_MONITOR g_monitors[MAX_MONITORS]; + #ifdef WITH_RDPSND RD_BOOL g_rdpsnd = False; #endif diff --git a/rdesktop.h b/rdesktop.h index 72983205..2ffe35e0 100644 --- a/rdesktop.h +++ b/rdesktop.h @@ -138,6 +138,8 @@ #define EXRD_WINDOW_CLOSED 62 #define EXRD_UNKNOWN 63 +#define MAX_MONITORS 16 + #define STRNCPY(dst,src,n) { strncpy(dst,src,n-1); dst[n-1] = 0; } #ifndef MIN diff --git a/secure.c b/secure.c index 6443c6e1..4dd97179 100644 --- a/secure.c +++ b/secure.c @@ -59,6 +59,11 @@ uint16 g_server_rdp_version = 0; static int g_sec_encrypt_use_count = 0; static int g_sec_decrypt_use_count = 0; +extern int g_num_monitors; +extern RDP_MONITOR g_monitors[MAX_MONITORS]; + +extern RD_BOOL g_extended_data_supported; + /* * I believe this is based on SSLv3 with the following differences: * MAC algorithm (5.2.3.1) uses only 32-bit length in place of seq_num/type/length fields @@ -407,6 +412,10 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol) if (g_num_channels > 0) length += g_num_channels * 12 + 8; + if ((g_num_monitors > 1) && g_extended_data_supported) { + length += (g_num_monitors * 20) + 12; + } + /* Generic Conference Control (T.124) ConferenceCreateRequest */ out_uint16_be(s, 5); out_uint16_be(s, 0x14); @@ -507,6 +516,33 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol) } } + /* 2.2.1.3.6 Client Monitor Data (TS_UD_CS_MONITOR) */ + if ((g_num_monitors > 1) && g_extended_data_supported) { + int lengthMonitors, n; + + logger(Protocol, Debug, "Informing server about %d monitors\n", g_num_monitors); + out_uint16_le(s, CS_MONITOR); /* User Data Header type */ + + lengthMonitors = 12 + (20 * g_num_monitors); + out_uint16_le(s, lengthMonitors); + out_uint32_le(s, 0); /* flags (unused) */ + out_uint32_le(s, g_num_monitors); /* monitorCount */ + for (n = 0; n < g_num_monitors; n++) { + out_uint32_le(s, g_monitors[n].x); /* left */ + out_uint32_le(s, g_monitors[n].y); /* top */ + out_uint32_le(s, g_monitors[n].x + + g_monitors[n].width-1); /* right */ + out_uint32_le(s, g_monitors[n].y + + g_monitors[n].height-1); /* bottom */ + out_uint32_le(s, g_monitors[n].is_primary ? 1 : 0); /* isPrimary */ + logger(Protocol, Debug, "Setting monitors %d %dx%d at %d,%d rect(l:%d,t:%d,r:%d,b:%d)\n", + n,g_monitors[n].width,g_monitors[n].height,g_monitors[n].x,g_monitors[n].y, + g_monitors[n].x, g_monitors[n].y, + g_monitors[n].x + g_monitors[n].width-1, g_monitors[n].y +g_monitors[n].height-1 + ); + } + } + s_mark_end(s); } diff --git a/types.h b/types.h index 922af8ac..ca1edb5c 100644 --- a/types.h +++ b/types.h @@ -306,6 +306,16 @@ typedef struct fileinfo } FILEINFO; +typedef struct rdp_monitor +{ + int x; + int y; + int width; + int height; + RD_BOOL is_primary; +} RDP_MONITOR; + + typedef RD_BOOL(*str_handle_lines_t) (const char *line, void *data); typedef enum { diff --git a/xwin.c b/xwin.c index 5d5b7c9a..49743b14 100644 --- a/xwin.c +++ b/xwin.c @@ -36,6 +36,9 @@ #ifdef HAVE_XRANDR #include #endif +#ifdef HAVE_XINERAMA +#include +#endif #ifdef __APPLE__ #include @@ -68,6 +71,10 @@ extern char g_seamless_spawn_cmd[]; extern int g_server_depth; extern int g_win_button_size; +extern int g_num_monitors; +extern RDP_MONITOR g_monitors[MAX_MONITORS]; + + /* This is a timer used to rate limit actual resizing */ static struct timeval g_resize_timer = {0}; @@ -1884,6 +1891,128 @@ set_wm_client_machine(Display * dpy, Window win) XSetWMClientMachine(dpy, win, &tp); } +#ifdef HAVE_XRANDR +static RD_BOOL +xwin_get_monitors_xrandr(void) +{ + int event_base, error_base; + XRRScreenResources *xrrr = NULL; + XRRCrtcInfo *xrrci = NULL; + int i, isPrimary; + + if (!XRRQueryExtension (g_display, &event_base, &error_base)) { + logger(GUI, Debug, "XRandR is not supported"); + return False; + } + + xrrr = XRRGetScreenResources(g_display, DefaultRootWindow(g_display)); + if (xrrr == NULL) { + logger(GUI, Warning, "XRRGetScreenResources failed"); + return False; + } + + g_num_monitors = xrrr->ncrtc; + logger(GUI, Debug, "Number of monitors = %d", g_num_monitors); + + memset(g_monitors, 0, sizeof(RDP_MONITOR) * g_num_monitors); + + isPrimary = 0; + + for (i = 0; i < g_num_monitors; ++i) { + xrrci = XRRGetCrtcInfo(g_display, xrrr, xrrr->crtcs[i]); + logger(GUI, Debug, "%dx%d+%d+%d", xrrci->width, xrrci->height, xrrci->x, xrrci->y); + g_monitors[i].x = xrrci->x; + g_monitors[i].y = xrrci->y; + g_monitors[i].width = xrrci->width; + g_monitors[i].width = (g_monitors[i].width + 3) & ~3; // make sure width is a multiple of 4 + g_monitors[i].height = xrrci->height; + g_monitors[i].is_primary = False; + + if ( (xrrci->x == 0) && (xrrci->y == 0)) + isPrimary = i; + + XRRFreeCrtcInfo(xrrci); + } + + g_monitors[isPrimary].is_primary = True; + + XRRFreeScreenResources(xrrr); + + return True; +} +#endif + +#ifdef HAVE_XINERAMA +static RD_BOOL +xwin_get_monitors_xinerama(void) +{ + int i, isPrimary; + int major_version, minor_version, ncrtc; + + if (!XineramaQueryExtension(g_display, &major_version, &minor_version)) { + logger(GUI, Debug, "Xinerama is not supported"); + return False; + } + if (!XineramaIsActive(g_display)) { + logger(GUI, Debug, "Xinerama is not active"); + return False; + } + + XineramaScreenInfo *p = XineramaQueryScreens(g_display, &ncrtc); + + if ((p == NULL) || (ncrtc <= 0)) { + logger(GUI, Debug, "XineramaQueryScreens failed to get screen info"); + return False; + } + + g_num_monitors = ncrtc; + + logger(GUI, Debug, "Number of monitors = %d", g_num_monitors); + + memset(g_monitors, 0, sizeof(RDP_MONITOR) * g_num_monitors); + + isPrimary = 0; + + for (i = 0; i < g_num_monitors; ++i) { + logger(GUI, Debug, "%dx%d+%d+%d", p[i].width, p[i].height, p[i].x_org, p[i].y_org); + g_monitors[i].x = p[i].x_org; + g_monitors[i].y = p[i].y_org; + g_monitors[i].width = p[i].width; + g_monitors[i].width = (g_monitors[i].width + 3) & ~3; // make sure width is a multiple of 4 + g_monitors[i].height = p[i].height; + g_monitors[i].is_primary = False; + + if ( (p[i].x_org == 0) && (p[i].y_org == 0)) + isPrimary = i; + } + + g_monitors[isPrimary].is_primary = True; + + XFree(p); + + return True; +} +#endif + +static RD_BOOL +xwin_get_monitors(void) +{ + RD_BOOL bMonitorFound = False; + +#ifdef HAVE_XRANDR + bMonitorFound = xwin_get_monitors_xrandr(); +#endif + +/* if XRandR is not avaible try Xinerama */ +#ifdef HAVE_XINERAMA + if(!bMonitorFound) + bMonitorFound = xwin_get_monitors_xinerama(); +#endif + + return bMonitorFound; +} + + /* Initialize the UI. This is done once per process. */ RD_BOOL ui_init(void) @@ -1984,11 +2113,14 @@ void ui_get_workarea_size(uint32 *width, uint32 *height) { uint32 x, y, w, h; + g_num_monitors = 1; + if (get_current_workarea(&x, &y, &w, &h) == 0) { *width = w; *height = h; g_using_full_workarea = True; + xwin_get_monitors(); } else { @@ -2331,6 +2463,7 @@ xwin_toggle_fullscreen(void) } g_fullscreen = !g_fullscreen; + g_num_monitors = 1; /* What size should the new window have? */ @@ -2341,6 +2474,7 @@ xwin_toggle_fullscreen(void) y = 0; width = WidthOfScreen(g_screen); height = HeightOfScreen(g_screen); + xwin_get_monitors(); } else { From d4c9f0b411145e1b3392f9f793d2a397789381ce Mon Sep 17 00:00:00 2001 From: Alexander Zakharov Date: Sun, 4 Feb 2018 16:39:50 +0300 Subject: [PATCH 3/3] Fix XRandR monitor detection Add initiail call to xwin_get_monitors() --- xwin.c | 63 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/xwin.c b/xwin.c index 49743b14..9f13b00c 100644 --- a/xwin.c +++ b/xwin.c @@ -1895,49 +1895,62 @@ set_wm_client_machine(Display * dpy, Window win) static RD_BOOL xwin_get_monitors_xrandr(void) { + int i, j; int event_base, error_base; - XRRScreenResources *xrrr = NULL; + XRRCrtcInfo *xrrci = NULL; - int i, isPrimary; + XRROutputInfo *info = NULL; + XRRScreenResources *xrrr = NULL; if (!XRRQueryExtension (g_display, &event_base, &error_base)) { - logger(GUI, Debug, "XRandR is not supported"); + logger(GUI, Debug, "%s: XRandR is not supported", __func__); return False; } xrrr = XRRGetScreenResources(g_display, DefaultRootWindow(g_display)); if (xrrr == NULL) { - logger(GUI, Warning, "XRRGetScreenResources failed"); + logger(GUI, Warning, "%s: XRRGetScreenResources failed", __func__); return False; } - g_num_monitors = xrrr->ncrtc; - logger(GUI, Debug, "Number of monitors = %d", g_num_monitors); + logger(GUI, Verbose, "%s: Number of outputs = %d", __func__, xrrr->noutput); - memset(g_monitors, 0, sizeof(RDP_MONITOR) * g_num_monitors); + j = 0; - isPrimary = 0; + for (i = 0; i < xrrr->noutput; i++) { + info = XRRGetOutputInfo(g_display, xrrr, xrrr->outputs[i]); - for (i = 0; i < g_num_monitors; ++i) { - xrrci = XRRGetCrtcInfo(g_display, xrrr, xrrr->crtcs[i]); - logger(GUI, Debug, "%dx%d+%d+%d", xrrci->width, xrrci->height, xrrci->x, xrrci->y); - g_monitors[i].x = xrrci->x; - g_monitors[i].y = xrrci->y; - g_monitors[i].width = xrrci->width; - g_monitors[i].width = (g_monitors[i].width + 3) & ~3; // make sure width is a multiple of 4 - g_monitors[i].height = xrrci->height; - g_monitors[i].is_primary = False; + if (info->connection != RR_Connected) { + XRRFreeOutputInfo(info); + continue; + } - if ( (xrrci->x == 0) && (xrrci->y == 0)) - isPrimary = i; + xrrci = XRRGetCrtcInfo(g_display, xrrr, info->crtc); + + logger(GUI, Verbose, "%d) %s: %dx%d+%d+%d", j, info->name, xrrci->width, xrrci->height, xrrci->x, xrrci->y); + + g_monitors[j].x = xrrci->x; + g_monitors[j].y = xrrci->y; + g_monitors[j].width = xrrci->width; + g_monitors[j].width = (g_monitors[j].width + 3) & ~3; // make sure width is a multiple of 4 + g_monitors[j].height = xrrci->height; + g_monitors[j].is_primary = False; + + if ((xrrci->x == 0) && (xrrci->y == 0)) + g_monitors[j].is_primary = True; + + j++; XRRFreeCrtcInfo(xrrci); + XRRFreeOutputInfo(info); } - g_monitors[isPrimary].is_primary = True; + logger(GUI, Verbose, "%s: Number of monitors = %d", __func__, j); XRRFreeScreenResources(xrrr); + g_num_monitors = j; + return True; } #endif @@ -1967,14 +1980,12 @@ xwin_get_monitors_xinerama(void) g_num_monitors = ncrtc; - logger(GUI, Debug, "Number of monitors = %d", g_num_monitors); - - memset(g_monitors, 0, sizeof(RDP_MONITOR) * g_num_monitors); + logger(GUI, Verbose, "Number of monitors = %d", g_num_monitors); isPrimary = 0; for (i = 0; i < g_num_monitors; ++i) { - logger(GUI, Debug, "%dx%d+%d+%d", p[i].width, p[i].height, p[i].x_org, p[i].y_org); + logger(GUI, Verbose, "%dx%d+%d+%d", p[i].width, p[i].height, p[i].x_org, p[i].y_org); g_monitors[i].x = p[i].x_org; g_monitors[i].y = p[i].y_org; g_monitors[i].width = p[i].width; @@ -1999,6 +2010,8 @@ xwin_get_monitors(void) { RD_BOOL bMonitorFound = False; + memset(g_monitors, 0, sizeof(RDP_MONITOR) * MAX_MONITORS); + #ifdef HAVE_XRANDR bMonitorFound = xwin_get_monitors_xrandr(); #endif @@ -2090,6 +2103,8 @@ ui_init(void) seamless_init(); } + xwin_get_monitors(); + return True; }