summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Mikaelsson <derfian@cendio.se>2018-03-13 16:27:14 +0100
committerPierre Ossman <ossman@cendio.se>2018-03-28 13:35:10 +0200
commit081eac429ceb26e0a370c244159324ab10c9b3e7 (patch)
treeb2295685ac6b965e1fecfadb0daeb54b5092c253
parent83aef969ab92a4f40559d058771689a030a9b2a6 (diff)
downloadrdesktop-081eac429ceb26e0a370c244159324ab10c9b3e7.zip
Save and re-use resolved address for given hostname
If tcp_connect is called with the same server name, don't look up the address again. This avoids connecting to other servers when using a round-robin RDS farm name, as recommended by Microsoft. This introduces a backwards-incompatible change. If rdesktop was reconnecting because the user was moving between networks and the server is no longer reachable on the same address, the user must re-start rdesktop to reach their server.
-rw-r--r--tcp.c142
1 files changed, 112 insertions, 30 deletions
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;
}