From: Michal Privoznik <mprivozn@redhat.com>
DBus allows only some characters to be specified verbatim [1].
Everything else must be specified via '%NN' syntax where NN is
hex value of the escaped character. Introduce
virStringEscapeDBus() helper to handle string escaping.
1: https://gitlab.freedesktop.org/dbus/dbus/-/blob/2dee5236088bcf690ba92b743a584720b8cb6f55/dbus/dbus-address.c#L288
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
---
src/libvirt_private.syms | 1 +
src/util/virstring.c | 64 ++++++++++++++++++++++++++++++++++++++++
src/util/virstring.h | 1 +
tests/virstringtest.c | 43 +++++++++++++++++++++++++++
4 files changed, 109 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4e57e4a8f6..ef8ddd0330 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3478,6 +3478,7 @@ virSkipSpacesBackwards;
virSkipToDigit;
virStrcpy;
virStringBufferIsPrintable;
+virStringEscapeDBus;
virStringFilterChars;
virStringFormatHex;
virStringHasCaseSuffix;
diff --git a/src/util/virstring.c b/src/util/virstring.c
index e001d76bf1..0729002d12 100644
--- a/src/util/virstring.c
+++ b/src/util/virstring.c
@@ -1118,3 +1118,67 @@ virStringFormatHex(const unsigned char *buf, size_t len)
return g_steal_pointer(&hex);
}
+
+
+/**
+ * virStringEscapeDBus:
+ * @str: string to escape
+ *
+ * Produces new string that's safe to pass to DBUS. Specifically,
+ * alphanumerical characters, digits, '-', '_', '/', '\\', '*' and '.' are
+ * kept. Everything else is escaped using "%NN", where NN is value of the
+ * character in hex.
+ *
+ * Returns: newly allocated string. Caller must free.
+ */
+char *
+virStringEscapeDBus(const char *str)
+{
+ size_t len;
+ size_t i;
+ size_t j = 0;
+ size_t alloc = 1;
+ char *ret;
+
+ if (!str)
+ return NULL;
+
+ len = strlen(str);
+ alloc = len * 1.25; /* assume some escaping */
+ ret = g_new0(char, alloc + 1);
+
+ for (i = 0; str[i]; i++) {
+ const char c = str[i];
+
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '-') ||
+ (c == '_') ||
+ (c == '/') ||
+ (c == '\\') ||
+ (c == '*') ||
+ (c == '.')) {
+ if (j >= alloc) {
+ ret = g_renew(char, ret, alloc + 11);
+ alloc += 10;
+ }
+
+ ret[j++] = c;
+ } else {
+ static const char hextable[] = "0123456789ABCDEF";
+
+ if (j + 3 >= alloc) {
+ ret = g_renew(char, ret, alloc + 11);
+ alloc += 10;
+ }
+
+ ret[j++] = '%';
+ ret[j++] = hextable[(c >> 4) & 15];
+ ret[j++] = hextable[c & 15];
+ }
+ }
+
+ ret[j++] = '\0';
+ return ret;
+}
diff --git a/src/util/virstring.h b/src/util/virstring.h
index 8c2208ece8..aadfc37e41 100644
--- a/src/util/virstring.h
+++ b/src/util/virstring.h
@@ -141,3 +141,4 @@ int virStringParseVersion(unsigned long long *version,
void virStringListRemoveDuplicates(char ***list);
char *virStringFormatHex(const unsigned char *buf, size_t len);
+char *virStringEscapeDBus(const char *str);
diff --git a/tests/virstringtest.c b/tests/virstringtest.c
index 0792155cc3..50b0e2cc5c 100644
--- a/tests/virstringtest.c
+++ b/tests/virstringtest.c
@@ -486,6 +486,29 @@ static int testFilterChars(const void *args)
return 0;
}
+
+struct testDBusData {
+ const char *string;
+ const char *expected;
+};
+
+
+static int
+testDBusEscape(const void *args)
+{
+ const struct testDBusData *data = args;
+ g_autofree char *res = virStringEscapeDBus(data->string);
+
+ if (STRNEQ_NULLABLE(res, data->expected)) {
+ fprintf(stderr, "%s: returned '%s', expected '%s'\n",
+ __FUNCTION__, NULLSTR(res), NULLSTR(data->expected));
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int
mymain(void)
{
@@ -767,6 +790,26 @@ mymain(void)
TEST_FILTER_CHARS(NULL, NULL, NULL);
TEST_FILTER_CHARS("hello 123 hello", "helo", "hellohello");
+#define TEST_DBUS_ESCAPE(str, exp) \
+ do { \
+ struct testDBusData dbusData = { \
+ .string = str, \
+ .expected = exp, \
+ }; \
+ if (virTestRun("DBus escape " #str, \
+ testDBusEscape, &dbusData) < 0) \
+ ret = -1; \
+ } while (0)
+
+ TEST_DBUS_ESCAPE(NULL, NULL);
+ TEST_DBUS_ESCAPE("", "");
+ TEST_DBUS_ESCAPE("-_/\\*.", "-_/\\*.");
+ TEST_DBUS_ESCAPE("abcdefghijklmnopqrstuvwxyz",
+ "abcdefghijklmnopqrstuvwxyz");
+ TEST_DBUS_ESCAPE("/some/~/path/with space/-_\\*./#$",
+ "/some/%7E/path/with%20space/-_\\*./%23%24");
+
+
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
2.51.2
© 2016 - 2026 Red Hat, Inc.