diff options
-rw-r--r-- | src/shared/dns-domain.c | 52 | ||||
-rw-r--r-- | src/shared/dns-domain.h | 2 | ||||
-rw-r--r-- | src/test/test-dns-domain.c | 21 |
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; } |