diff options
author | Karl Mikaelsson <derfian@cendio.se> | 2018-03-23 09:38:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-23 09:38:17 +0100 |
commit | 8346fea6cc42e405aaad94d22c305949c201a288 (patch) | |
tree | 16717b0f7576125783332b6576c44f17ffb26e5c | |
parent | 4580492c1224f9c5d72f8863e8027563d58494ec (diff) | |
parent | d6c99bf5998d0e2ed43ee4222bcf43bb66bb094d (diff) | |
download | rdesktop-8346fea6cc42e405aaad94d22c305949c201a288.zip |
Merge pull request #249 from derfian/reconnect-related-fixes
* When connecting to a Windows 2008 Server, pressing Cancel or hitting Escape while entering credentials (on the server) would lead to a reconnect if there was a resize pending.
* When connecting to a RDS farm name (round robin DNS entry as per MS recommendations), reconnecting could connect you to a different server, leading to a login screen rather than your session.
* rdesktop never had any logging that told you which address you were connecting to.
-rw-r--r-- | rdesktop.c | 73 | ||||
-rw-r--r-- | tcp.c | 142 |
2 files changed, 149 insertions, 66 deletions
@@ -1395,55 +1395,56 @@ main(int argc, char *argv[]) deactivated = False; g_reconnect_loop = False; - ext_disc_reason = 0; rdp_main_loop(&deactivated, &ext_disc_reason); tcp_run_ui(False); - - logger(Core, Verbose, "Disconnecting..."); rdp_disconnect(); - /* If error info is set we do want to exit rdesktop - connect loop. We do this by clearing flags that - triggers a reconnect that could be set elsewere */ - if (ext_disc_reason != 0) + if (deactivated) { - g_redirect = False; - g_network_error = False; - g_pending_resize = False; + /* Server disconnected while deactivated */ + logger(Core, Notice, "Disconnecting..."); + break; } - - if (g_redirect) - continue; - - /* handle network error and start autoreconnect */ - if (g_network_error && !deactivated) + else { - logger(Core, Notice, - "Disconnected due to network error, retrying to reconnect for %d minutes.", - RECONNECT_TIMEOUT / 60); - g_network_error = False; - g_reconnect_loop = True; - continue; - } - - ui_seamless_end(); - ui_destroy_window(); + /* Unexpected disconnect or rdesktop-initiated loop exit */ - /* Enter a reconnect loop if we have a pending resize request */ - if (g_pending_resize) - { - logger(Core, Verbose, "Resize reconnect loop triggered, new size %dx%d", - g_requested_session_width, g_requested_session_height); - g_pending_resize = False; - g_reconnect_loop = True; + if (g_user_quit) + { + /* User closed window */ + break; + } + else if (g_redirect) + { + /* see beginning of loop */ + } + else if (g_network_error) + { + logger(Core, Notice, + "Disconnected due to network error, retrying to reconnect for %d minutes.", + RECONNECT_TIMEOUT / 60); + g_network_error = False; + g_reconnect_loop = True; + } + else if (g_pending_resize) + { + /* Prepare to re-create rdesktop window */ + ui_seamless_end(); + ui_destroy_window(); + + logger(Core, Verbose, "Resize reconnect loop triggered, new size %dx%d", + g_requested_session_width, g_requested_session_height); + g_pending_resize = False; + g_reconnect_loop = True; + } continue; } - - /* exit main reconnect loop */ - break; } + ui_seamless_end(); + ui_destroy_window(); + cache_save_state(); ui_deinit(); @@ -58,6 +58,13 @@ #define STREAM_COUNT 1 #endif +#ifdef IPv6 +static struct addrinfo *g_server_address = NULL; +#else +struct sockaddr_in *g_server_address = NULL; +#endif + +static char *g_last_server_name = NULL; static RD_BOOL g_ssl_initialized = False; static SSL *g_ssl = NULL; static SSL_CTX *g_ssl_ctx = NULL; @@ -416,47 +423,85 @@ tcp_tls_get_server_pubkey(STREAM s) return (s->size != 0); } -/* Establish a connection on the TCP layer */ +/* Helper function to determine if rdesktop should resolve hostnames again or not */ +static RD_BOOL +tcp_connect_resolve_hostname(const char *server) +{ + return (g_server_address == NULL || + g_last_server_name == NULL || + strcmp(g_last_server_name, server) != 0); +} + +/* Establish a connection on the TCP layer + + This function tries to avoid resolving any server address twice. The + official Windows 2008 documentation states that the windows farm name + should be a round-robin DNS entry containing all the terminal servers + in the farm. When connected to the farm address, if we look up the + address again when reconnecting (for any reason) we risk reconnecting + to a different server in the farm. +*/ + RD_BOOL tcp_connect(char *server) { socklen_t option_len; uint32 option_value; int i; + char buf[NI_MAXHOST]; #ifdef IPv6 int n; - struct addrinfo hints, *res, *ressave; + struct addrinfo hints, *res, *addr; + struct sockaddr *oldaddr; char tcp_port_rdp_s[10]; - snprintf(tcp_port_rdp_s, 10, "%d", g_tcp_port_rdp); + if (tcp_connect_resolve_hostname(server)) + { + snprintf(tcp_port_rdp_s, 10, "%d", g_tcp_port_rdp); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; - if ((n = getaddrinfo(server, tcp_port_rdp_s, &hints, &res))) + if ((n = getaddrinfo(server, tcp_port_rdp_s, &hints, &res))) + { + logger(Core, Error, "tcp_connect(), getaddrinfo() failed: %s", gai_strerror(n)); + return False; + } + } + else { - logger(Core, Error, "tcp_connect(), getaddrinfo() failed: %s", gai_strerror(n)); - return False; + res = g_server_address; } - ressave = res; g_sock = -1; - while (res) + + for (addr = res; addr != NULL; addr = addr->ai_next) { - g_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (!(g_sock < 0)) + g_sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + if (g_sock < 0) + { + logger(Core, Debug, "tcp_connect(), socket() failed: %s", TCP_STRERROR); + continue; + } + + n = getnameinfo(addr->ai_addr, addr->ai_addrlen, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); + if (n != 0) { - if (connect(g_sock, res->ai_addr, res->ai_addrlen) == 0) - break; - TCP_CLOSE(g_sock); - g_sock = -1; + logger(Core, Error, "tcp_connect(), getnameinfo() failed: %s", gai_strerror(n)); + return False; } - res = res->ai_next; + + logger(Core, Debug, "tcp_connect(), trying %s (%s)", server, buf); + + if (connect(g_sock, addr->ai_addr, addr->ai_addrlen) == 0) + break; + + TCP_CLOSE(g_sock); + g_sock = -1; } - freeaddrinfo(ressave); if (g_sock == -1) { @@ -464,19 +509,50 @@ tcp_connect(char *server) return False; } -#else /* no IPv6 support */ + /* Save server address for later use, if we haven't already. */ - struct hostent *nslookup; - struct sockaddr_in servaddr; + if (g_server_address == NULL) + { + g_server_address = xmalloc(sizeof(struct addrinfo)); + g_server_address->ai_addr = xmalloc(sizeof(struct sockaddr_storage)); + } - if ((nslookup = gethostbyname(server)) != NULL) + if (g_server_address != addr) { - memcpy(&servaddr.sin_addr, nslookup->h_addr, sizeof(servaddr.sin_addr)); + /* don't overwrite ptr to allocated sockaddr */ + oldaddr = g_server_address->ai_addr; + memcpy(g_server_address, addr, sizeof(struct addrinfo)); + g_server_address->ai_addr = oldaddr; + + memcpy(g_server_address->ai_addr, addr->ai_addr, addr->ai_addrlen); + + g_server_address->ai_canonname = NULL; + g_server_address->ai_next = NULL; + + freeaddrinfo(res); } - else if ((servaddr.sin_addr.s_addr = inet_addr(server)) == INADDR_NONE) + +#else /* no IPv6 support */ + struct hostent *nslookup = NULL; + + if (tcp_connect_resolve_hostname(server)) { - logger(Core, Error, "tcp_connect(), unable to resolve host '%s'", server); - return False; + if (g_server_address != NULL) + xfree(g_server_address); + g_server_address = xmalloc(sizeof(struct sockaddr_in)); + g_server_address->sin_family = AF_INET; + g_server_address->sin_port = htons((uint16) g_tcp_port_rdp); + + if ((nslookup = gethostbyname(server)) != NULL) + { + memcpy(&g_server_address->sin_addr, nslookup->h_addr, + sizeof(g_server_address->sin_addr)); + } + else if ((g_server_address->sin_addr.s_addr = inet_addr(server)) == INADDR_NONE) + { + logger(Core, Error, "tcp_connect(), unable to resolve host '%s'", server); + return False; + } } if ((g_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) @@ -485,10 +561,12 @@ tcp_connect(char *server) return False; } - servaddr.sin_family = AF_INET; - servaddr.sin_port = htons((uint16) g_tcp_port_rdp); + logger(Core, Debug, "tcp_connect(), trying %s (%s)", + server, inet_ntop(g_server_address->sin_family, + &g_server_address->sin_addr, + buf, sizeof(buf))); - if (connect(g_sock, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) < 0) + if (connect(g_sock, (struct sockaddr *) g_server_address, sizeof(struct sockaddr)) < 0) { if (!g_reconnect_loop) logger(Core, Error, "tcp_connect(), connect() failed: %s", TCP_STRERROR); @@ -524,6 +602,10 @@ tcp_connect(char *server) g_out[i].data = (uint8 *) xmalloc(g_out[i].size); } + /* After successful connect: update the last server name */ + if (g_last_server_name) + xfree(g_last_server_name); + g_last_server_name = strdup(server); return True; } |