Revert "GnuTLS: remove GNUTLS_E_AGAIN handling"
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Tue, 3 Mar 2020 07:06:22 +0000 (08:06 +0100)
committerHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Tue, 3 Mar 2020 07:06:22 +0000 (08:06 +0100)
This reverts commit 0b049796b89a59fc322119b54199d92c404ef687.

Thanks to Andreas Metzler for pointing me to:
https://gitlab.com/gnutls/gnutls/issues/644#note_123363338

src/src/tls-gnu.c

index 90c4cd0..72f1787 100644 (file)
@@ -2513,7 +2513,7 @@ sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 do
   rc = gnutls_handshake(state->session);
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 do
   rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_AGAIN ||  rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
@@ -2993,7 +2993,7 @@ sigalrm_seen = FALSE;
 ALARM(ob->command_timeout);
 do
   rc = gnutls_handshake(state->session);
 ALARM(ob->command_timeout);
 do
   rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
@@ -3157,7 +3157,10 @@ DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, buf
 sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 
 sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 
-inbytes = gnutls_record_recv(state->session, state->xfer_buffer, MIN(ssl_xfer_buffer_size, lim));
+do
+  inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
+    MIN(ssl_xfer_buffer_size, lim));
+while (inbytes == GNUTLS_E_AGAIN);
 
 if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
 
 if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
@@ -3314,7 +3317,9 @@ DEBUG(D_tls)
   debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n",
       state->session, buff, len);
 
   debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n",
       state->session, buff, len);
 
-inbytes = gnutls_record_recv(state->session, buff, len);
+do
+  inbytes = gnutls_record_recv(state->session, buff, len);
+while (inbytes == GNUTLS_E_AGAIN);
 
 if (inbytes > 0) return inbytes;
 if (inbytes == 0)
 
 if (inbytes > 0) return inbytes;
 if (inbytes == 0)
@@ -3375,7 +3380,9 @@ while (left > 0)
   DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n",
       state->session, buff, left);
 
   DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n",
       state->session, buff, left);
 
-  outbytes = gnutls_record_send(state->session, buff, left);
+  do
+    outbytes = gnutls_record_send(state->session, buff, left);
+  while (outbytes == GNUTLS_E_AGAIN);
 
   DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
 
 
   DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
 
@@ -3407,12 +3414,21 @@ if (len > INT_MAX)
 if (!more && state->corked)
   {
   DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
 if (!more && state->corked)
   {
   DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
-  outbytes = gnutls_record_uncork(state->session, 0);
+  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;
     }
   if (outbytes < 0)
     {
     record_io_error(state, len, US"uncork", NULL);
     return -1;
     }
+
   state->corked = FALSE;
   }
 #endif
   state->corked = FALSE;
   }
 #endif