Gitweb:
https://git.exim.org/exim.git/commitdiff/8f9adfd36222d4e9e730734e00dffe874073e5b4
Commit: 8f9adfd36222d4e9e730734e00dffe874073e5b4
Parent: 50a3f20592c79da4acd409b59803dd5ff31b9781
Author: Heiko Schlittermann (HS12-RIPE) <hs@???>
AuthorDate: Tue Feb 18 18:59:49 2020 +0100
Committer: Heiko Schlittermann (HS12-RIPE) <hs@???>
CommitDate: Wed Feb 19 12:02:40 2020 +0100
GnuTLS: tls_write(): wait after uncorking the session
---
src/src/tls-gnu.c | 34 ++++++++++++++++++++++++++++------
1 file changed, 28 insertions(+), 6 deletions(-)
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 2e1b9e4..e28ad9b 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -3353,9 +3353,14 @@ tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
ssize_t outbytes;
size_t left = len;
exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
-#ifdef SUPPORT_CORK
-if (more && !state->corked) gnutls_record_cork(state->session);
+#ifdef SUPPORT_CORK
+if (more && !state->corked)
+ {
+ DEBUG(D_tls) debug_printf("gnutls_record_cork(session=%p)\n", state->session);
+ gnutls_record_cork(state->session);
+ state->corked = TRUE;
+ }
#endif
DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
@@ -3371,6 +3376,7 @@ while (left > 0)
while (outbytes == GNUTLS_E_AGAIN);
DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
+
if (outbytes < 0)
{
DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__);
@@ -3396,10 +3402,26 @@ if (len > INT_MAX)
}
#ifdef SUPPORT_CORK
-if (more != state->corked)
- {
- if (!more) (void) gnutls_record_uncork(state->session, 0);
- state->corked = more;
+if (!more && state->corked)
+ {
+ DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
+ do {
+ do
+ /* We can't use GNUTLS_RECORD_WAIT here, as it retries on
+ GNUTLS_E_AGAIN || GNUTLS_E_INTR, which would break our timeout set by alarm().
+ The GNUTLS_E_AGAIN should not happen ever, as our sockets are blocking anyway.
+ But who knows. (That all relies on the fact that GNUTLS_E_INTR and GNUTLS_E_AGAIN
+ match the EINTR and EAGAIN errno values.) */
+ outbytes = gnutls_record_uncork(state->session, 0);
+ while (outbytes == GNUTLS_E_AGAIN);
+
+ if (outbytes < 0)
+ {
+ record_io_error(state, len, US"uncork", NULL);
+ return -1;
+ }
+ } while (gnutls_record_check_corked(state->session) > 0);
+ state->corked = FALSE;
}
#endif