From: Zbigniew Jędrzejewski-Szmek Date: Tue, 11 Jul 2017 08:42:21 +0000 (-0400) Subject: resolved: allow resolution of names which libidn2 considers invalid (#6315) X-Git-Tag: v234~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ad1f3fe6a816ddd7c2e80f4639825e88d93b5a98;p=thirdparty%2Fsystemd.git resolved: allow resolution of names which libidn2 considers invalid (#6315) https://tools.ietf.org/html/rfc5891#section-4.2.3.1 says that > The Unicode string MUST NOT contain "--" (two consecutive hyphens) in the third > and fourth character positions and MUST NOT start or end with a "-" (hyphen). This means that libidn2 refuses to encode such names. Let's just resolve them without trying to use IDN. --- diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index efa16ad93d6..5aa23485761 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -345,10 +345,10 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, return r; r = dns_question_new_address(&question_idna, family, hostname, true); - if (r < 0) + if (r < 0 && r != -EALREADY) return r; - r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags); + r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, ifindex, flags); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index af29f731643..24f3e8e351f 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -309,8 +309,14 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo r = dns_name_apply_idna(name, &buf); if (r < 0) return r; - if (r > 0) + if (r > 0 && !streq(name, buf)) name = buf; + else + /* We did not manage to create convert the idna name, or it's + * the same as the original name. We assume the caller already + * created an uncoverted question, so let's not repeat work + * unnecessarily. */ + return -EALREADY; } q = dns_question_new(family == AF_UNSPEC ? 2 : 1); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 40aec3a1ea7..12c4d65dd3f 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -1282,10 +1282,13 @@ int dns_name_apply_idna(const char *name, char **ret) { IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); if (r == IDN2_OK) return 1; /* *ret has been written */ - else if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) + log_debug("idn2_lookup_u8(\"%s\") failed: %s", name, idn2_strerror(r)); + if (r == IDN2_2HYPHEN) + /* The name has two hypens — forbidden by IDNA2008 in some cases */ + return 0; + if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) return -ENOSPC; - else - return -EINVAL; + return -EINVAL; #elif defined(HAVE_LIBIDN) _cleanup_free_ char *buf = NULL; size_t n = 0, allocated = 0; @@ -1322,7 +1325,7 @@ int dns_name_apply_idna(const char *name, char **ret) { else buf[n++] = '.'; - n +=r; + n += r; } if (n > DNS_HOSTNAME_MAX) @@ -1335,7 +1338,7 @@ int dns_name_apply_idna(const char *name, char **ret) { *ret = buf; buf = NULL; - return (int) n; + return 1; #else return 0; #endif diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d86add94db5..11cf0b1f0b2 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -607,24 +607,53 @@ 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) { -#if defined(HAVE_LIBIDN2) || defined(HAVE_LIBIDN) +static void test_dns_name_apply_idna_one(const char *s, int expected, const char *result) { _cleanup_free_ char *buf = NULL; - assert_se(dns_name_apply_idna(s, &buf) >= 0); - assert_se(dns_name_equal(buf, result) > 0); -#endif + int r; + + r = dns_name_apply_idna(s, &buf); + log_debug("dns_name_apply_idna: \"%s\" → %d/\"%s\" (expected %d/\"%s\")", + s, r, strnull(buf), expected, strnull(result)); + + assert_se(r == expected); + if (expected == 1) + assert_se(dns_name_equal(buf, result) == 1); } 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"); +#if defined HAVE_LIBIDN2 || defined HAVE_LIBIDN + const int ret = 1; +#else + const int ret = 0; +#endif + + /* IDNA2008 forbids names with hyphens in third and fourth positions + * (https://tools.ietf.org/html/rfc5891#section-4.2.3.1). + * IDNA2003 does not have this restriction + * (https://tools.ietf.org/html/rfc3490#section-5). + * This means that when using libidn we will transform and test more + * labels. If registrars follow IDNA2008 we'll just be performing a + * useless lookup. + */ +#if defined HAVE_LIBIDN + const int ret2 = 1; +#else + const int ret2 = 0; +#endif + + test_dns_name_apply_idna_one("", ret, ""); + test_dns_name_apply_idna_one("foo", ret, "foo"); + test_dns_name_apply_idna_one("foo.", ret, "foo"); + test_dns_name_apply_idna_one("foo.bar", ret, "foo.bar"); + test_dns_name_apply_idna_one("foo.bar.", ret, "foo.bar"); + test_dns_name_apply_idna_one("föö", ret, "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.", ret, "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.bär", ret, "xn--f-1gaa.xn--br-via"); + test_dns_name_apply_idna_one("föö.bär.", ret, "xn--f-1gaa.xn--br-via"); + test_dns_name_apply_idna_one("xn--f-1gaa.xn--br-via", ret, "xn--f-1gaa.xn--br-via"); + + test_dns_name_apply_idna_one("r3---sn-ab5l6ne7.googlevideo.com", ret2, + ret2 ? "r3---sn-ab5l6ne7.googlevideo.com" : ""); } static void test_dns_name_is_valid_or_address(void) { @@ -640,6 +669,9 @@ static void test_dns_name_is_valid_or_address(void) { } int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); test_dns_label_unescape(); test_dns_label_unescape_suffix();