summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Mikaelsson <derfian@cendio.se>2018-03-23 09:38:17 +0100
committerGitHub <noreply@github.com>2018-03-23 09:38:17 +0100
commit8346fea6cc42e405aaad94d22c305949c201a288 (patch)
tree16717b0f7576125783332b6576c44f17ffb26e5c
parent4580492c1224f9c5d72f8863e8027563d58494ec (diff)
parentd6c99bf5998d0e2ed43ee4222bcf43bb66bb094d (diff)
downloadrdesktop-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.c73
-rw-r--r--tcp.c142
2 files changed, 149 insertions, 66 deletions
diff --git a/rdesktop.c b/rdesktop.c
index 522eaff..cb5e4b1 100644
--- a/rdesktop.c
+++ b/rdesktop.c
@@ -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();
diff --git a/tcp.c b/tcp.c
index 0cbb0ec..979feae 100644
--- a/tcp.c
+++ b/tcp.c
@@ -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;
}