diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 33925bfbd1..8a0dec1540 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -529,6 +529,28 @@ int dns_name_endswith(const char *name, const char *suffix) { } } +int dns_name_between(const char *a, const char *b, const char *c) { + int n; + + /* Determine if b is strictly greater than a and strictly smaller than c. + We consider the order of names to be circular, so that if a is + strictly greater than c, we consider b to be between them if it is + either greater than a or smaller than c. This is how the canonical + DNS name order used in NSEC records work. */ + + n = dns_name_compare_func(a, c); + if (n == 0) + return -EINVAL; + else if (n < 0) + /* a<---b--->c */ + return dns_name_compare_func(a, b) < 0 && + dns_name_compare_func(b, c) < 0; + else + /* <--b--c a--b--> */ + return dns_name_compare_func(b, c) < 0 || + dns_name_compare_func(a, b) < 0; +} + int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { const uint8_t *p; int r; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 5728ce34bb..bd50ad3e6d 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -50,6 +50,7 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_ int dns_name_compare_func(const void *a, const void *b); 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); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 9a5a2a1c23..31e110cf0d 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -160,6 +160,38 @@ static void test_dns_name_equal(void) { test_dns_name_equal_one("..", "..", -EINVAL); } +static void test_dns_name_between_one(const char *a, const char *b, const char *c, int ret) { + int r; + + r = dns_name_between(a, b, c); + assert_se(r == ret); + + r = dns_name_between(c, b, a); + if (ret >= 0) + assert_se(r == 0); + else + assert_se(r == ret); +} + +static void test_dns_name_between(void) { + /* see https://tools.ietf.org/html/rfc4034#section-6.1 + Note that we use "\033.z.example" in stead of "\001.z.example" as we + consider the latter invalid */ + test_dns_name_between_one("example", "a.example", "yljkjljk.a.example", true); + test_dns_name_between_one("a.example", "yljkjljk.a.example", "Z.a.example", true); + test_dns_name_between_one("yljkjljk.a.example", "Z.a.example", "zABC.a.EXAMPLE", true); + test_dns_name_between_one("Z.a.example", "zABC.a.EXAMPLE", "z.example", true); + test_dns_name_between_one("zABC.a.EXAMPLE", "z.example", "\\033.z.example", true); + test_dns_name_between_one("z.example", "\\033.z.example", "*.z.example", true); + test_dns_name_between_one("\\033.z.example", "*.z.example", "\\200.z.example", true); + test_dns_name_between_one("*.z.example", "\\200.z.example", "example", true); + test_dns_name_between_one("\\200.z.example", "example", "a.example", true); + + test_dns_name_between_one("example", "a.example", "example", -EINVAL); + test_dns_name_between_one("example", "example", "yljkjljk.a.example", false); + test_dns_name_between_one("example", "yljkjljk.a.example", "yljkjljk.a.example", false); +} + static void test_dns_name_endswith_one(const char *a, const char *b, int ret) { assert_se(dns_name_endswith(a, b) == ret); } @@ -225,6 +257,7 @@ int main(int argc, char *argv[]) { test_dns_name_normalize(); test_dns_name_equal(); test_dns_name_endswith(); + test_dns_name_between(); test_dns_name_root(); test_dns_name_single_label(); test_dns_name_reverse();