chiark / gitweb /
shared: dns-name - introduce dns_label_unescape_suffix()
authorTom Gundersen <teg@jklm.no>
Mon, 20 Jul 2015 14:01:03 +0000 (16:01 +0200)
committerSven Eden <yamakuzure@gmx.net>
Tue, 14 Mar 2017 09:07:02 +0000 (10:07 +0100)
Intended to be called repeatedly, and returns then successive unescaped labels
from the most to the least significant (left to right).

This is slightly inefficient as it scans the string three times (two would be
sufficient): once to find the end of the string, once to find the beginning
of each label and lastly once to do the actual unescaping. The latter two
could be done in one go, but that seemed unnecessarily convoluted.

src/shared/dns-domain.c
src/shared/dns-domain.h
src/test/test-dns-domain.c

index a2b9124a9fd9cde60e32524da2ef408ba4a1d4b6..8a0dec154070ffb32b2e3d0c297d909ccf0ef8c9 100644 (file)
@@ -114,6 +114,68 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
         return r;
 }
 
+/* @label_terminal: terminal character of a label, updated to point to the terminal character of
+ *                  the previous label (always skipping one dot) or to NULL if there are no more
+ *                  labels. */
+int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
+        const char *terminal;
+        int r;
+
+        assert(name);
+        assert(label_terminal);
+        assert(dest);
+
+        /* no more labels */
+        if (!*label_terminal) {
+                if (sz >= 1)
+                        *dest = 0;
+
+                return 0;
+        }
+
+        assert(**label_terminal == '.' || **label_terminal == 0);
+
+        /* skip current terminal character */
+        terminal = *label_terminal - 1;
+
+        /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+        for (;;) {
+                if (terminal < name) {
+                        /* reached the first label, so indicate that there are no more */
+                        terminal = NULL;
+                        break;
+                }
+
+                /* find the start of the last label */
+                if (*terminal == '.') {
+                        const char *y;
+                        unsigned slashes = 0;
+
+                        for (y = terminal - 1; y >= name && *y == '\\'; y--)
+                                slashes ++;
+
+                        if (slashes % 2 == 0) {
+                                /* the '.' was not escaped */
+                                name = terminal + 1;
+                                break;
+                        } else {
+                                terminal = y;
+                                continue;
+                        }
+                }
+
+                terminal --;
+        }
+
+        r = dns_label_unescape(&name, dest, sz);
+        if (r < 0)
+                return r;
+
+        *label_terminal = terminal;
+
+        return r;
+}
+
 int dns_label_escape(const char *p, size_t l, char **ret) {
         _cleanup_free_ char *s = NULL;
         char *q;
index 4d9a54e5955567e3a01bff00a9ad2b9064dfbd99..4ee10ab9ee322b9824cc2d6c595732b6b617e5e1 100644 (file)
@@ -29,6 +29,7 @@
 #define DNS_NAME_MAX 255
 
 int dns_label_unescape(const char **name, char *dest, size_t sz);
+int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
 int dns_label_escape(const char *p, size_t l, char **ret);
 
 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
index 567c1f6f1b500f0325dcdeec81c8863db526dc21..31e110cf0d6ee41b0c37b17c4ec6d5731afdaa43 100644 (file)
@@ -50,6 +50,46 @@ static void test_dns_label_unescape(void) {
         test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
 }
 
+static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
+        char buffer[buffer_sz];
+        const char *label;
+        int r;
+
+        label = what + strlen(what);
+
+        r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+        assert_se(r == ret1);
+        if (r >= 0)
+                assert_se(streq(buffer, expect1));
+
+        r = dns_label_unescape_suffix(what, &label, buffer, buffer_sz);
+        assert_se(r == ret2);
+        if (r >= 0)
+                assert_se(streq(buffer, expect2));
+}
+
+static void test_dns_label_unescape_suffix(void) {
+        test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0);
+        test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC);
+        test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0);
+        test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0);
+        test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5);
+        test_dns_label_unescape_suffix_one("hallo.foobar\n", "foobar", "foobar", 20, -EINVAL, -EINVAL);
+        test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
+        test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo  ", "", 20, 7, 0);
+        test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
+        test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
+        test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
+        test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
+        test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
+        test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
+        test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
+        test_dns_label_unescape_suffix_one("foo...bar", "bar", "", 20, 3, -EINVAL);
+        test_dns_label_unescape_suffix_one("foo\\.bar", "foo.bar", "", 20, 7, 0);
+        test_dns_label_unescape_suffix_one("foo\\\\.bar", "bar", "foo\\", 20, 3, 4);
+        test_dns_label_unescape_suffix_one("foo\\\\\\.bar", "foo\\.bar", "", 20, 8, 0);
+}
+
 static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) {
         _cleanup_free_ char *t = NULL;
         int r;
@@ -212,6 +252,7 @@ static void test_dns_name_reverse(void) {
 int main(int argc, char *argv[]) {
 
         test_dns_label_unescape();
+        test_dns_label_unescape_suffix();
         test_dns_label_escape();
         test_dns_name_normalize();
         test_dns_name_equal();