summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/shared/dns-domain.c49
-rw-r--r--src/shared/dns-domain.h1
-rw-r--r--src/test/test-dns-domain.c20
3 files changed, 69 insertions, 1 deletions
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 59475115ba..e777badb6b 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -263,7 +263,6 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
*(q++) = '0' + (char) ((uint8_t) *p % 10);
sz -= 4;
-
}
p++;
@@ -653,6 +652,54 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
+static int dns_label_unescape_undo_idna(const char **name, char *dest, size_t sz) {
+ int r, k;
+
+ /* Clobbers all arguments on failure... */
+
+ r = dns_label_unescape(name, dest, sz);
+ if (r <= 0)
+ return r;
+
+ k = dns_label_undo_idna(dest, r, dest, sz);
+ if (k < 0)
+ return k;
+ if (k == 0) /* not an IDNA name */
+ return r;
+
+ return k;
+}
+
+int dns_name_startswith(const char *name, const char *prefix) {
+ const char *n, *p;
+ int r, q;
+
+ assert(name);
+ assert(prefix);
+
+ n = name;
+ p = prefix;
+
+ for (;;) {
+ char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+
+ r = dns_label_unescape_undo_idna(&p, lp, sizeof(lp));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+
+ q = dns_label_unescape_undo_idna(&n, ln, sizeof(ln));
+ if (q < 0)
+ return q;
+
+ if (r != q)
+ return false;
+ if (ascii_strcasecmp_n(ln, lp, r) != 0)
+ return false;
+ }
+}
+
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
int r, q, k, w;
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index dd8ae3ac98..4fbe0a618f 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -83,6 +83,7 @@ extern const struct hash_ops dns_name_hash_ops;
int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
+int dns_name_startswith(const char *name, const char *prefix);
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 6c3c49908f..fe3ae45349 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -276,6 +276,25 @@ static void test_dns_name_endswith(void) {
test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
}
+static void test_dns_name_startswith_one(const char *a, const char *b, int ret) {
+ assert_se(dns_name_startswith(a, b) == ret);
+}
+
+static void test_dns_name_startswith(void) {
+ test_dns_name_startswith_one("", "", true);
+ test_dns_name_startswith_one("", "xxx", false);
+ test_dns_name_startswith_one("xxx", "", true);
+ test_dns_name_startswith_one("x", "x", true);
+ test_dns_name_startswith_one("x", "y", false);
+ test_dns_name_startswith_one("x.y", "x.y", true);
+ test_dns_name_startswith_one("x.y", "y.x", false);
+ test_dns_name_startswith_one("x.y", "x", true);
+ test_dns_name_startswith_one("x.y", "X", true);
+ test_dns_name_startswith_one("x.y", "y", false);
+ test_dns_name_startswith_one("x.y", "", true);
+ test_dns_name_startswith_one("x.y", "X", true);
+}
+
static void test_dns_name_is_root(void) {
assert_se(dns_name_is_root(""));
assert_se(dns_name_is_root("."));
@@ -567,6 +586,7 @@ int main(int argc, char *argv[]) {
test_dns_name_normalize();
test_dns_name_equal();
test_dns_name_endswith();
+ test_dns_name_startswith();
test_dns_name_between();
test_dns_name_is_root();
test_dns_name_is_single_label();