From: Hyman Huang <yong.hu...@smartx.com>

As advised by the GNU TLS, the caller should attempt again
if the gnutls_record_{recv,send} return EAGAIN or EINTR;
check the following link to view the details:
https://www.gnutls.org/manual/html_node/Data-transfer-and-termination.html

Add the retry parameter for virNetTLSSession{Read,Write}
functions in accordance with this guideline.

This prevents the upper application from encountering the
following error message when it calls the virConnectOpenAuth API:
Unable to read TLS confirmation: Resource temporarily unavailable

Signed-off-by: Hyman Huang <yong.hu...@smartx.com>
---
 src/rpc/virnetclient.c     |  2 +-
 src/rpc/virnetsocket.c     |  4 ++--
 src/rpc/virnettlscontext.c | 28 ++++++++++++++++++++++++++--
 src/rpc/virnettlscontext.h |  6 ++++--
 4 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c
index 92933220e2..5340db4211 100644
--- a/src/rpc/virnetclient.c
+++ b/src/rpc/virnetclient.c
@@ -1003,7 +1003,7 @@ int virNetClientSetTLSSession(virNetClient *client,
     ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
 #endif /* !WIN32 */
 
-    len = virNetTLSSessionRead(client->tls, buf, 1);
+    len = virNetTLSSessionRead(client->tls, buf, 1, true);
     if (len < 0 && errno != ENOMSG) {
         virReportSystemError(errno, "%s",
                              _("Unable to read TLS confirmation"));
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
index e8fc2d5f7d..6774dd4a4b 100644
--- a/src/rpc/virnetsocket.c
+++ b/src/rpc/virnetsocket.c
@@ -1739,7 +1739,7 @@ static ssize_t virNetSocketReadWire(virNetSocket *sock, 
char *buf, size_t len)
     if (sock->tlsSession &&
         virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
         VIR_NET_TLS_HANDSHAKE_COMPLETE) {
-        ret = virNetTLSSessionRead(sock->tlsSession, buf, len);
+        ret = virNetTLSSessionRead(sock->tlsSession, buf, len, false);
     } else {
         ret = read(sock->fd, buf, len);
     }
@@ -1807,7 +1807,7 @@ static ssize_t virNetSocketWriteWire(virNetSocket *sock, 
const char *buf, size_t
     if (sock->tlsSession &&
         virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
         VIR_NET_TLS_HANDSHAKE_COMPLETE) {
-        ret = virNetTLSSessionWrite(sock->tlsSession, buf, len);
+        ret = virNetTLSSessionWrite(sock->tlsSession, buf, len, false);
     } else {
         ret = write(sock->fd, buf, len); /* sc_avoid_write */
     }
diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
index e8023133b4..92d909c0b7 100644
--- a/src/rpc/virnettlscontext.c
+++ b/src/rpc/virnettlscontext.c
@@ -679,16 +679,28 @@ void virNetTLSSessionSetIOCallbacks(virNetTLSSession 
*sess,
 
 
 ssize_t virNetTLSSessionWrite(virNetTLSSession *sess,
-                              const char *buf, size_t len)
+                              const char *buf, size_t len,
+                              bool retry)
 {
     ssize_t ret;
 
+ rewrite:
     virObjectLock(sess);
     ret = gnutls_record_send(sess->session, buf, len);
 
     if (ret >= 0)
         goto cleanup;
 
+    if (retry && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) {
+        /*
+         * GNU TLS advises calling the function again to obtain the data if 
EAGAIN is returned.
+         * See reference: 
https://www.gnutls.org/manual/html_node/Data-transfer-and-termination.html
+         * */
+        VIR_DEBUG("Try writing data from the TLS session again");
+        virObjectUnlock(sess);
+        goto rewrite;
+    }
+
     switch (ret) {
     case GNUTLS_E_AGAIN:
         errno = EAGAIN;
@@ -712,16 +724,28 @@ ssize_t virNetTLSSessionWrite(virNetTLSSession *sess,
 }
 
 ssize_t virNetTLSSessionRead(virNetTLSSession *sess,
-                             char *buf, size_t len)
+                             char *buf, size_t len,
+                             bool retry)
 {
     ssize_t ret;
 
+ reread:
     virObjectLock(sess);
     ret = gnutls_record_recv(sess->session, buf, len);
 
     if (ret >= 0)
         goto cleanup;
 
+    if (retry && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)) {
+        /*
+         * GNU TLS advises calling the function again to obtain the data if 
EAGAIN is returned.
+         * See reference: 
https://www.gnutls.org/manual/html_node/Data-transfer-and-termination.html
+         * */
+        VIR_DEBUG("Try reading data from the TLS session again");
+        virObjectUnlock(sess);
+        goto reread;
+    }
+
     switch (ret) {
     case GNUTLS_E_AGAIN:
         errno = EAGAIN;
diff --git a/src/rpc/virnettlscontext.h b/src/rpc/virnettlscontext.h
index 11c954ce4b..da26d7836b 100644
--- a/src/rpc/virnettlscontext.h
+++ b/src/rpc/virnettlscontext.h
@@ -81,9 +81,11 @@ void virNetTLSSessionSetIOCallbacks(virNetTLSSession *sess,
                                     void *opaque);
 
 ssize_t virNetTLSSessionWrite(virNetTLSSession *sess,
-                              const char *buf, size_t len);
+                              const char *buf, size_t len,
+                              bool retry);
 ssize_t virNetTLSSessionRead(virNetTLSSession *sess,
-                             char *buf, size_t len);
+                             char *buf, size_t len,
+                             bool retry);
 
 int virNetTLSSessionHandshake(virNetTLSSession *sess);
 
-- 
2.27.0

Reply via email to