From e5ce6ac29a8bf7445a730127bafde65092707046 Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 7 Feb 2016 12:48:01 +0100 Subject: [PATCH 1/7] New RDP autoreconnect feature, should fix #723 --- remmina-plugins/rdp/rdp_event.c | 82 ++++++++++++++---- remmina-plugins/rdp/rdp_plugin.c | 108 ++++++++++++++++++++++++ remmina-plugins/rdp/rdp_plugin.h | 4 + remmina/po/it.po | 3 + remmina/src/remmina_connection_window.c | 3 + 5 files changed, 182 insertions(+), 18 deletions(-) diff --git a/remmina-plugins/rdp/rdp_event.c b/remmina-plugins/rdp/rdp_event.c index 24fcf1c6d1..bac2b0f3f9 100644 --- a/remmina-plugins/rdp/rdp_event.c +++ b/remmina-plugins/rdp/rdp_event.c @@ -53,7 +53,7 @@ static void remmina_rdp_event_on_focus_in(GtkWidget* widget, GdkEventKey* event, GdkDeviceManager *manager; GdkDevice *keyboard = NULL; - if ( !rfi ) + if (!rfi || !rfi->connected || rfi->is_reconnecting) return; input = rfi->instance->input; @@ -86,7 +86,9 @@ static void remmina_rdp_event_event_push(RemminaProtocolWidget* gp, const Remmin rfContext* rfi = GET_PLUGIN_DATA(gp); RemminaPluginRdpEvent* event; - if ( !rfi ) + /* Here we push mouse/keyboard events in the event_queue */ + + if (!rfi || !rfi->connected || rfi->is_reconnecting) return; if (rfi->event_queue) @@ -149,7 +151,7 @@ static void remmina_rdp_event_scale_area(RemminaProtocolWidget* gp, gint* x, gin gint sx, sy, sw, sh; rfContext* rfi = GET_PLUGIN_DATA(gp); - if (!rfi->surface) + if (!rfi || !rfi->connected || rfi->is_reconnecting || !rfi->surface) return; width = remmina_plugin_service->protocol_plugin_get_width(gp); @@ -276,21 +278,48 @@ static gboolean remmina_rdp_event_on_draw(GtkWidget* widget, cairo_t* context, R TRACE_CALL("remmina_rdp_event_on_draw"); gboolean scale; rfContext* rfi = GET_PLUGIN_DATA(gp); + guint width, height; + gchar *msg; + cairo_text_extents_t extents; - if (!rfi) return FALSE; - - if (!rfi->surface) + if (!rfi || !rfi->connected) return FALSE; - scale = remmina_plugin_service->protocol_plugin_get_scale(gp); - if (scale) - cairo_scale(context, rfi->scale_x, rfi->scale_y); + if (rfi->is_reconnecting) + { + /* freerdp is reconnecting, just show a message to the user */ + + width = gtk_widget_get_allocated_width(widget); + height = gtk_widget_get_allocated_height (widget); + + /* Draw text */ + msg = g_strdup_printf(_("Reconnection in progress. Attempt %d of %d..."), + rfi->reconnect_nattempt, rfi->reconnect_maxattempts); + + cairo_set_source_rgb(context, 0.9, 0.9, 0.9); + cairo_text_extents(context, msg, &extents); + cairo_move_to(context, (width - (extents.width + extents.x_bearing)) / 2, (height - (extents.height + extents.y_bearing)) / 2); + cairo_show_text(context, msg); + + } + else + { + /* Standard drawing: we copy the surface from RDP */ + + if (!rfi->surface) + return FALSE; + + scale = remmina_plugin_service->protocol_plugin_get_scale(gp); + + if (scale) + cairo_scale(context, rfi->scale_x, rfi->scale_y); - cairo_set_source_surface(context, rfi->surface, 0, 0); + cairo_set_source_surface(context, rfi->surface, 0, 0); - cairo_set_operator (context, CAIRO_OPERATOR_SOURCE); // Ignore alpha channel from FreeRDP - cairo_paint(context); + cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); // Ignore alpha channel from FreeRDP + cairo_paint(context); + } return TRUE; } @@ -300,7 +329,8 @@ static gboolean remmina_rdp_event_on_configure(GtkWidget* widget, GdkEventConfig TRACE_CALL("remmina_rdp_event_on_configure"); rfContext* rfi = GET_PLUGIN_DATA(gp); - if (!rfi) return FALSE; + if (!rfi || !rfi->connected || rfi->is_reconnecting) + return FALSE; /* We do a delayed reallocating to improve performance */ @@ -322,7 +352,8 @@ static void remmina_rdp_event_translate_pos(RemminaProtocolWidget* gp, int ix, i * RDP coordinates and put result on (*ox,*uy) * */ - if (!rfi) return; + if (!rfi || !rfi->connected || rfi->is_reconnecting) + return; if ((rfi->scale) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) { @@ -346,7 +377,8 @@ static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidge * local window coordinates and put result on (*ox,*uy) * */ - if (!rfi) return; + if (!rfi || !rfi->connected || rfi->is_reconnecting) + return; if ((rfi->scale) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) { @@ -468,7 +500,8 @@ static gboolean remmina_rdp_event_on_key(GtkWidget* widget, GdkEventKey* event, RemminaPluginRdpEvent rdp_event; DWORD scancode; - if ( !rfi ) return TRUE; + if (!rfi || !rfi->connected || rfi->is_reconnecting) + return FALSE; #ifdef ENABLE_GTK_INSPECTOR_KEY /* GTK inspector key is propagated up. Disabled by default. @@ -542,7 +575,9 @@ gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *ev rfContext* rfi = GET_PLUGIN_DATA(gp); rfClipboard* clipboard; - if ( !rfi ) return TRUE; + if (!rfi || !rfi->connected || rfi->is_reconnecting) + return FALSE; + clipboard = &(rfi->clipboard); if ( clipboard->sync ) { @@ -564,7 +599,7 @@ void remmina_rdp_event_init(RemminaProtocolWidget* gp) rfContext* rfi = GET_PLUGIN_DATA(gp); GtkClipboard* clipboard; - if ( !rfi ) return; + if (!rfi) return; rfi->drawing_area = gtk_drawing_area_new(); gtk_widget_show(rfi->drawing_area); @@ -763,6 +798,13 @@ static void remmina_rdp_event_connected(RemminaProtocolWidget* gp, RemminaPlugin remmina_rdp_event_update_scale(gp); } +static void remmina_rdp_event_reconnect_progress(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui) +{ + TRACE_CALL("remmina_rdp_event_reconnect_progress"); + rfContext* rfi = GET_PLUGIN_DATA(gp); + gdk_window_invalidate_rect(gtk_widget_get_window(rfi->drawing_area), NULL, TRUE); +} + static BOOL remmina_rdp_event_create_cursor(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* ui) { TRACE_CALL("remmina_rdp_event_create_cursor"); @@ -897,6 +939,10 @@ gboolean remmina_rdp_event_queue_ui(RemminaProtocolWidget* gp) remmina_rdp_event_connected(gp, ui); break; + case REMMINA_RDP_UI_RECONNECT_PROGRESS: + remmina_rdp_event_reconnect_progress(gp, ui); + break; + case REMMINA_RDP_UI_CURSOR: remmina_rdp_event_cursor(gp, ui); break; diff --git a/remmina-plugins/rdp/rdp_plugin.c b/remmina-plugins/rdp/rdp_plugin.c index 5e926468f8..39919e965c 100644 --- a/remmina-plugins/rdp/rdp_plugin.c +++ b/remmina-plugins/rdp/rdp_plugin.c @@ -160,6 +160,76 @@ void rf_object_free(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* obj) g_free(obj); } +BOOL rf_auto_reconnect(rfContext* rfi) +{ + TRACE_CALL("rf_auto_reconnect"); + rdpSettings* settings = rfi->instance->settings; + RemminaPluginRdpUiObject* ui; + + rfi->is_reconnecting = TRUE; + rfi->reconnect_maxattempts = settings->AutoReconnectMaxRetries; + rfi->reconnect_nattempt = 0; + + /* Only auto reconnect on network disconnects. */ + if (freerdp_error_info(rfi->instance) != 0) + return FALSE; + + if (!settings->AutoReconnectionEnabled) + { + /* No auto-reconnect - just quit */ + return FALSE; + } + + /* A network disconnect was detected and we should try to reconnect */ + remmina_plugin_service->log_printf("[RDP][%s] network disconnection detected, initiating reconnection attempt\n", + rfi->settings->ServerHostname); + + ui = g_new0(RemminaPluginRdpUiObject, 1); + ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS; + rf_queue_ui(rfi->protocol_widget, ui); + + /* Sleep half a second to allow: + * - processing of the ui event we just pushed on the queue + * - better network conditions + * Remember: we hare on a thread, so the main gui won't lock */ + + usleep(500000); + + /* Perform an auto-reconnect. */ + while (TRUE) + { + /* Quit retrying if max retries has been exceeded */ + if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) + { + return FALSE; + } + + /* Attempt the next reconnect */ + remmina_plugin_service->log_printf("[RDP][%s] attempting reconnection, attempt #%d of %d\n", + rfi->settings->ServerHostname, rfi->reconnect_nattempt, rfi->reconnect_maxattempts); + + ui = g_new0(RemminaPluginRdpUiObject, 1); + ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS; + rf_queue_ui(rfi->protocol_widget, ui); + + if (freerdp_reconnect(rfi->instance)) + { + /* Reconnection is successful */ + remmina_plugin_service->log_printf("[RDP][%s] reconnection successful.\n", + rfi->settings->ServerHostname); + rfi->is_reconnecting = FALSE; + return TRUE; + } + + sleep(1); + } + + remmina_plugin_service->log_printf("[RDP][%s] maximum number of reconnection attempts exceeded.\n", + rfi->settings->ServerHostname); + + return FALSE; +} + BOOL rf_begin_paint(rdpContext* context) { TRACE_CALL("rf_begin_paint"); @@ -508,6 +578,8 @@ static void remmina_rdp_main_loop(RemminaProtocolWidget* gp) if (!freerdp_check_event_handles(rfi->instance->context)) { + if (rf_auto_reconnect(rfi)) + continue; fprintf(stderr, "Failed to check FreeRDP event handles\n"); break; } @@ -563,6 +635,34 @@ static void remmina_rdp_send_ctrlaltdel(RemminaProtocolWidget *gp) keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE); } +static gboolean remmina_rdp_check_host_resolution(RemminaProtocolWidget *gp, char *hostname, int tcpport) +{ + TRACE_CALL("remmina_rdp_check_host_resolution"); + struct addrinfo hints; + struct addrinfo* result = NULL; + int status; + char service[16]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + sprintf(service, "%d", tcpport); + status = getaddrinfo(hostname, service, &hints, &result); + if (status != 0) { + remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to find the address of RDP server %s."), + hostname); + freeaddrinfo(result); + return FALSE; + } + + freeaddrinfo(result); + return TRUE; + +} + static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) { TRACE_CALL("remmina_rdp_main"); @@ -595,6 +695,8 @@ static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) remmina_plugin_service->get_server_port(s, 3389, &host, &port); rfi->settings->ServerHostname = strdup(host); + rfi->settings->AutoReconnectionEnabled = TRUE; + cert_host = host; cert_port = port; @@ -603,6 +705,11 @@ static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) if ( cert_hostport ) { remmina_plugin_service->get_server_port(cert_hostport, 3389, &cert_host, &cert_port); } + } else { + /* When SSH tunnel is not enabled, we can verify that ServerHostname + * resolves correctly via DNS */ + if (!remmina_rdp_check_host_resolution(gp, host, port)) + return FALSE; } if (cert_port == 3389) @@ -987,6 +1094,7 @@ static void remmina_rdp_init(RemminaProtocolWidget* gp) rfi->settings = instance->settings; rfi->instance->context->channels = freerdp_channels_new(); rfi->connected = False; + rfi->is_reconnecting = False; pthread_mutex_init(&rfi->mutex, NULL); diff --git a/remmina-plugins/rdp/rdp_plugin.h b/remmina-plugins/rdp/rdp_plugin.h index 8084875940..1fd633bd7a 100644 --- a/remmina-plugins/rdp/rdp_plugin.h +++ b/remmina-plugins/rdp/rdp_plugin.h @@ -134,6 +134,9 @@ struct rf_context RFX_CONTEXT* rfx_context; gboolean connected; + gboolean is_reconnecting; + int reconnect_maxattempts; + int reconnect_nattempt; gboolean sw_gdi; GtkWidget* drawing_area; @@ -203,6 +206,7 @@ typedef enum { REMMINA_RDP_UI_UPDATE_REGION = 0, REMMINA_RDP_UI_CONNECTED, + REMMINA_RDP_UI_RECONNECT_PROGRESS, REMMINA_RDP_UI_CURSOR, REMMINA_RDP_UI_RFX, REMMINA_RDP_UI_NOCODEC, diff --git a/remmina/po/it.po b/remmina/po/it.po index 0ae2ebbc40..027ec51500 100644 --- a/remmina/po/it.po +++ b/remmina/po/it.po @@ -1606,3 +1606,6 @@ msgstr "Configura le battute" msgid "Listening connection on protocol TCP" msgstr "Connessione in ascolto su protocollo TCP" + +msgid "Unable to find the IP address of RDP server %s." +msgstr "Impossibile trovare l'indirizzo IP del server RDP %s." diff --git a/remmina/src/remmina_connection_window.c b/remmina/src/remmina_connection_window.c index dee1d62ed6..531e3799d9 100644 --- a/remmina/src/remmina_connection_window.c +++ b/remmina/src/remmina_connection_window.c @@ -214,6 +214,9 @@ static void remmina_connection_window_class_init(RemminaConnectionWindowClass* k " border:0;\n" " background-color: black;\n" "}\n" + "GtkDrawingArea {\n" + " background-color: black;\n" + "}\n" "GtkToolbar {\n" " -GtkWidget-window-dragging: 0;\n" "}\n" From 009d0d8a0c231fa72595811a3772d2a98b3a68ac Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 7 Feb 2016 17:47:14 +0100 Subject: [PATCH 2/7] RDP reconnection timing fix --- remmina-plugins/rdp/rdp_plugin.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/remmina-plugins/rdp/rdp_plugin.c b/remmina-plugins/rdp/rdp_plugin.c index 39919e965c..e7be8edd20 100644 --- a/remmina-plugins/rdp/rdp_plugin.c +++ b/remmina-plugins/rdp/rdp_plugin.c @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -165,6 +166,7 @@ BOOL rf_auto_reconnect(rfContext* rfi) TRACE_CALL("rf_auto_reconnect"); rdpSettings* settings = rfi->instance->settings; RemminaPluginRdpUiObject* ui; + time_t treconn; rfi->is_reconnecting = TRUE; rfi->reconnect_maxattempts = settings->AutoReconnectMaxRetries; @@ -212,6 +214,7 @@ BOOL rf_auto_reconnect(rfContext* rfi) ui->type = REMMINA_RDP_UI_RECONNECT_PROGRESS; rf_queue_ui(rfi->protocol_widget, ui); + treconn = time(NULL); if (freerdp_reconnect(rfi->instance)) { /* Reconnection is successful */ @@ -221,7 +224,9 @@ BOOL rf_auto_reconnect(rfContext* rfi) return TRUE; } - sleep(1); + /* Wait until 5 secs have elapsed from last reconnect attempt */ + while (time(NULL) - treconn < 5) + sleep(1); } remmina_plugin_service->log_printf("[RDP][%s] maximum number of reconnection attempts exceeded.\n", From 56dc6c729ae06f6f7bd9568d00bc7a3dab4263e4 Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 14 Feb 2016 13:28:37 +0100 Subject: [PATCH 3/7] SSH tunnel TCP Keepalive and TCP User Timeout. Improved ssh logging. --- CMakeLists.txt | 1 + config.h.in | 1 + remmina/src/remmina_ssh.c | 60 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c38ed5e0e..56b1d943d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ check_include_files(sys/param.h HAVE_SYS_PARAM_H) check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) check_include_files(arpa/inet.h HAVE_ARPA_INET_H) check_include_files(netinet/in.h HAVE_NETINET_IN_H) +check_include_files(netinet/tcp.h HAVE_NETINET_TCP_H) check_include_files(termios.h HAVE_TERMIOS_H) check_include_files(netdb.h HAVE_NETDB_H) check_include_files(fcntl.h HAVE_FCNTL_H) diff --git a/config.h.in b/config.h.in index e753644191..769295740e 100644 --- a/config.h.in +++ b/config.h.in @@ -5,6 +5,7 @@ #cmakedefine HAVE_SYS_SOCKET_H #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETINET_IN_H +#cmakedefine HAVE_NETINET_TCP_H #cmakedefine HAVE_TERMIOS_H #cmakedefine HAVE_NETDB_H #cmakedefine HAVE_FCNTL_H diff --git a/remmina/src/remmina_ssh.c b/remmina/src/remmina_ssh.c index fd0aa1ab72..22247a0b6e 100644 --- a/remmina/src/remmina_ssh.c +++ b/remmina/src/remmina_ssh.c @@ -77,6 +77,16 @@ #include "remmina_pref.h" #include "remmina/remmina_trace_calls.h" +#ifdef HAVE_NETINET_TCP_H +#include +#define SSH_SOCKET_TCP_KEEPIDLE 5 +#define SSH_SOCKET_TCP_KEEPCNT 3 +#define SSH_SOCKET_TCP_KEEPINTVL 3 +/* Remember to lower SSH_SOCKET_TCP_USER_TIMEOUT to 4 when kernel bug 108191 will be fixed */ +#define SSH_SOCKET_TCP_USER_TIMEOUT 9 +#endif + + /*-----------------------------------------------------------------------------* * SSH Base * *-----------------------------------------------------------------------------*/ @@ -401,22 +411,29 @@ remmina_ssh_init_session (RemminaSSH *ssh) { TRACE_CALL("remmina_ssh_init_session"); gint verbosity; +#ifdef HAVE_NETINET_TCP_H + socket_t sshsock; + gint optval; +#endif ssh->callback = g_new0 (struct ssh_callbacks_struct, 1); - ssh->callback->userdata = ssh; /* Init & startup the SSH session */ ssh->session = ssh_new (); ssh_options_set (ssh->session, SSH_OPTIONS_HOST, ssh->server); ssh_options_set (ssh->session, SSH_OPTIONS_PORT, &ssh->port); ssh_options_set (ssh->session, SSH_OPTIONS_USER, ssh->user); + + ssh_callbacks_init(ssh->callback); if (remmina_log_running ()) { verbosity = remmina_pref.ssh_loglevel; ssh_options_set (ssh->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh->callback->log_function = remmina_ssh_log_callback; + /* Reset libssh legacy userdata. This is a workaround for a libssh bug */ + ssh_set_log_userdata(ssh->session); } - ssh_callbacks_init (ssh->callback); + ssh->callback->userdata = ssh; ssh_set_callbacks(ssh->session, ssh->callback); /* As the latest parse the ~/.ssh/config file */ @@ -430,6 +447,34 @@ remmina_ssh_init_session (RemminaSSH *ssh) return FALSE; } +#ifdef HAVE_NETINET_TCP_H + /* Set keepalive on ssh socket, so we can keep firewalls awaken and detect + * when we loss the tunnel */ + sshsock = ssh_get_fd(ssh->session); + if (sshsock >= 0) { + optval = 1; + if(setsockopt (sshsock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof (optval)) < 0){ + remmina_log_printf ("[SSH] TCP KeepAlive not set\n"); + } + optval = SSH_SOCKET_TCP_KEEPIDLE; + if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof (optval)) < 0) { + remmina_log_printf ("[SSH] TCP_KEEPIDLE not set\n"); + } + optval = SSH_SOCKET_TCP_KEEPCNT; + if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof (optval)) < 0) { + remmina_log_printf ("[SSH] TCP_KEEPCNT not set\n"); + } + optval = SSH_SOCKET_TCP_KEEPINTVL; + if (setsockopt(sshsock, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof (optval)) < 0) { + remmina_log_printf ("[SSH] TCP_KEEPINTVL not set\n"); + } + optval = SSH_SOCKET_TCP_USER_TIMEOUT; + if (setsockopt(sshsock, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof (optval)) < 0) { + remmina_log_printf ("[SSH] TCP_USER_TIMEOUT not set\n"); + } + } +#endif + /* Try the "none" authentication */ if (ssh_userauth_none (ssh->session, NULL) == SSH_AUTH_SUCCESS) { @@ -1028,14 +1073,19 @@ remmina_ssh_tunnel_main_thread_proc (gpointer data) if (lenw <= 0) { disconnected = TRUE; + remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_write() returned an error: %s"); break; } } } - if (len == 0) disconnected = TRUE; + if (len == 0) { + remmina_ssh_set_error (REMMINA_SSH (tunnel), "read on tunnel listening socket returned an error: %s"); + disconnected = TRUE; + } } if (disconnected) { + remmina_log_printf("[SSH] tunnel has been disconnected. Reason: %s\n", REMMINA_SSH(tunnel)->error); remmina_ssh_tunnel_remove_channel (tunnel, i); continue; } @@ -1053,6 +1103,7 @@ remmina_ssh_tunnel_main_thread_proc (gpointer data) len = ssh_channel_poll (tunnel->channels[i], 0); if (len == SSH_ERROR || len == SSH_EOF) { + remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_poll() returned an error : %s"); disconnected = TRUE; } else if (len > 0) @@ -1061,6 +1112,7 @@ remmina_ssh_tunnel_main_thread_proc (gpointer data) len = ssh_channel_read_nonblocking (tunnel->channels[i], tunnel->socketbuffers[i]->data, len, 0); if (len <= 0) { + remmina_ssh_set_error (REMMINA_SSH (tunnel), "ssh_channel_read_nonblocking() returned an error : %s"); disconnected = TRUE; } else @@ -1085,6 +1137,7 @@ remmina_ssh_tunnel_main_thread_proc (gpointer data) } if (lenw <= 0) { + remmina_ssh_set_error (REMMINA_SSH (tunnel), "write on tunnel listening socket returned an error: %s"); disconnected = TRUE; break; } @@ -1098,6 +1151,7 @@ remmina_ssh_tunnel_main_thread_proc (gpointer data) if (disconnected) { + remmina_log_printf("[SSH] tunnel has been disconnected. Reason: %s\n", REMMINA_SSH(tunnel)->error); remmina_ssh_tunnel_remove_channel (tunnel, i); continue; } From 33b10abf0771a53124b16f67df0e6f2f75503e91 Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 14 Feb 2016 13:36:56 +0100 Subject: [PATCH 4/7] Attempt to have RDP reconnect working on SSH tunnel --- remmina-plugins/rdp/rdp_plugin.c | 206 ++++++++++++++++---------- remmina/src/remmina_init_dialog.c | 3 + remmina/src/remmina_protocol_widget.c | 9 +- 3 files changed, 136 insertions(+), 82 deletions(-) diff --git a/remmina-plugins/rdp/rdp_plugin.c b/remmina-plugins/rdp/rdp_plugin.c index e7be8edd20..a2cb821f8b 100644 --- a/remmina-plugins/rdp/rdp_plugin.c +++ b/remmina-plugins/rdp/rdp_plugin.c @@ -161,6 +161,109 @@ void rf_object_free(RemminaProtocolWidget* gp, RemminaPluginRdpUiObject* obj) g_free(obj); } +static gboolean remmina_rdp_check_host_resolution(RemminaProtocolWidget *gp, char *hostname, int tcpport) +{ + TRACE_CALL("remmina_rdp_check_host_resolution"); + struct addrinfo hints; + struct addrinfo* result = NULL; + int status; + char service[16]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + sprintf(service, "%d", tcpport); + status = getaddrinfo(hostname, service, &hints, &result); + if (status != 0) { + remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to find the address of RDP server %s."), + hostname); + freeaddrinfo(result); + return FALSE; + } + + freeaddrinfo(result); + return TRUE; + +} + +static gboolean remmina_rdp_tunnel_init(RemminaProtocolWidget* gp) +{ + TRACE_CALL("remmina_rdp_tunnel_init"); + + /* Opens the optional SSH tunnel if needed. + * Used also when reopening the same tunnel for a freerdp reconnect. + * Returns TRUE if all OK, and setups correct rfi->Settings values + * with connection and certificate parameters */ + + gchar* hostport; + gchar* s; + gchar* host; + gchar *cert_host; + gint cert_port; + gint port; + const gchar *cert_hostport; + + rfContext* rfi = GET_PLUGIN_DATA(gp); + RemminaFile* remminafile; + + remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + + hostport = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE); + if (hostport == NULL) { + return FALSE; + } + + remmina_plugin_service->get_server_port(hostport, 3389, &host, &port); + + cert_host = host; + cert_port = port; + + if (remmina_plugin_service->file_get_int(remminafile, "ssh_enabled", FALSE)) { + cert_hostport = remmina_plugin_service->file_get_string(remminafile, "server"); + if ( cert_hostport ) { + remmina_plugin_service->get_server_port(cert_hostport, 3389, &cert_host, &cert_port); + } + + } else { + /* When SSH tunnel is not enabled, we can verify that host + * resolves correctly via DNS */ + if (!remmina_rdp_check_host_resolution(gp, host, port)) { + g_free(host); + g_free(hostport); + return FALSE; + } + } + + if (!rfi->is_reconnecting) { + /* settings->CertificateName and settings->ServerHostname is created + * only on 1st connect, not on reconnections */ + + rfi->settings->ServerHostname = strdup(host); + + if (cert_port == 3389) + { + rfi->settings->CertificateName = strdup(cert_host); + } + else + { + s = g_strdup_printf("%s:%d", cert_host, cert_port); + rfi->settings->CertificateName = strdup(s); + g_free(s); + } + } + + if (cert_host != host) g_free(cert_host); + g_free(host); + g_free(hostport); + + rfi->settings->ServerPort = port; + + return TRUE; +} + BOOL rf_auto_reconnect(rfContext* rfi) { TRACE_CALL("rf_auto_reconnect"); @@ -173,12 +276,15 @@ BOOL rf_auto_reconnect(rfContext* rfi) rfi->reconnect_nattempt = 0; /* Only auto reconnect on network disconnects. */ - if (freerdp_error_info(rfi->instance) != 0) + if (freerdp_error_info(rfi->instance) != 0) { + rfi->is_reconnecting = FALSE; return FALSE; + } if (!settings->AutoReconnectionEnabled) { /* No auto-reconnect - just quit */ + rfi->is_reconnecting = FALSE; return FALSE; } @@ -203,7 +309,9 @@ BOOL rf_auto_reconnect(rfContext* rfi) /* Quit retrying if max retries has been exceeded */ if (rfi->reconnect_nattempt++ >= rfi->reconnect_maxattempts) { - return FALSE; + remmina_plugin_service->log_printf("[RDP][%s] maximum number of reconnection attempts exceeded.\n", + rfi->settings->ServerHostname); + break; } /* Attempt the next reconnect */ @@ -215,13 +323,20 @@ BOOL rf_auto_reconnect(rfContext* rfi) rf_queue_ui(rfi->protocol_widget, ui); treconn = time(NULL); - if (freerdp_reconnect(rfi->instance)) - { - /* Reconnection is successful */ - remmina_plugin_service->log_printf("[RDP][%s] reconnection successful.\n", + + /* Reconnect the SSH tunnel, if needed */ + if (!remmina_rdp_tunnel_init(rfi->protocol_widget)) { + remmina_plugin_service->log_printf("[RDP][%s] unable to recreate tunnel with remmina_rdp_tunnel_init.\n", rfi->settings->ServerHostname); - rfi->is_reconnecting = FALSE; - return TRUE; + } else { + if (freerdp_reconnect(rfi->instance)) + { + /* Reconnection is successful */ + remmina_plugin_service->log_printf("[RDP][%s] reconnection successful.\n", + rfi->settings->ServerHostname); + rfi->is_reconnecting = FALSE; + return TRUE; + } } /* Wait until 5 secs have elapsed from last reconnect attempt */ @@ -229,9 +344,7 @@ BOOL rf_auto_reconnect(rfContext* rfi) sleep(1); } - remmina_plugin_service->log_printf("[RDP][%s] maximum number of reconnection attempts exceeded.\n", - rfi->settings->ServerHostname); - + rfi->is_reconnecting = FALSE; return FALSE; } @@ -640,42 +753,11 @@ static void remmina_rdp_send_ctrlaltdel(RemminaProtocolWidget *gp) keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE); } -static gboolean remmina_rdp_check_host_resolution(RemminaProtocolWidget *gp, char *hostname, int tcpport) -{ - TRACE_CALL("remmina_rdp_check_host_resolution"); - struct addrinfo hints; - struct addrinfo* result = NULL; - int status; - char service[16]; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = 0; - hints.ai_protocol = 0; - - sprintf(service, "%d", tcpport); - status = getaddrinfo(hostname, service, &hints, &result); - if (status != 0) { - remmina_plugin_service->protocol_plugin_set_error(gp, _("Unable to find the address of RDP server %s."), - hostname); - freeaddrinfo(result); - return FALSE; - } - - freeaddrinfo(result); - return TRUE; - -} - static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) { TRACE_CALL("remmina_rdp_main"); gchar* s; - gint port; - gchar* host; gchar* value; - gchar* hostport; gint rdpsnd_rate; gint rdpsnd_channel; char *rdpsnd_params[3]; @@ -685,54 +767,16 @@ static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) const gchar* cs; RemminaFile* remminafile; rfContext* rfi = GET_PLUGIN_DATA(gp); - const gchar *cert_hostport; - gchar *cert_host; - gint cert_port; gchar *gateway_host; gint gateway_port; remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); - s = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, 3389, FALSE); - if (s == NULL) + if (!remmina_rdp_tunnel_init(gp)) return FALSE; - remmina_plugin_service->get_server_port(s, 3389, &host, &port); - rfi->settings->ServerHostname = strdup(host); - rfi->settings->AutoReconnectionEnabled = TRUE; - cert_host = host; - cert_port = port; - - if (remmina_plugin_service->file_get_int(remminafile, "ssh_enabled", FALSE)) { - cert_hostport = remmina_plugin_service->file_get_string(remminafile, "server"); - if ( cert_hostport ) { - remmina_plugin_service->get_server_port(cert_hostport, 3389, &cert_host, &cert_port); - } - } else { - /* When SSH tunnel is not enabled, we can verify that ServerHostname - * resolves correctly via DNS */ - if (!remmina_rdp_check_host_resolution(gp, host, port)) - return FALSE; - } - - if (cert_port == 3389) - { - rfi->settings->CertificateName = strdup(cert_host); - } - else - { - hostport = g_strdup_printf("%s:%d", cert_host, cert_port); - rfi->settings->CertificateName = strdup(hostport); - g_free(hostport); - } - - if (cert_host != host) g_free(cert_host); - g_free(host); - g_free(s); - - rfi->settings->ServerPort = port; rfi->settings->ColorDepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 0); if (rfi->settings->ColorDepth == 0) diff --git a/remmina/src/remmina_init_dialog.c b/remmina/src/remmina_init_dialog.c index 5387960f77..c823ba1885 100644 --- a/remmina/src/remmina_init_dialog.c +++ b/remmina/src/remmina_init_dialog.c @@ -167,6 +167,9 @@ void remmina_init_dialog_set_status(RemminaInitDialog *dialog, const gchar *stat va_list args; + if (!dialog) + return; + if (status_format) { if (dialog->status) diff --git a/remmina/src/remmina_protocol_widget.c b/remmina/src/remmina_protocol_widget.c index 3c4f20cdd3..32586b7924 100644 --- a/remmina/src/remmina_protocol_widget.c +++ b/remmina/src/remmina_protocol_widget.c @@ -645,6 +645,13 @@ gchar* remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget* gp, gi return dest; } + /* If we have a previous ssh tunnel, destroy it */ + if (gp->priv->ssh_tunnel) + { + remmina_ssh_tunnel_free(gp->priv->ssh_tunnel); + gp->priv->ssh_tunnel = NULL; + } + if (!remmina_protocol_widget_init_tunnel (gp)) { g_free(host); @@ -652,7 +659,7 @@ gchar* remmina_protocol_widget_start_direct_tunnel(RemminaProtocolWidget* gp, gi } remmina_init_dialog_set_status (REMMINA_INIT_DIALOG (gp->priv->init_dialog), - _("Connecting to %s through SSH tunnel..."), server); + _("Connecting to %s through SSH tunnel..."), server); if (remmina_file_get_int (gp->priv->remmina_file, "ssh_loopback", FALSE)) { From ff21144af729ad514fb6f715909756d11d200a65 Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 14 Feb 2016 14:04:16 +0100 Subject: [PATCH 5/7] Fix color of FTB handle area --- remmina/src/remmina_connection_window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/remmina/src/remmina_connection_window.c b/remmina/src/remmina_connection_window.c index 531e3799d9..071d594aa8 100644 --- a/remmina/src/remmina_connection_window.c +++ b/remmina/src/remmina_connection_window.c @@ -254,6 +254,9 @@ static void remmina_connection_window_class_init(RemminaConnectionWindowClass* k " padding: 0px;\n" " background-color: #f0f0f0;\n" "}\n" + "#ftb-handle {\n" + " background-color: #f0f0f0;\n" + "}\n" ,-1, NULL); @@ -2776,6 +2779,7 @@ static void remmina_connection_holder_create_overlay_ftb_overlay(RemminaConnecti GtkWidget* handle = gtk_drawing_area_new(); gtk_widget_set_size_request(handle, 4, 4); + gtk_widget_set_name(handle, "ftb-handle"); revealer = gtk_revealer_new(); From a621cec02068b8be924bb7f37464f57d83abf4c3 Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 28 Feb 2016 12:15:30 +0100 Subject: [PATCH 6/7] Make reconnection message bigger --- remmina-plugins/rdp/rdp_event.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/remmina-plugins/rdp/rdp_event.c b/remmina-plugins/rdp/rdp_event.c index bac2b0f3f9..19ccac46ce 100644 --- a/remmina-plugins/rdp/rdp_event.c +++ b/remmina-plugins/rdp/rdp_event.c @@ -297,6 +297,8 @@ static gboolean remmina_rdp_event_on_draw(GtkWidget* widget, cairo_t* context, R msg = g_strdup_printf(_("Reconnection in progress. Attempt %d of %d..."), rfi->reconnect_nattempt, rfi->reconnect_maxattempts); + cairo_select_font_face(context, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(context, 24); cairo_set_source_rgb(context, 0.9, 0.9, 0.9); cairo_text_extents(context, msg, &extents); cairo_move_to(context, (width - (extents.width + extents.x_bearing)) / 2, (height - (extents.height + extents.y_bearing)) / 2); From 4b8bbdfe7ca93303e4b1b246fa983275973e8d4d Mon Sep 17 00:00:00 2001 From: Giovanni Panozzo Date: Sun, 28 Feb 2016 13:57:22 +0100 Subject: [PATCH 7/7] Limit RDP autoreconnect to direct TCP connection (no ssh tun) --- remmina-plugins/rdp/rdp_plugin.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/remmina-plugins/rdp/rdp_plugin.c b/remmina-plugins/rdp/rdp_plugin.c index a2cb821f8b..35cc9c1d05 100644 --- a/remmina-plugins/rdp/rdp_plugin.c +++ b/remmina-plugins/rdp/rdp_plugin.c @@ -775,7 +775,10 @@ static gboolean remmina_rdp_main(RemminaProtocolWidget* gp) if (!remmina_rdp_tunnel_init(gp)) return FALSE; - rfi->settings->AutoReconnectionEnabled = TRUE; + /* Enable by default RDP AutoReconnection, only when ssh tunnel is not enabled */ + if (!remmina_plugin_service->file_get_int(remminafile, "ssh_enabled", FALSE)) { + rfi->settings->AutoReconnectionEnabled = TRUE; + } rfi->settings->ColorDepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 0);