]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Fix timeout handling for DTLS handshake retries
authorDavid Woodhouse <dwmw2@infradead.org>
Wed, 14 Apr 2021 10:22:39 +0000 (11:22 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Wed, 21 Apr 2021 12:12:31 +0000 (13:12 +0100)
We weren't actually waking the mainloop up when the TLS library wanted
to (re)send a DTLS handshake. Plumb the timeout pointer through
dtls_try_handshake() to fix that.

Also feed it to dtls_reconnect(), which helps us ensure that we wake up
after dtls_attempt_period() to try again.

Export dtls_reconnect() while we're at it, since PPP will want to be
able to invoke it.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
dtls.c
gnutls-dtls.c
openconnect-internal.h
openssl-dtls.c

diff --git a/dtls.c b/dtls.c
index d28f0078400933c847675d0e140248a2a1dca83f..fa1bb9816553c2d4fdb312c2e98f1913d0bc521e 100644 (file)
--- a/dtls.c
+++ b/dtls.c
@@ -96,7 +96,7 @@ char *openconnect_bin2base64(const char *prefix, const uint8_t *data, unsigned l
        return p;
 }
 
-static int connect_dtls_socket(struct openconnect_info *vpninfo)
+static int connect_dtls_socket(struct openconnect_info *vpninfo, int *timeout)
 {
        int dtls_fd, ret;
 
@@ -147,7 +147,7 @@ static int connect_dtls_socket(struct openconnect_info *vpninfo)
 
        time(&vpninfo->new_dtls_started);
 
-       return dtls_try_handshake(vpninfo);
+       return dtls_try_handshake(vpninfo, timeout);
 }
 
 void dtls_close(struct openconnect_info *vpninfo)
@@ -164,7 +164,7 @@ void dtls_close(struct openconnect_info *vpninfo)
        vpninfo->dtls_state = DTLS_SLEEPING;
 }
 
-static int dtls_reconnect(struct openconnect_info *vpninfo)
+int dtls_reconnect(struct openconnect_info *vpninfo, int *timeout)
 {
        dtls_close(vpninfo);
 
@@ -172,7 +172,7 @@ static int dtls_reconnect(struct openconnect_info *vpninfo)
                return -EINVAL;
 
        vpninfo->dtls_state = DTLS_SLEEPING;
-       return connect_dtls_socket(vpninfo);
+       return connect_dtls_socket(vpninfo, timeout);
 }
 
 int dtls_setup(struct openconnect_info *vpninfo)
@@ -190,7 +190,7 @@ int dtls_setup(struct openconnect_info *vpninfo)
        if (vpninfo->dtls_times.rekey <= 0)
                vpninfo->dtls_times.rekey_method = REKEY_NONE;
 
-       if (connect_dtls_socket(vpninfo))
+       if (connect_dtls_socket(vpninfo, NULL))
                return -EINVAL;
 
        vpn_progress(vpninfo, PRG_DEBUG,
@@ -239,12 +239,12 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
 
        if (vpninfo->dtls_need_reconnect) {
                vpninfo->dtls_need_reconnect = 0;
-               dtls_reconnect(vpninfo);
+               dtls_reconnect(vpninfo, timeout);
                return 1;
        }
 
        if (vpninfo->dtls_state == DTLS_CONNECTING) {
-               dtls_try_handshake(vpninfo);
+               dtls_try_handshake(vpninfo, timeout);
                vpninfo->delay_tunnel_reason = "DTLS MTU detection";
                return 0;
        }
@@ -254,7 +254,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
 
                if (when <= 0) {
                        vpn_progress(vpninfo, PRG_DEBUG, _("Attempt new DTLS connection\n"));
-                       if (connect_dtls_socket(vpninfo) < 0)
+                       if (connect_dtls_socket(vpninfo, timeout) < 0)
                                *timeout = 1000;
                } else if ((when * 1000) < *timeout) {
                        *timeout = when * 1000;
@@ -349,10 +349,10 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                if (vpninfo->dtls_times.rekey_method == REKEY_SSL) {
                        time(&vpninfo->new_dtls_started);
                        vpninfo->dtls_state = DTLS_CONNECTING;
-                       ret = dtls_try_handshake(vpninfo);
+                       ret = dtls_try_handshake(vpninfo, timeout);
                        if (ret) {
                                vpn_progress(vpninfo, PRG_ERR, _("DTLS Rehandshake failed; reconnecting.\n"));
-                               return connect_dtls_socket(vpninfo);
+                               return connect_dtls_socket(vpninfo, timeout);
                        }
                }
 
@@ -362,7 +362,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
        case KA_DPD_DEAD:
                vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n"));
                /* Fall back to SSL, and start a new DTLS connection */
-               dtls_reconnect(vpninfo);
+               dtls_reconnect(vpninfo, timeout);
                return 1;
 
        case KA_DPD:
@@ -431,7 +431,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                        if (ret < 0) {
                                /* If it's a real error, kill the DTLS connection so
                                   the requeued packet will be sent over SSL */
-                               dtls_reconnect(vpninfo);
+                               dtls_reconnect(vpninfo, timeout);
                                work_done = 1;
                        }
                        return work_done;
index 2aa7fd77b4e0608e9114bd664001611ea08d7118..f14a68802a61acc6e835817094a651466623df08 100644 (file)
@@ -397,7 +397,7 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
        return 0;
 }
 
-int dtls_try_handshake(struct openconnect_info *vpninfo)
+int dtls_try_handshake(struct openconnect_info *vpninfo, int *timeout)
 {
        int err = gnutls_handshake(vpninfo->dtls_ssl);
        char *str;
@@ -476,8 +476,18 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
        }
 
        if (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED) {
-               if (time(NULL) < vpninfo->new_dtls_started + 12)
+               int quit_time = vpninfo->new_dtls_started + 12 - time(NULL);
+               if (quit_time > 0) {
+                       if (timeout) {
+                               unsigned next_resend = gnutls_dtls_get_timeout(vpninfo->dtls_ssl);
+                               if (next_resend && *timeout > next_resend)
+                                       *timeout = next_resend;
+
+                               if (*timeout > quit_time * 1000)
+                                       *timeout = quit_time * 1000;
+                       }
                        return 0;
+               }
                vpn_progress(vpninfo, PRG_DEBUG, _("DTLS handshake timed out\n"));
        }
 
@@ -491,6 +501,8 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
 
        vpninfo->dtls_state = DTLS_SLEEPING;
        time(&vpninfo->new_dtls_started);
+       if (timeout && *timeout > vpninfo->dtls_attempt_period * 1000)
+               *timeout = vpninfo->dtls_attempt_period * 1000;
        return -EINVAL;
 }
 
index 84cbf746257ed1e8cdbe94f5499be302f85b3eb6..3f9699b35c4a1b13cbf4054679164f1bcd2591b4 100644 (file)
@@ -948,7 +948,7 @@ int create_wintun(struct openconnect_info *vpninfo);
 
 /* {gnutls,openssl}-dtls.c */
 int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd);
-int dtls_try_handshake(struct openconnect_info *vpninfo);
+int dtls_try_handshake(struct openconnect_info *vpninfo, int *timeout);
 unsigned dtls_set_mtu(struct openconnect_info *vpninfo, unsigned mtu);
 void dtls_ssl_free(struct openconnect_info *vpninfo);
 
@@ -957,6 +957,7 @@ void destroy_eap_ttls(struct openconnect_info *vpninfo, void *sess);
 
 /* dtls.c */
 int dtls_setup(struct openconnect_info *vpninfo);
+int dtls_reconnect(struct openconnect_info *vpninfo, int *timeout);
 int udp_tos_update(struct openconnect_info *vpninfo, struct pkt *pkt);
 int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable);
 void dtls_close(struct openconnect_info *vpninfo);
index 90cbdc62cce5b927ac72b520fe24ae2a7d710bc9..d47dd70115a6942653f4534a615cd03a3c5d576b 100644 (file)
@@ -553,7 +553,7 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
        return 0;
 }
 
-int dtls_try_handshake(struct openconnect_info *vpninfo)
+int dtls_try_handshake(struct openconnect_info *vpninfo, int *timeout)
 {
        int ret = SSL_do_handshake(vpninfo->dtls_ssl);
 
@@ -683,9 +683,24 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
 
        ret = SSL_get_error(vpninfo->dtls_ssl, ret);
        if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ) {
-               static int badossl_bitched = 0;
-               if (time(NULL) < vpninfo->new_dtls_started + 12)
+               int quit_time = vpninfo->new_dtls_started + 12 - time(NULL);
+
+               if (quit_time > 0) {
+                       if (timeout) {
+                               struct timeval tv;
+                               if (SSL_ctrl(vpninfo->dtls_ssl, DTLS_CTRL_GET_TIMEOUT, 0, &tv)) {
+                                       unsigned timeout_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+                                       if (*timeout > timeout_ms)
+                                               *timeout = timeout_ms;
+                               }
+
+                               if (*timeout > quit_time * 1000)
+                                       *timeout = quit_time * 1000;
+                       }
                        return 0;
+               }
+
+               static int badossl_bitched = 0;
                if (((OPENSSL_VERSION_NUMBER >= 0x100000b0L && OPENSSL_VERSION_NUMBER <= 0x100000c0L) || \
                     (OPENSSL_VERSION_NUMBER >= 0x10001040L && OPENSSL_VERSION_NUMBER <= 0x10001060L) || \
                     OPENSSL_VERSION_NUMBER == 0x10002000L) && !badossl_bitched) {
@@ -705,6 +720,8 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
 
        vpninfo->dtls_state = DTLS_SLEEPING;
        time(&vpninfo->new_dtls_started);
+       if (timeout && *timeout > vpninfo->dtls_attempt_period * 1000)
+               *timeout = vpninfo->dtls_attempt_period * 1000;
        return -EINVAL;
 }