|
@@ -1,161 +1,143 @@
|
|
1
|
+From 2f55fec323730a94ed49d401d93b913d85e43b65 Mon Sep 17 00:00:00 2001
|
|
2
|
+From: Nikos Mavrogiannopoulos <nmav@gnutls.org>
|
|
3
|
+Date: Mon, 1 Dec 2014 20:10:06 +0100
|
|
4
|
+Subject: [PATCH 1/2] Re-resolve when reconnecting CSTP and the X-CSTP-DynDNS
|
|
5
|
+ is set by the server
|
|
6
|
+
|
|
7
|
+That is, when reconnecting CSTP due to peer tearing the connection
|
|
8
|
+down attempt to re-resolve its IP. That handles the case where
|
|
9
|
+the server is using dynamic DNS and is advertising it.
|
|
10
|
+
|
|
11
|
+[dwmw2: refactored to simplify it somewhat]
|
|
12
|
+
|
|
13
|
+Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
|
|
14
|
+Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
|
|
15
|
+---
|
|
16
|
+ cstp.c | 3 +++
|
|
17
|
+ openconnect-internal.h | 1 +
|
|
18
|
+ ssl.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
|
|
19
|
+ 4 files changed, 52 insertions(+), 2 deletions(-)
|
|
20
|
+
|
1
|
21
|
diff --git a/cstp.c b/cstp.c
|
2
|
|
-index b1235ef..f955b82 100644
|
|
22
|
+index 3b93538..55225f4 100644
|
3
|
23
|
--- a/cstp.c
|
4
|
24
|
+++ b/cstp.c
|
5
|
|
-@@ -570,7 +570,10 @@ int openconnect_make_cstp_connection(struct openconnect_info *vpninfo)
|
6
|
|
- return ret;
|
7
|
|
- }
|
8
|
|
-
|
9
|
|
--static int cstp_reconnect(struct openconnect_info *vpninfo)
|
10
|
|
-+/* When dead peer is set, this function will re-attempt resolving
|
11
|
|
-+ * the peer in case its IP changed.
|
12
|
|
-+ */
|
13
|
|
-+static int cstp_reconnect(struct openconnect_info *vpninfo, unsigned dead_peer)
|
14
|
|
- {
|
15
|
|
- int ret;
|
16
|
|
- int timeout;
|
17
|
|
-@@ -591,6 +594,16 @@ static int cstp_reconnect(struct openconnect_info *vpninfo)
|
18
|
|
- timeout = vpninfo->reconnect_timeout;
|
19
|
|
- interval = vpninfo->reconnect_interval;
|
20
|
|
-
|
21
|
|
-+ /* handle cases with dynamic DNS by forcing a new resolve.
|
22
|
|
-+ * The original IP is saved to retry as fallback if resolving
|
23
|
|
-+ * fails.
|
24
|
|
-+ */
|
25
|
|
-+ if (dead_peer && vpninfo->first_peer_addr == NULL) {
|
26
|
|
-+ vpninfo->first_peer_addr = vpninfo->peer_addr;
|
27
|
|
-+ vpninfo->first_peer_addrlen = vpninfo->peer_addrlen;
|
28
|
|
-+ vpninfo->peer_addr = NULL;
|
29
|
|
-+ }
|
30
|
|
-+
|
31
|
|
- while ((ret = openconnect_make_cstp_connection(vpninfo))) {
|
32
|
|
- if (timeout <= 0)
|
33
|
|
- return ret;
|
34
|
|
-@@ -611,6 +624,11 @@ static int cstp_reconnect(struct openconnect_info *vpninfo)
|
35
|
|
- interval += vpninfo->reconnect_interval;
|
36
|
|
- if (interval > RECONNECT_INTERVAL_MAX)
|
37
|
|
- interval = RECONNECT_INTERVAL_MAX;
|
38
|
|
-+
|
39
|
|
-+ if (dead_peer && vpninfo->first_peer_addr != NULL) {
|
40
|
|
-+ free(vpninfo->peer_addr);
|
41
|
|
-+ vpninfo->peer_addr = NULL;
|
42
|
|
-+ }
|
43
|
|
- }
|
44
|
|
- script_config_tun(vpninfo, "reconnect");
|
45
|
|
- return 0;
|
46
|
|
-@@ -903,8 +921,15 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
|
47
|
|
- /* Not that this will ever happen; we don't even process
|
48
|
|
- the setting when we're asked for it. */
|
49
|
|
- vpn_progress(vpninfo, PRG_INFO, _("CSTP rekey due\n"));
|
50
|
|
-- if (vpninfo->ssl_times.rekey_method == REKEY_TUNNEL)
|
51
|
|
-- goto do_reconnect;
|
52
|
|
-+ if (vpninfo->ssl_times.rekey_method == REKEY_TUNNEL) {
|
53
|
|
-+ ret = cstp_reconnect(vpninfo, 0);
|
54
|
|
-+ if (ret) {
|
55
|
|
-+ vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
|
56
|
|
-+ vpninfo->quit_reason = "CSTP reconnect failed";
|
57
|
|
-+ return ret;
|
58
|
|
-+ }
|
59
|
|
-+ goto do_dtls_reconnect;
|
60
|
|
-+ }
|
61
|
|
- else if (vpninfo->ssl_times.rekey_method == REKEY_SSL) {
|
62
|
|
- ret = cstp_handshake(vpninfo, 0);
|
63
|
|
- if (ret) {
|
64
|
|
-@@ -922,7 +947,7 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
|
65
|
|
- vpn_progress(vpninfo, PRG_ERR,
|
66
|
|
- _("CSTP Dead Peer Detection detected dead peer!\n"));
|
67
|
|
- do_reconnect:
|
68
|
|
-- ret = cstp_reconnect(vpninfo);
|
69
|
|
-+ ret = cstp_reconnect(vpninfo, 1);
|
70
|
|
- if (ret) {
|
71
|
|
- vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
|
72
|
|
- vpninfo->quit_reason = "CSTP reconnect failed";
|
73
|
|
-diff --git a/library.c b/library.c
|
74
|
|
-index f5d3dc9..7c8d5ec 100644
|
75
|
|
---- a/library.c
|
76
|
|
-+++ b/library.c
|
77
|
|
-@@ -178,6 +178,7 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
|
78
|
|
- CloseHandle(vpninfo->dtls_event);
|
79
|
|
- #endif
|
80
|
|
- free(vpninfo->peer_addr);
|
81
|
|
-+ free(vpninfo->first_peer_addr);
|
82
|
|
- free_optlist(vpninfo->csd_env);
|
83
|
|
- free_optlist(vpninfo->script_env);
|
84
|
|
- free_optlist(vpninfo->cookies);
|
85
|
|
-@@ -291,6 +292,8 @@ int openconnect_set_hostname(struct openconnect_info *vpninfo,
|
86
|
|
- vpninfo->unique_hostname = NULL;
|
87
|
|
- free(vpninfo->peer_addr);
|
88
|
|
- vpninfo->peer_addr = NULL;
|
89
|
|
-+ free(vpninfo->first_peer_addr);
|
90
|
|
-+ vpninfo->first_peer_addr = NULL;
|
91
|
|
-
|
92
|
|
- return 0;
|
93
|
|
- }
|
|
25
|
+@@ -378,6 +378,9 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
|
|
26
|
+ int cstpmtu = atol(colon);
|
|
27
|
+ if (cstpmtu > mtu)
|
|
28
|
+ mtu = cstpmtu;
|
|
29
|
++ } else if (!strcmp(buf + 7, "DynDNS")) {
|
|
30
|
++ if (!strcmp(colon, "true"))
|
|
31
|
++ vpninfo->is_dyndns = 1;
|
|
32
|
+ } else if (!strcmp(buf + 7, "Address-IP6")) {
|
|
33
|
+ vpninfo->ip_info.netmask6 = new_option->value;
|
|
34
|
+ } else if (!strcmp(buf + 7, "Address")) {
|
94
|
35
|
diff --git a/openconnect-internal.h b/openconnect-internal.h
|
95
|
|
-index 1bc79e5..cafbb3c 100644
|
|
36
|
+index 1bc79e5..db6c2ba 100644
|
96
|
37
|
--- a/openconnect-internal.h
|
97
|
38
|
+++ b/openconnect-internal.h
|
98
|
|
-@@ -424,6 +424,9 @@ struct openconnect_info {
|
99
|
|
- struct sockaddr *peer_addr;
|
100
|
|
- struct sockaddr *dtls_addr;
|
101
|
|
-
|
102
|
|
-+ struct sockaddr *first_peer_addr;
|
103
|
|
-+ socklen_t first_peer_addrlen;
|
104
|
|
-+
|
|
39
|
+@@ -427,6 +427,7 @@ struct openconnect_info {
|
105
|
40
|
int dtls_local_port;
|
106
|
41
|
|
107
|
42
|
int deflate;
|
|
43
|
++ int is_dyndns; /* Attempt to redo DNS lookup on each CSTP reconnect */
|
|
44
|
+ char *useragent;
|
|
45
|
+
|
|
46
|
+ const char *quit_reason;
|
108
|
47
|
diff --git a/ssl.c b/ssl.c
|
109
|
|
-index b50652d..e341871 100644
|
|
48
|
+index b50652d..d47a819 100644
|
110
|
49
|
--- a/ssl.c
|
111
|
50
|
+++ b/ssl.c
|
112
|
|
-@@ -110,6 +110,7 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
51
|
+@@ -106,6 +106,23 @@ unsigned string_is_hostname(const char *str)
|
|
52
|
+ return 1;
|
|
53
|
+ }
|
|
54
|
+
|
|
55
|
++static int match_sockaddr(struct sockaddr *a, struct sockaddr *b)
|
|
56
|
++{
|
|
57
|
++ if (a->sa_family == AF_INET) {
|
|
58
|
++ struct sockaddr_in *a4 = (void *)a;
|
|
59
|
++ struct sockaddr_in *b4 = (void *)b;
|
|
60
|
++
|
|
61
|
++ return (a4->sin_addr.s_addr == b4->sin_addr.s_addr) &&
|
|
62
|
++ (a4->sin_port == b4->sin_port);
|
|
63
|
++ } else if (a->sa_family == AF_INET6) {
|
|
64
|
++ struct sockaddr_in6 *a6 = (void *)a;
|
|
65
|
++ struct sockaddr_in6 *b6 = (void *)b;
|
|
66
|
++ return !memcmp(&a6->sin6_addr, &b6->sin6_addr, sizeof(a6->sin6_addr) &&
|
|
67
|
++ a6->sin6_port == b6->sin6_port);
|
|
68
|
++ } else
|
|
69
|
++ return 0;
|
|
70
|
++}
|
|
71
|
++
|
|
72
|
+ int connect_https_socket(struct openconnect_info *vpninfo)
|
113
|
73
|
{
|
114
|
74
|
int ssl_sock = -1;
|
115
|
|
- int err;
|
116
|
|
-+ unsigned retry_old_ip = 0;
|
117
|
|
-
|
|
75
|
+@@ -114,7 +131,11 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
118
|
76
|
if (!vpninfo->port)
|
119
|
77
|
vpninfo->port = 443;
|
120
|
|
-@@ -230,6 +231,8 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
78
|
+
|
|
79
|
+- if (vpninfo->peer_addr) {
|
|
80
|
++ /* If we're talking to a server which told us it has dynamic DNS, don't
|
|
81
|
++ just re-use its previous IP address. If we're talking to a proxy, we
|
|
82
|
++ can use *its* previous IP address. We expect it'll re-do the DNS
|
|
83
|
++ lookup for the server anyway. */
|
|
84
|
++ if (vpninfo->peer_addr && (!vpninfo->is_dyndns || vpninfo->proxy)) {
|
|
85
|
+ reconnect:
|
|
86
|
+ #ifdef SOCK_CLOEXEC
|
|
87
|
+ ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
|
|
88
|
+@@ -230,6 +251,13 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
121
|
89
|
if (hints.ai_flags & AI_NUMERICHOST)
|
122
|
90
|
free(hostname);
|
123
|
91
|
ssl_sock = -EINVAL;
|
124
|
|
-+ if (vpninfo->first_peer_addr != NULL)
|
125
|
|
-+ retry_old_ip = 1;
|
|
92
|
++ /* If we were just retrying for dynamic DNS, reconnct using
|
|
93
|
++ the previously-known IP address */
|
|
94
|
++ if (vpninfo->peer_addr) {
|
|
95
|
++ vpn_progress(vpninfo, PRG_ERR,
|
|
96
|
++ _("Reconnecting to DynDNS server using previously cached IP address\n"));
|
|
97
|
++ goto reconnect;
|
|
98
|
++ }
|
126
|
99
|
goto out;
|
127
|
100
|
}
|
128
|
101
|
if (hints.ai_flags & AI_NUMERICHOST)
|
129
|
|
-@@ -291,7 +294,10 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
102
|
+@@ -257,6 +285,8 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
103
|
+ if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
|
|
104
|
+ /* Store the peer address we actually used, so that DTLS can
|
|
105
|
+ use it again later */
|
|
106
|
++ free(vpninfo->peer_addr);
|
|
107
|
++ vpninfo->peer_addrlen = 0;
|
|
108
|
+ vpninfo->peer_addr = malloc(rp->ai_addrlen);
|
|
109
|
+ if (!vpninfo->peer_addr) {
|
|
110
|
+ vpn_progress(vpninfo, PRG_ERR,
|
|
111
|
+@@ -288,6 +318,17 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
112
|
+ }
|
|
113
|
+ closesocket(ssl_sock);
|
|
114
|
+ ssl_sock = -1;
|
|
115
|
++
|
|
116
|
++ /* If we're in DynDNS mode but this *was* the cached IP address,
|
|
117
|
++ * don't bother falling back to it if it didn't work. */
|
|
118
|
++ if (vpninfo->peer_addr && vpninfo->peer_addrlen == rp->ai_addrlen &&
|
|
119
|
++ match_sockaddr(vpninfo->peer_addr, rp->ai_addr)) {
|
|
120
|
++ vpn_progress(vpninfo, PRG_TRACE,
|
|
121
|
++ _("Forgetting non-functional previous peer address\n"));
|
|
122
|
++ free(vpninfo->peer_addr);
|
|
123
|
++ vpninfo->peer_addr = 0;
|
|
124
|
++ vpninfo->peer_addrlen = 0;
|
|
125
|
++ }
|
130
|
126
|
}
|
131
|
127
|
freeaddrinfo(result);
|
132
|
128
|
|
133
|
|
-+
|
134
|
|
- if (ssl_sock < 0) {
|
135
|
|
-+ if (vpninfo->first_peer_addr != NULL)
|
136
|
|
-+ retry_old_ip = 1;
|
137
|
|
- vpn_progress(vpninfo, PRG_ERR,
|
|
129
|
+@@ -296,6 +337,11 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
138
|
130
|
_("Failed to connect to host %s\n"),
|
139
|
131
|
vpninfo->proxy?:vpninfo->hostname);
|
140
|
|
-@@ -314,6 +320,21 @@ int connect_https_socket(struct openconnect_info *vpninfo)
|
|
132
|
+ ssl_sock = -EINVAL;
|
|
133
|
++ if (vpninfo->peer_addr) {
|
|
134
|
++ vpn_progress(vpninfo, PRG_ERR,
|
|
135
|
++ _("Reconnecting to DynDNS server using previously cached IP address\n"));
|
|
136
|
++ goto reconnect;
|
|
137
|
++ }
|
|
138
|
+ goto out;
|
141
|
139
|
}
|
142
|
140
|
}
|
143
|
|
- out:
|
144
|
|
-+ if (retry_old_ip != 0 && vpninfo->first_peer_addr != NULL) {
|
145
|
|
-+ vpn_progress(vpninfo, PRG_ERR,
|
146
|
|
-+ _("Retrying connection to host %s with original IP\n"),
|
147
|
|
-+ vpninfo->proxy?:vpninfo->hostname);
|
148
|
|
-+
|
149
|
|
-+ retry_old_ip = 0;
|
150
|
|
-+ if (vpninfo->first_peer_addrlen > vpninfo->peer_addrlen || vpninfo->peer_addr == NULL)
|
151
|
|
-+ realloc_inplace(vpninfo->peer_addr, vpninfo->first_peer_addrlen);
|
152
|
|
-+
|
153
|
|
-+ if (vpninfo->peer_addr != NULL) {
|
154
|
|
-+ memcpy(vpninfo->peer_addr, vpninfo->first_peer_addr, vpninfo->first_peer_addrlen);
|
155
|
|
-+ goto reconnect;
|
156
|
|
-+ }
|
157
|
|
-+ }
|
158
|
|
-+
|
159
|
|
- /* If proxy processing returned -EAGAIN to reconnect before attempting
|
160
|
|
- further auth, and we failed to reconnect, we have to clean up here. */
|
161
|
|
- cleanup_proxy_auth(vpninfo);
|
|
141
|
+--
|
|
142
|
+2.1.3
|
|
143
|
+
|