[Libguestfs] [PATCH nbdkit 4/4] reflection: Enhance plugin to support client address mode.

Richard W.M. Jones rjones at redhat.com
Sun Sep 15 14:55:45 UTC 2019


---
 .../reflection/nbdkit-reflection-plugin.pod   | 23 ++++-
 plugins/reflection/reflection.c               | 88 +++++++++++++++++++
 tests/Makefile.am                             |  2 +
 tests/test-reflection-address.sh              | 63 +++++++++++++
 4 files changed, 174 insertions(+), 2 deletions(-)

diff --git a/plugins/reflection/nbdkit-reflection-plugin.pod b/plugins/reflection/nbdkit-reflection-plugin.pod
index 1b260b6..7f52c58 100644
--- a/plugins/reflection/nbdkit-reflection-plugin.pod
+++ b/plugins/reflection/nbdkit-reflection-plugin.pod
@@ -1,10 +1,10 @@
 =head1 NAME
 
-nbdkit-reflection-plugin - reflect export name back to the client
+nbdkit-reflection-plugin - reflect client info back to the client
 
 =head1 SYNOPSIS
 
- nbdkit reflection [mode=]exportname|base64exportname
+ nbdkit reflection [mode=]exportname|base64exportname|address
 
 =head1 DESCRIPTION
 
@@ -17,6 +17,9 @@ similar except the client must base64-encode the data in the export
 name, allowing arbitrary binary data to be sent (see L</EXAMPLES>
 below to make this clearer).
 
+C<mode=address> creates a disk which contains the client's IP address
+and port number as a string.
+
 The plugin only supports read-only access.  To make the disk writable,
 add L<nbdkit-cow-filter(1)> on top.
 
@@ -57,10 +60,26 @@ qemu command line:
  AAAAAAAAAAAAAAAAAAAAVao=
  '
 
+Another use for the reflection plugin is to send back the client's IP
+address:
+
+ $ nbdkit reflection mode=address
+ $ nbdsh -u 'nbd://localhost' -c 'print(h.pread(h.get_size(), 0))'
+
+which will print something like:
+
+ b'[::1]:58912'
+
 =head1 PARAMETERS
 
 =over 4
 
+=item [B<mode=>]B<address>
+
+Reflect the client's IP address and client port number as a string in
+the usual format.  For Unix sockets this sets the disk to the string
+C<"unix"> to avoid leaking host paths.
+
 =item [B<mode=>]B<base64exportname>
 
 Reflect the export name passed by the client, assuming the client
diff --git a/plugins/reflection/reflection.c b/plugins/reflection/reflection.c
index 74f7169..a0d7c60 100644
--- a/plugins/reflection/reflection.c
+++ b/plugins/reflection/reflection.c
@@ -35,6 +35,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
 #include <gnutls/gnutls.h>
@@ -49,6 +52,7 @@
 enum mode {
   MODE_EXPORTNAME,
   MODE_BASE64EXPORTNAME,
+  MODE_ADDRESS,
 };
 static enum mode mode = MODE_EXPORTNAME;
 
@@ -67,6 +71,9 @@ reflection_config (const char *key, const char *value)
       return -1;
 #endif
     }
+    else if (strcasecmp (value, "address") == 0) {
+      mode = MODE_ADDRESS;
+    }
     else {
       nbdkit_error ("unknown mode: '%s'", value);
       return -1;
@@ -137,6 +144,74 @@ decode_base64 (const char *data, size_t len, struct handle *ret)
 #endif
 }
 
+static int
+handle_address (struct sockaddr *sa, socklen_t addrlen,
+                struct handle *ret)
+{
+  struct sockaddr_in *addr = (struct sockaddr_in *) sa;
+  struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) sa;
+  union {
+    char straddr[INET_ADDRSTRLEN];
+    char straddr6[INET6_ADDRSTRLEN];
+  } u;
+  int r;
+  char *str;
+
+  switch (addr->sin_family) {
+  case AF_INET:
+    if (inet_ntop (AF_INET, &addr->sin_addr,
+                   u.straddr, sizeof u.straddr) == NULL) {
+      nbdkit_error ("inet_ntop: %m");
+      return -1;
+    }
+    r = asprintf (&str, "%s:%d", u.straddr, ntohs (addr->sin_port));
+    if (r == -1) {
+      nbdkit_error ("asprintf: %m");
+      return -1;
+    }
+    ret->len = r;
+    ret->data = str;
+    return 0;
+
+  case AF_INET6:
+    if (inet_ntop (AF_INET6, &addr6->sin6_addr,
+                   u.straddr6, sizeof u.straddr6) == NULL) {
+      nbdkit_error ("inet_ntop: %m");
+      return -1;
+    }
+    r = asprintf (&str, "[%s]:%d", u.straddr6, ntohs (addr6->sin6_port));
+    if (r == -1) {
+      nbdkit_error ("asprintf: %m");
+      return -1;
+    }
+    ret->len = r;
+    ret->data = str;
+    return 0;
+
+  case AF_UNIX:
+    /* We don't want to expose the socket path because it's a host
+     * filesystem name.  The client might not really be running on the
+     * same machine (eg. it using a proxy).  However it doesn't even
+     * matter because getpeername(2) on Linux returns a zero length
+     * sun_path in this case!
+     */
+    str = strdup ("unix");
+    if (str == NULL) {
+      nbdkit_error ("strdup: %m");
+      return -1;
+    }
+    ret->len = strlen (str);
+    ret->data = str;
+    return 0;
+
+  default:
+    nbdkit_debug ("unsupported socket family %d", addr->sin_family);
+    ret->data = NULL;
+    ret->len = 0;
+    return 0;
+  }
+}
+
 /* Create the per-connection handle.
  *
  * This is a rather unusual plugin because it has to parse data sent
@@ -147,12 +222,16 @@ decode_base64 (const char *data, size_t len, struct handle *ret)
  * - Inputs that result in unbounded output.
  *
  * - Inputs that could hang, crash or exploit the server.
+ *
+ * - Leaking host information (eg. paths).
  */
 static void *
 reflection_open (int readonly)
 {
   const char *export_name;
   size_t export_name_len;
+  struct sockaddr_storage addr;
+  socklen_t addrlen;
   struct handle *h;
 
   h = malloc (sizeof *h);
@@ -189,6 +268,15 @@ reflection_open (int readonly)
       return h;
     }
 
+  case MODE_ADDRESS:
+    addrlen = sizeof addr;
+    if (nbdkit_peer_name ((struct sockaddr *) &addr, &addrlen) == -1 ||
+        handle_address ((struct sockaddr *) &addr, addrlen, h) == -1) {
+      free (h);
+      return NULL;
+    }
+    return h;
+
   default:
     abort ();
   }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 69d5d5e..1b1e05b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -111,6 +111,7 @@ EXTRA_DIST = \
 	test-rate-dynamic.sh \
 	test.rb \
 	test-readahead-copy.sh \
+	test-reflection-address.sh \
 	test-reflection-base64.sh \
 	test-reflection-raw.sh \
 	test-shutdown.sh \
@@ -606,6 +607,7 @@ test_random_LDADD = libtest.la $(LIBGUESTFS_LIBS)
 
 # reflection plugin test.
 TESTS += \
+	test-reflection-address.sh \
 	test-reflection-base64.sh \
 	test-reflection-raw.sh \
 	$(NULL)
diff --git a/tests/test-reflection-address.sh b/tests/test-reflection-address.sh
new file mode 100755
index 0000000..68a4e8e
--- /dev/null
+++ b/tests/test-reflection-address.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2018-2019 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+# Test the relection plugin with mode=address.
+
+source ./functions.sh
+set -e
+set -x
+
+requires nbdsh --version
+
+sock=`mktemp -u`
+files="reflection-address.out reflection-address.pid $sock"
+rm -f $files
+cleanup_fn rm -f $files
+
+# Run nbdkit.
+start_nbdkit -P reflection-address.pid -U $sock \
+       reflection mode=address
+
+export sock
+nbdsh -c - <<'EOF'
+import os
+import re
+
+h.connect_unix (os.environ["sock"])
+
+size = h.get_size ()
+assert size > 0
+
+buf = h.pread (size, 0)
+print ("buf = %r" % buf)
+assert buf == b'unix'
+EOF
-- 
2.23.0




More information about the Libguestfs mailing list