Blob Blame History Raw
From 8bbee9c0ff052cf8ab5ba81fd1b67e3c45e7012a Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 27 Jul 2022 16:07:37 +0100
Subject: [PATCH] lib/crypto.c: Ignore TLS premature termination after write
 shutdown

qemu-nbd doesn't call gnutls_bye to cleanly shut down the connection
after we send NBD_CMD_DISC.  When copying from a qemu-nbd server (or
any operation which calls nbd_shutdown) you will see errors like this:

  $ nbdcopy nbds://foo?tls-certificates=/var/tmp/pki null:
  nbds://foo?tls-certificates=/var/tmp/pki: nbd_shutdown: gnutls_record_recv: The TLS connection was non-properly terminated.

Relatedly you may also see:

  nbd_shutdown: gnutls_record_recv: Error in the pull function.

This commit suppresses the error in the case where we know that we
have shut down writes (which happens after NBD_CMD_DISC has been sent
on the wire).
---
 interop/interop.c |  9 ---------
 lib/crypto.c      | 17 +++++++++++++++++
 lib/internal.h    |  1 +
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/interop/interop.c b/interop/interop.c
index 036545b..cce9407 100644
--- a/interop/interop.c
+++ b/interop/interop.c
@@ -226,19 +226,10 @@ main (int argc, char *argv[])
 
   /* XXX In future test more operations here. */
 
-#if !TLS
-  /* XXX qemu doesn't shut down the connection nicely (using
-   * gnutls_bye) and because of this the following call will fail
-   * with:
-   *
-   * nbd_shutdown: gnutls_record_recv: The TLS connection was
-   * non-properly terminated.
-   */
   if (nbd_shutdown (nbd, 0) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
   }
-#endif
 
   nbd_close (nbd);
 
diff --git a/lib/crypto.c b/lib/crypto.c
index ca9520e..aa5d820 100644
--- a/lib/crypto.c
+++ b/lib/crypto.c
@@ -187,6 +187,22 @@ tls_recv (struct nbd_handle *h, struct socket *sock, void *buf, size_t len)
       errno = EAGAIN;
       return -1;
     }
+    if (h->tls_shut_writes &&
+        (r == GNUTLS_E_PULL_ERROR || r == GNUTLS_E_PREMATURE_TERMINATION)) {
+      /* qemu-nbd doesn't call gnutls_bye to cleanly shut down the
+       * connection after we send NBD_CMD_DISC, instead it simply
+       * closes the connection.  On the client side we see
+       * "gnutls_record_recv: The TLS connection was non-properly
+       * terminated" or "gnutls_record_recv: Error in the pull
+       * function.".
+       *
+       * If we see these errors after we shut down the write side
+       * (h->tls_shut_writes), which happens after we have sent
+       * NBD_CMD_DISC on the wire, downgrade them to a debug message.
+       */
+      debug (h, "gnutls_record_recv: %s", gnutls_strerror (r));
+      return 0; /* EOF */
+    }
     set_error (0, "gnutls_record_recv: %s", gnutls_strerror (r));
     errno = EIO;
     return -1;
@@ -234,6 +250,7 @@ tls_shut_writes (struct nbd_handle *h, struct socket *sock)
     return false;
   if (r != 0)
     debug (h, "ignoring gnutls_bye failure: %s", gnutls_strerror (r));
+  h->tls_shut_writes = true;
   return sock->u.tls.oldsock->ops->shut_writes (h, sock->u.tls.oldsock);
 }
 
diff --git a/lib/internal.h b/lib/internal.h
index 6aaced3..f1b4c63 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -307,6 +307,7 @@ struct nbd_handle {
   struct command *reply_cmd;
 
   bool disconnect_request;      /* True if we've queued NBD_CMD_DISC */
+  bool tls_shut_writes;         /* Used by lib/crypto.c to track disconnect. */
 };
 
 struct meta_context {
-- 
2.31.1