diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index ba24a77dad..3635167b33 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -395,9 +395,9 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, } #endif -int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { - _cleanup_free_ char *ret = NULL; - size_t n = 0; +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret) { + _cleanup_free_ char *result = NULL; + size_t n_result = 0, n_unescaped = 0; const char *p; bool first = true; int r; @@ -427,17 +427,18 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r break; } + n_unescaped += r + !first; /* Count unescaped length to make max length determination below */ - if (_ret) { - if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (ret) { + if (!GREEDY_REALLOC(result, n_result + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; - r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); + r = dns_label_escape(label, r, result + n_result + !first, DNS_LABEL_ESCAPED_MAX); if (r < 0) return r; if (!first) - ret[n] = '.'; + result[n_result] = '.'; } else { char escaped[DNS_LABEL_ESCAPED_MAX]; @@ -446,28 +447,34 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r return r; } - n += r + !first; + n_result += r + !first; first = false; } finish: - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; + if (n_unescaped == 0) { + /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (_ret) { - if (n == 0) { - /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (!GREEDY_REALLOC(ret, 2)) + if (ret) { + if (!GREEDY_REALLOC(result, 2)) /* Room for dot, and already pre-allocate space for the trailing NUL byte at the same time */ return -ENOMEM; - ret[n++] = '.'; - } else { - if (!GREEDY_REALLOC(ret, n + 1)) - return -ENOMEM; + result[n_result++] = '.'; } - ret[n] = 0; - *_ret = TAKE_PTR(ret); + n_unescaped++; + } + + if (n_unescaped > DNS_HOSTNAME_MAX) /* Enforce max length check on unescaped length */ + return -EINVAL; + + if (ret) { + /* Suffix with a NUL byte */ + if (!GREEDY_REALLOC(result, n_result + 1)) + return -ENOMEM; + + result[n_result] = 0; + *ret = TAKE_PTR(result); } return 0; diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d775601dae..8088055295 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -375,6 +375,9 @@ static void test_dns_name_is_valid_one(const char *s, int ret, int ret_ldh) { } TEST(dns_name_is_valid) { + test_dns_name_is_valid_one("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[._qotd._tcp.local", 1, 0); + test_dns_name_is_valid_one("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]._qotd._tcp.local", 0, 0); + test_dns_name_is_valid_one("foo", 1, 1); test_dns_name_is_valid_one("foo.", 1, 1); test_dns_name_is_valid_one("foo..", 0, 0);