summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/shared/dns-domain.c52
-rw-r--r--src/shared/dns-domain.h2
-rw-r--r--src/test/test-dns-domain.c21
3 files changed, 75 insertions, 0 deletions
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 04624734dc..33bc26ad7d 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -1303,3 +1303,55 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
k++;
}
}
+
+int dns_name_apply_idna(const char *name, char **ret) {
+ _cleanup_free_ char *buf = NULL;
+ size_t n = 0, allocated = 0;
+ bool first = true;
+ int r, q;
+
+ assert(name);
+ assert(ret);
+
+ for (;;) {
+ char label[DNS_LABEL_MAX];
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ q = dns_label_apply_idna(label, r, label, sizeof(label));
+ if (q < 0)
+ return q;
+ if (q > 0)
+ r = q;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
+
+ r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ if (first)
+ first = false;
+ else
+ buf[n++] = '.';
+
+ n +=r;
+ }
+
+ if (n > DNS_HOSTNAME_MAX)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + 1))
+ return -ENOMEM;
+
+ buf[n] = 0;
+ *ret = buf;
+ buf = NULL;
+
+ return (int) n;
+}
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 5f9542ef98..40c9ee5f27 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -108,3 +108,5 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret);
int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);
int dns_name_common_suffix(const char *a, const char *b, const char **ret);
+
+int dns_name_apply_idna(const char *name, char **ret);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 987f1fc887..3b260ee75d 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -598,6 +598,26 @@ static void test_dns_name_common_suffix(void) {
test_dns_name_common_suffix_one("FOO.BAR", "tEST.bAR", "BAR");
}
+static void test_dns_name_apply_idna_one(const char *s, const char *result) {
+#ifdef HAVE_LIBIDN
+ _cleanup_free_ char *buf = NULL;
+ assert_se(dns_name_apply_idna(s, &buf) >= 0);
+ assert_se(dns_name_equal(buf, result) > 0);
+#endif
+}
+
+static void test_dns_name_apply_idna(void) {
+ test_dns_name_apply_idna_one("", "");
+ test_dns_name_apply_idna_one("foo", "foo");
+ test_dns_name_apply_idna_one("foo.", "foo");
+ test_dns_name_apply_idna_one("foo.bar", "foo.bar");
+ test_dns_name_apply_idna_one("foo.bar.", "foo.bar");
+ test_dns_name_apply_idna_one("föö", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.bär", "xn--f-1gaa.xn--br-via");
+ test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via");
+}
+
int main(int argc, char *argv[]) {
test_dns_label_unescape();
@@ -624,6 +644,7 @@ int main(int argc, char *argv[]) {
test_dns_name_equal_skip();
test_dns_name_compare_func();
test_dns_name_common_suffix();
+ test_dns_name_apply_idna();
return 0;
}