From 6b65ed2e2120cde2e9995f78ad8ed9d774fec0dd Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 1 Dec 2014 20:10:06 +0100 Subject: [PATCH] Re-resolve when reconnecting CSTP and the X-CSTP-DynDNS is set by the server That is, when reconnecting CSTP due to peer tearing the connection down attempt to re-resolve its IP. That handles the case where the server is using dynamic DNS and is advertising it. Signed-off-by: Nikos Mavrogiannopoulos --- cstp.c | 18 ++++++++++++++++++ library.c | 3 +++ openconnect-internal.h | 4 ++++ ssl.c | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/cstp.c b/cstp.c index b1235ef..779f83d 100644 --- a/cstp.c +++ b/cstp.c @@ -378,6 +378,9 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) int cstpmtu = atol(colon); if (cstpmtu > mtu) mtu = cstpmtu; + } else if (!strcmp(buf + 7, "DynDNS")) { + if (!strcmp(colon, "true")) + vpninfo->is_dyndns = 1; } else if (!strcmp(buf + 7, "Address-IP6")) { vpninfo->ip_info.netmask6 = new_option->value; } else if (!strcmp(buf + 7, "Address")) { @@ -591,6 +594,16 @@ static int cstp_reconnect(struct openconnect_info *vpninfo) timeout = vpninfo->reconnect_timeout; interval = vpninfo->reconnect_interval; + /* handle cases with dynamic DNS by forcing a new resolve. + * The original IP is saved to retry as fallback if resolving + * fails. + */ + if (vpninfo->is_dyndns && vpninfo->first_peer_addr == NULL) { + vpninfo->first_peer_addr = vpninfo->peer_addr; + vpninfo->first_peer_addrlen = vpninfo->peer_addrlen; + vpninfo->peer_addr = NULL; + } + while ((ret = openconnect_make_cstp_connection(vpninfo))) { if (timeout <= 0) return ret; @@ -611,6 +624,11 @@ static int cstp_reconnect(struct openconnect_info *vpninfo) interval += vpninfo->reconnect_interval; if (interval > RECONNECT_INTERVAL_MAX) interval = RECONNECT_INTERVAL_MAX; + + if (vpninfo->is_dyndns && vpninfo->first_peer_addr != NULL) { + free(vpninfo->peer_addr); + vpninfo->peer_addr = NULL; + } } script_config_tun(vpninfo, "reconnect"); return 0; diff --git a/library.c b/library.c index f5d3dc9..7c8d5ec 100644 --- a/library.c +++ b/library.c @@ -178,6 +178,7 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo) CloseHandle(vpninfo->dtls_event); #endif free(vpninfo->peer_addr); + free(vpninfo->first_peer_addr); free_optlist(vpninfo->csd_env); free_optlist(vpninfo->script_env); free_optlist(vpninfo->cookies); @@ -291,6 +292,8 @@ int openconnect_set_hostname(struct openconnect_info *vpninfo, vpninfo->unique_hostname = NULL; free(vpninfo->peer_addr); vpninfo->peer_addr = NULL; + free(vpninfo->first_peer_addr); + vpninfo->first_peer_addr = NULL; return 0; } diff --git a/openconnect-internal.h b/openconnect-internal.h index 1bc79e5..6929a0e 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -424,6 +424,10 @@ struct openconnect_info { struct sockaddr *peer_addr; struct sockaddr *dtls_addr; + struct sockaddr *first_peer_addr; + socklen_t first_peer_addrlen; + unsigned is_dyndns; + int dtls_local_port; int deflate; diff --git a/ssl.c b/ssl.c index b50652d..e341871 100644 --- a/ssl.c +++ b/ssl.c @@ -110,6 +110,7 @@ int connect_https_socket(struct openconnect_info *vpninfo) { int ssl_sock = -1; int err; + unsigned retry_old_ip = 0; if (!vpninfo->port) vpninfo->port = 443; @@ -230,6 +231,8 @@ int connect_https_socket(struct openconnect_info *vpninfo) if (hints.ai_flags & AI_NUMERICHOST) free(hostname); ssl_sock = -EINVAL; + if (vpninfo->first_peer_addr != NULL) + retry_old_ip = 1; goto out; } if (hints.ai_flags & AI_NUMERICHOST) @@ -291,7 +294,10 @@ int connect_https_socket(struct openconnect_info *vpninfo) } freeaddrinfo(result); + if (ssl_sock < 0) { + if (vpninfo->first_peer_addr != NULL) + retry_old_ip = 1; vpn_progress(vpninfo, PRG_ERR, _("Failed to connect to host %s\n"), vpninfo->proxy?:vpninfo->hostname); @@ -314,6 +320,21 @@ int connect_https_socket(struct openconnect_info *vpninfo) } } out: + if (retry_old_ip != 0 && vpninfo->first_peer_addr != NULL) { + vpn_progress(vpninfo, PRG_ERR, + _("Retrying connection to host %s with original IP\n"), + vpninfo->proxy?:vpninfo->hostname); + + retry_old_ip = 0; + if (vpninfo->first_peer_addrlen > vpninfo->peer_addrlen || vpninfo->peer_addr == NULL) + realloc_inplace(vpninfo->peer_addr, vpninfo->first_peer_addrlen); + + if (vpninfo->peer_addr != NULL) { + memcpy(vpninfo->peer_addr, vpninfo->first_peer_addr, vpninfo->first_peer_addrlen); + goto reconnect; + } + } + /* If proxy processing returned -EAGAIN to reconnect before attempting further auth, and we failed to reconnect, we have to clean up here. */ cleanup_proxy_auth(vpninfo); -- 2.1.3