[Libguestfs] [PATCH libnbd v2 4/5] lib: New API nbd_get_uri to get an NBD URI for a connection.

Richard W.M. Jones rjones at redhat.com
Thu Apr 8 10:36:14 UTC 2021


This will make a best-effort attempt to construct an NBD URI for
connecting back to the current server.  In many cases this is not
really possible (eg. if we were connected with nbd_connect_socket),
and it's not guaranteed to be correct.
---
 generator/API.ml     |  28 +++++++-
 lib/uri.c            | 167 +++++++++++++++++++++++++++++++++++++++++++
 tests/aio-connect.c  |  19 +++++
 tests/connect-tcp.c  |  20 ++++++
 tests/connect-unix.c |  20 ++++++
 tests/connect-uri.c  |  17 +++++
 6 files changed, 269 insertions(+), 2 deletions(-)

diff --git a/generator/API.ml b/generator/API.ml
index 6300b1c..2f1baa8 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -1383,7 +1383,7 @@ compiled with gnutls; you can test whether this is the case
 with L<nbd_supports_tls(3)>.";
     see_also = [URLLink "https://github.com/NetworkBlockDevice/nbd/blob/master/doc/uri.md";
                 Link "set_export_name"; Link "set_tls";
-                Link "set_opt_mode"];
+                Link "set_opt_mode"; Link "get_uri"];
   };
 
   "connect_unix", {
@@ -2875,7 +2875,30 @@ to support TLS encryption, or false if not.";
     longdesc = "\
 Returns true if libnbd was compiled with libxml2 which is required
 to support NBD URIs, or false if not.";
-    see_also = [Link "connect_uri"; Link "aio_connect_uri"];
+    see_also = [Link "connect_uri"; Link "aio_connect_uri";
+                Link "get_uri"];
+  };
+
+  "get_uri", {
+    default_call with
+    args = []; ret = RString;
+    permitted_states = [ Connecting; Negotiating; Connected; Closed; Dead ];
+    shortdesc = "construct an NBD URI for a connection";
+    longdesc = "\
+This makes a best effort attempt to construct an NBD URI which
+could be used to connect to this NBD server (eg. using
+L<nbd_connect_uri(3)>).
+
+The URI returned is not guaranteed to work, and in some cases
+(eg. if connected with L<nbd_connect_socket(3)>) it is not possible
+at all.  Even if a URI is returned it may not be optimal.
+
+On error, L<nbd_get_errno(3)> will be set to C<ENOTSUP> if the
+library was compiled without support for URIs.  In other error
+cases, L<nbd_get_errno(3)> and L<nbd_get_error(3)> should contain
+information about why constructing a URI was not possible.";
+    see_also = [Link "connect_uri"; Link "aio_connect_uri";
+                Link "supports_uri"];
   };
 ]
 
@@ -3009,6 +3032,7 @@ let first_version = [
   (* Added in 1.7.x development cycle, will be stable and supported in 1.8. *)
   "set_private_data", (1, 8);
   "get_private_data", (1, 8);
+  "get_uri", (1, 8);
 
   (* These calls are proposed for a future version of libnbd, but
    * have not been added to any released version so far.
diff --git a/lib/uri.c b/lib/uri.c
index 9f5a290..06eda19 100644
--- a/lib/uri.c
+++ b/lib/uri.c
@@ -25,6 +25,14 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#endif
 
 #include "internal.h"
 #include "vector.h"
@@ -335,6 +343,158 @@ cleanup:
   return ret;
 }
 
+/* This is best effort.  If we didn't save enough information when
+ * connecting then return NULL but try to set errno and the error
+ * string to something useful.
+ */
+
+static int append_query_params (char **query_params,
+                                const char *key, const char *value);
+
+char *
+nbd_unlocked_get_uri (struct nbd_handle *h)
+{
+  xmlURI uri = { 0 };
+  bool using_tls;
+  char *server = NULL;
+  char *query_params = NULL;
+  char *path = NULL;
+  char *ret = NULL;
+
+  if (h->tls == 2)              /* TLS == require */
+    using_tls = true;
+  else if (h->tls_negotiated)
+    using_tls = true;
+  else
+    using_tls = false;
+
+  /* Set scheme, server or socket. */
+  if (h->hostname && h->port) {
+    uri.scheme = using_tls ? "nbds" : "nbd";
+    if (asprintf (&server, "%s:%s", h->hostname, h->port) == -1) {
+      set_error (errno, "asprintf");
+      goto out;
+    }
+    uri.server = server;
+  }
+
+  else if (h->connaddrlen > 0) {
+    switch (h->connaddr.ss_family) {
+    case AF_INET:
+    case AF_INET6: {
+      int err;
+      char host[NI_MAXHOST];
+      char serv[NI_MAXSERV];
+
+      uri.scheme = using_tls ? "nbds" : "nbd";
+      err = getnameinfo ((struct sockaddr *) &h->connaddr, h->connaddrlen,
+                         host, sizeof host, serv, sizeof serv, NI_NUMERICHOST);
+      if (err != 0) {
+        set_error (0, "getnameinfo: %s", gai_strerror (err));
+        goto out;
+      }
+      if (asprintf (&server, "%s:%s", host, serv) == -1) {
+        set_error (errno, "asprintf");
+        goto out;
+      }
+      uri.server = server;
+      break;
+    }
+
+    case AF_UNIX: {
+      struct sockaddr_un *sun = (struct sockaddr_un *) &h->connaddr;
+
+      if (sun->sun_path[0] == '\0') {
+        /* Unix domain sockets in the abstract namespace are in theory
+         * supported in NBD URIs, but libxml2 cannot handle them so
+         * libnbd cannot use them here or in nbd_connect_uri.
+         */
+        set_error (EPROTONOSUPPORT, "Unix domain sockets in the "
+                   "abstract namespace are not yet supported");
+        goto out;
+      }
+
+      uri.scheme = using_tls ? "nbds+unix" : "nbd+unix";
+      if (append_query_params (&query_params, "socket", sun->sun_path) == -1)
+        goto out;
+      /* You have to set this otherwise xmlSaveUri generates bogus
+       * URIs "nbd+unix:/?socket=..."
+       */
+      uri.server = "";
+      break;
+    }
+
+#ifdef AF_VSOCK
+    case AF_VSOCK: {
+      struct sockaddr_vm *svm = (struct sockaddr_vm *) &h->connaddr;
+
+      uri.scheme = using_tls ? "nbds+vsock" : "nbd+vsock";
+      if (asprintf (&server, "%u:%u", svm->svm_cid, svm->svm_port) == -1) {
+        set_error (errno, "asprintf");
+        goto out;
+      }
+      uri.server = server;
+      break;
+    }
+#endif
+
+    default:
+      set_error (EAFNOSUPPORT,
+                 "address family %d not supported", h->connaddr.ss_family);
+      goto out;
+    }
+  }
+
+  else {
+    set_error (EINVAL, "cannot construct a URI for this connection type");
+    goto out;
+  }
+
+  /* Set other fields. */
+  if (h->tls_username)
+    uri.user = h->tls_username;
+  if (h->export_name) {
+    if (asprintf (&path, "/%s", h->export_name) == -1) {
+      set_error (errno, "asprintf");
+      goto out;
+    }
+    uri.path = path;
+  }
+  if (h->tls_psk_file) {
+    if (append_query_params (&query_params,
+                             "tls-psk-file", h->tls_psk_file) == -1)
+      goto out;
+  }
+
+  uri.query_raw = query_params;
+
+  /* Construct the final URI and return it. */
+  ret = (char *) xmlSaveUri (&uri);
+  if (ret == NULL)
+    set_error (errno, "xmlSaveUri failed");
+ out:
+  free (server);
+  free (query_params);
+  free (path);
+  return ret;
+}
+
+static int
+append_query_params (char **query_params, const char *key, const char *value)
+{
+  char *old_query_params = *query_params;
+
+  if (asprintf (query_params, "%s%s%s=%s",
+                old_query_params ? : "",
+                old_query_params ? "&" : "",
+                key, value) == -1) {
+    set_error (errno, "asprintf");
+    return -1;
+  }
+  free (old_query_params);
+  return 0;
+}
+
 #else /* !HAVE_LIBXML2 */
 
 #define NOT_SUPPORTED_ERROR \
@@ -354,4 +514,11 @@ nbd_unlocked_aio_connect_uri (struct nbd_handle *h, const char *raw_uri)
   return -1;
 }
 
+char *
+nbd_unlocked_get_uri (struct nbd_handle *h)
+{
+  set_error (ENOTSUP, NOT_SUPPORTED_ERROR);
+  return NULL;
+}
+
 #endif /* !HAVE_LIBXML2 */
diff --git a/tests/aio-connect.c b/tests/aio-connect.c
index 50e9055..0584005 100644
--- a/tests/aio-connect.c
+++ b/tests/aio-connect.c
@@ -43,6 +43,7 @@ main (int argc, char *argv[])
   pid_t pid;
   size_t i;
   struct sockaddr_in addr;
+  char *actual_uri, *expected_uri;
 
   unlink (PIDFILE);
 
@@ -96,6 +97,24 @@ main (int argc, char *argv[])
     }
   }
 
+  /* libnbd should be able to construct a URI for this connection. */
+  if (asprintf (&expected_uri, "nbd://127.0.0.1:%s/", port_str) == -1) {
+    perror ("asprintf");
+    exit (EXIT_FAILURE);
+  }
+  actual_uri = nbd_get_uri (nbd);
+  if (actual_uri == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (strcmp (actual_uri, expected_uri) != 0) {
+    fprintf (stderr, "%s: actual URI %s != expected URI %s\n",
+             argv[0], actual_uri, expected_uri);
+    exit (EXIT_FAILURE);
+  }
+  free (actual_uri);
+  free (expected_uri);
+
   if (nbd_shutdown (nbd, 0) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
diff --git a/tests/connect-tcp.c b/tests/connect-tcp.c
index d7a36e4..e96b1ec 100644
--- a/tests/connect-tcp.c
+++ b/tests/connect-tcp.c
@@ -22,6 +22,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <time.h>
@@ -38,6 +39,7 @@ main (int argc, char *argv[])
   char port_str[16];
   pid_t pid;
   size_t i;
+  char *actual_uri, *expected_uri;
 
   unlink (PIDFILE);
 
@@ -79,6 +81,24 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }
 
+  /* libnbd should be able to construct a URI for this connection. */
+  if (asprintf (&expected_uri, "nbd://localhost:%s/", port_str) == -1) {
+    perror ("asprintf");
+    exit (EXIT_FAILURE);
+  }
+  actual_uri = nbd_get_uri (nbd);
+  if (actual_uri == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (strcmp (actual_uri, expected_uri) != 0) {
+    fprintf (stderr, "%s: actual URI %s != expected URI %s\n",
+             argv[0], actual_uri, expected_uri);
+    exit (EXIT_FAILURE);
+  }
+  free (actual_uri);
+  free (expected_uri);
+
   if (nbd_shutdown (nbd, 0) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
diff --git a/tests/connect-unix.c b/tests/connect-unix.c
index 8c18166..0000b98 100644
--- a/tests/connect-unix.c
+++ b/tests/connect-unix.c
@@ -22,6 +22,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 
@@ -36,6 +37,7 @@ main (int argc, char *argv[])
   struct nbd_handle *nbd;
   pid_t pid;
   size_t i;
+  char *actual_uri, *expected_uri;
 
   if (mkstemp (socket) == -1) {
     perror (socket);
@@ -77,6 +79,24 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }
 
+  /* libnbd should be able to construct a URI for this connection. */
+  if (asprintf (&expected_uri, "nbd+unix:///?socket=%s", socket) == -1) {
+    perror ("asprintf");
+    exit (EXIT_FAILURE);
+  }
+  actual_uri = nbd_get_uri (nbd);
+  if (actual_uri == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (strcmp (actual_uri, expected_uri) != 0) {
+    fprintf (stderr, "%s: actual URI %s != expected URI %s\n",
+             argv[0], actual_uri, expected_uri);
+    exit (EXIT_FAILURE);
+  }
+  free (actual_uri);
+  free (expected_uri);
+
   if (nbd_shutdown (nbd, 0) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
diff --git a/tests/connect-uri.c b/tests/connect-uri.c
index 6e7d168..9093247 100644
--- a/tests/connect-uri.c
+++ b/tests/connect-uri.c
@@ -35,6 +35,7 @@ main (int argc, char *argv[])
   struct nbd_handle *nbd;
   pid_t pid;
   size_t i;
+  char *get_uri;
 
 #ifdef SOCKET
   unlink (SOCKET);
@@ -89,6 +90,22 @@ main (int argc, char *argv[])
     }
   }
 
+  /* Usually the URI returned by nbd_get_uri should be the same as the
+   * one passed to nbd_connect_uri, or at least it will be in our test
+   * cases.
+   */
+  get_uri = nbd_get_uri (nbd);
+  if (get_uri == NULL) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (strcmp (URI, get_uri) != 0) {
+    fprintf (stderr, "%s: connect URI %s != get URI %s\n",
+             argv[0], URI, get_uri);
+    exit (EXIT_FAILURE);
+  }
+  free (get_uri);
+
   if (nbd_shutdown (nbd, 0) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
-- 
2.29.0.rc2




More information about the Libguestfs mailing list