diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index a29499339..d5b54e193 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -51,6 +51,8 @@ #include #endif +#define WITH_AUTORECONNECT + #include #include #include @@ -1336,6 +1338,51 @@ void* xf_channels_thread(void* arg) return NULL; } +#ifdef WITH_AUTORECONNECT +BOOL xf_auto_reconnect(freerdp* instance) +{ + xfContext* xfc = (xfContext*)instance->context; + + UINT32 num_retries = 0; + UINT32 max_retries = instance->settings->AutoReconnectMaxRetries; + + /* Only auto reconnect on network disconnects. */ + if (freerdp_error_info(instance) != 0) return FALSE; + + /* A network disconnect was detected */ + fprintf(stderr, "Network disconnect!\n"); + if (!instance->settings->AutoReconnectionEnabled) + { + /* No auto-reconnect - just quit */ + return FALSE; + } + + /* Perform an auto-reconnect. */ + for (;;) + { + /* Quit retrying if max retries has been exceeded */ + if (num_retries++ >= max_retries) + { + return FALSE; + } + + /* Attempt the next reconnect */ + fprintf(stderr, "Attempting reconnect (%u of %u)\n", num_retries, max_retries); + if (freerdp_reconnect(instance)) + { + xfc->disconnect = FALSE; + return TRUE; + } + + sleep(5); + } + + fprintf(stderr, "Maximum reconnect retries exceeded\n"); + + return FALSE; +} +#endif + /** Main loop for the rdp connection. * It will be run from the thread's entry point (thread_func()). * It initiates the connection, and will continue to run until the session ends, @@ -1382,6 +1429,11 @@ void* xf_thread(void* param) ZeroMemory(wfds, sizeof(wfds)); ZeroMemory(&timeout, sizeof(struct timeval)); +#ifdef WITH_AUTORECONNECT + instance->settings->AutoReconnectionEnabled = TRUE; + instance->settings->AutoReconnectMaxRetries = 20; +#endif + status = freerdp_connect(instance); xfc = (xfContext*) instance->context; @@ -1526,6 +1578,9 @@ void* xf_thread(void* param) { if (freerdp_check_fds(instance) != TRUE) { +#ifdef WITH_AUTORECONNECT + if (xf_auto_reconnect(instance)) continue; +#endif fprintf(stderr, "Failed to check FreeRDP file descriptor\n"); break; } diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index a7a4182f0..b1a461986 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -221,6 +221,7 @@ FREERDP_API void freerdp_context_free(freerdp* instance); FREERDP_API BOOL freerdp_connect(freerdp* instance); FREERDP_API BOOL freerdp_shall_disconnect(freerdp* instance); FREERDP_API BOOL freerdp_disconnect(freerdp* instance); +FREERDP_API BOOL freerdp_reconnect(freerdp* instance); FREERDP_API BOOL freerdp_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount); FREERDP_API BOOL freerdp_check_fds(freerdp* instance); diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index e84bb8e29..d170a5479 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -336,6 +336,33 @@ BOOL rdp_client_redirect(rdpRdp* rdp) return status; } +BOOL rdp_client_reconnect(rdpRdp* rdp) +{ + int i; + + transport_disconnect(rdp->transport); + + mcs_free(rdp->mcs); + nego_free(rdp->nego); + license_free(rdp->license); + transport_free(rdp->transport); + + /* Reset virtual channel status */ + for (i = 0; i < rdp->settings->ChannelCount; i++) + { + rdp->settings->ChannelDefArray[i].joined = FALSE; + } + + rdp->transport = transport_new(rdp->settings); + rdp->license = license_new(rdp); + rdp->nego = nego_new(rdp->transport); + rdp->mcs = mcs_new(rdp->transport); + + rdp->transport->layer = TRANSPORT_LAYER_TCP; + + return rdp_client_connect(rdp); +} + static BYTE fips_ivec[8] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; static BOOL rdp_client_establish_keys(rdpRdp* rdp) diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 828e79227..65708ae75 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -49,6 +49,7 @@ enum CONNECTION_STATE BOOL rdp_client_connect(rdpRdp* rdp); BOOL rdp_client_redirect(rdpRdp* rdp); +BOOL rdp_client_reconnect(rdpRdp* rdp); BOOL rdp_client_connect_mcs_connect_response(rdpRdp* rdp, wStream* s); BOOL rdp_client_connect_mcs_attach_user_confirm(rdpRdp* rdp, wStream* s); BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s); diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 57ea44526..7f075f49d 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -322,6 +322,11 @@ BOOL freerdp_disconnect(freerdp* instance) return TRUE; } +BOOL freerdp_reconnect(freerdp* instance) +{ + return rdp_client_reconnect(instance->context->rdp); +} + BOOL freerdp_shall_disconnect(freerdp* instance) { return instance->context->rdp->disconnect; diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index 34cf4cc35..e4902e6e6 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -41,6 +41,9 @@ #include #ifdef __APPLE__ +#ifndef SOL_TCP +#define SOL_TCP IPPROTO_TCP +#endif #ifndef TCP_KEEPIDLE #define TCP_KEEPIDLE TCP_KEEPALIVE #endif @@ -251,7 +254,29 @@ BOOL tcp_set_keep_alive_mode(rdpTcp* tcp) if (setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &option_value, option_len) < 0) { - perror("setsockopt() IPPROTO_TCP, SO_KEEPIDLE:"); + perror("setsockopt() IPPROTO_TCP, TCP_KEEPIDLE:"); + return FALSE; + } +#endif + +#ifdef TCP_KEEPCNT + option_value = 3; + option_len = sizeof(option_value); + + if (setsockopt(tcp->sockfd, SOL_TCP, TCP_KEEPCNT, (void *) &option_value, option_len) < 0) + { + perror("setsockopt() SOL_TCP, TCP_KEEPCNT:"); + return FALSE; + } +#endif + +#ifdef TCP_KEEPINTVL + option_value = 2; + option_len = sizeof(option_value); + + if (setsockopt(tcp->sockfd, SOL_TCP, TCP_KEEPINTVL, (void *) &option_value, option_len) < 0) + { + perror("setsockopt() SOL_TCP, TCP_KEEPINTVL:"); return FALSE; } #endif diff --git a/libfreerdp/utils/signal.c b/libfreerdp/utils/signal.c index 82cdec9ad..1fd62a28e 100644 --- a/libfreerdp/utils/signal.c +++ b/libfreerdp/utils/signal.c @@ -49,6 +49,8 @@ static void fatal_handler(int signum) struct sigaction default_sigaction; sigset_t this_mask; + printf("fatal_handler: signum=%d\n", signum); + if (terminal_needs_reset) tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags); @@ -74,7 +76,6 @@ const int fatal_signals[] = SIGILL, SIGINT, SIGKILL, - SIGPIPE, SIGQUIT, SIGSEGV, SIGSTOP, @@ -128,6 +129,9 @@ int freerdp_handle_signals(void) pthread_sigmask(SIG_SETMASK, &orig_set, NULL); + /* Ignore SIGPIPE signal. */ + signal(SIGPIPE, SIG_IGN); + return 0; }