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.
This commit is contained in:
commit
8346fea6cc
75
rdesktop.c
75
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;
|
||||
/* Unexpected disconnect or rdesktop-initiated loop exit */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ui_seamless_end();
|
||||
ui_destroy_window();
|
||||
|
||||
/* 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;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* exit main reconnect loop */
|
||||
break;
|
||||
}
|
||||
|
||||
ui_seamless_end();
|
||||
ui_destroy_window();
|
||||
|
||||
cache_save_state();
|
||||
ui_deinit();
|
||||
|
||||
|
154
tcp.c
154
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);
|
||||
|
||||
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 (tcp_connect_resolve_hostname(server))
|
||||
{
|
||||
logger(Core, Error, "tcp_connect(), getaddrinfo() failed: %s", gai_strerror(n));
|
||||
return False;
|
||||
}
|
||||
snprintf(tcp_port_rdp_s, 10, "%d", g_tcp_port_rdp);
|
||||
|
||||
ressave = res;
|
||||
g_sock = -1;
|
||||
while (res)
|
||||
{
|
||||
g_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (!(g_sock < 0))
|
||||
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 (connect(g_sock, res->ai_addr, res->ai_addrlen) == 0)
|
||||
break;
|
||||
TCP_CLOSE(g_sock);
|
||||
g_sock = -1;
|
||||
logger(Core, Error, "tcp_connect(), getaddrinfo() failed: %s", gai_strerror(n));
|
||||
return False;
|
||||
}
|
||||
res = res->ai_next;
|
||||
}
|
||||
freeaddrinfo(ressave);
|
||||
else
|
||||
{
|
||||
res = g_server_address;
|
||||
}
|
||||
|
||||
g_sock = -1;
|
||||
|
||||
for (addr = res; addr != NULL; addr = addr->ai_next)
|
||||
{
|
||||
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)
|
||||
{
|
||||
logger(Core, Error, "tcp_connect(), getnameinfo() failed: %s", gai_strerror(n));
|
||||
return False;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 ((nslookup = gethostbyname(server)) != NULL)
|
||||
if (g_server_address == NULL)
|
||||
{
|
||||
memcpy(&servaddr.sin_addr, nslookup->h_addr, sizeof(servaddr.sin_addr));
|
||||
g_server_address = xmalloc(sizeof(struct addrinfo));
|
||||
g_server_address->ai_addr = xmalloc(sizeof(struct sockaddr_storage));
|
||||
}
|
||||
else if ((servaddr.sin_addr.s_addr = inet_addr(server)) == INADDR_NONE)
|
||||
|
||||
if (g_server_address != addr)
|
||||
{
|
||||
logger(Core, Error, "tcp_connect(), unable to resolve host '%s'", server);
|
||||
return False;
|
||||
/* 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 /* no IPv6 support */
|
||||
struct hostent *nslookup = NULL;
|
||||
|
||||
if (tcp_connect_resolve_hostname(server))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user