From: Nick Owens Date: Tue, 2 Jun 2015 18:49:43 +0000 (-0700) Subject: resolve: move dns routines into shared X-Git-Tag: v226.4~1^2~326 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=c3bca121efa7ab2b876aec48d686690518fc5bd7 resolve: move dns routines into shared --- diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c new file mode 100644 index 000000000..20a44ce4e --- /dev/null +++ b/src/shared/dns-domain.c @@ -0,0 +1,613 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#ifdef HAVE_LIBIDN +#include +#include +#endif + +#include "dns-domain.h" + +int dns_label_unescape(const char **name, char *dest, size_t sz) { + const char *n; + char *d; + int r = 0; + + assert(name); + assert(*name); + assert(dest); + + n = *name; + d = dest; + + for (;;) { + if (*n == '.') { + n++; + break; + } + + if (*n == 0) + break; + + if (sz <= 0) + return -ENOSPC; + + if (r >= DNS_LABEL_MAX) + return -EINVAL; + + if (*n == '\\') { + /* Escaped character */ + + n++; + + if (*n == 0) + /* Ending NUL */ + return -EINVAL; + + else if (*n == '\\' || *n == '.') { + /* Escaped backslash or dot */ + *(d++) = *(n++); + sz--; + r++; + + } else if (n[0] >= '0' && n[0] <= '9') { + unsigned k; + + /* Escaped literal ASCII character */ + + if (!(n[1] >= '0' && n[1] <= '9') || + !(n[2] >= '0' && n[2] <= '9')) + return -EINVAL; + + k = ((unsigned) (n[0] - '0') * 100) + + ((unsigned) (n[1] - '0') * 10) + + ((unsigned) (n[2] - '0')); + + /* Don't allow CC characters or anything that doesn't fit in 8bit */ + if (k < ' ' || k > 255 || k == 127) + return -EINVAL; + + *(d++) = (char) k; + sz--; + r++; + + n += 3; + } else + return -EINVAL; + + } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { + + /* Normal character */ + *(d++) = *(n++); + sz--; + r++; + } else + return -EINVAL; + } + + /* Empty label that is not at the end? */ + if (r == 0 && *n) + return -EINVAL; + + if (sz >= 1) + *d = 0; + + *name = n; + return r; +} + +int dns_label_escape(const char *p, size_t l, char **ret) { + _cleanup_free_ char *s = NULL; + char *q; + int r; + + assert(p); + assert(ret); + + if (l > DNS_LABEL_MAX) + return -EINVAL; + + s = malloc(l * 4 + 1); + if (!s) + return -ENOMEM; + + q = s; + while (l > 0) { + + if (*p == '.' || *p == '\\') { + + /* Dot or backslash */ + *(q++) = '\\'; + *(q++) = *p; + + } else if (*p == '_' || + *p == '-' || + (*p >= '0' && *p <= '9') || + (*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z')) { + + /* Proper character */ + *(q++) = *p; + } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { + + /* Everything else */ + *(q++) = '\\'; + *(q++) = '0' + (char) ((uint8_t) *p / 100); + *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); + *(q++) = '0' + (char) ((uint8_t) *p % 10); + + } else + return -EINVAL; + + p++; + l--; + } + + *q = 0; + *ret = s; + r = q - s; + s = NULL; + + return r; +} + +int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { +#ifdef HAVE_LIBIDN + _cleanup_free_ uint32_t *input = NULL; + size_t input_size; + const char *p; + bool contains_8bit = false; + + assert(encoded); + assert(decoded); + assert(decoded_max >= DNS_LABEL_MAX); + + if (encoded_size <= 0) + return 0; + + for (p = encoded; p < encoded + encoded_size; p++) + if ((uint8_t) *p > 127) + contains_8bit = true; + + if (!contains_8bit) + return 0; + + input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); + if (!input) + return -ENOMEM; + + if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0) + return -EINVAL; + + return strlen(decoded); +#else + return 0; +#endif +} + +int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { +#ifdef HAVE_LIBIDN + size_t input_size, output_size; + _cleanup_free_ uint32_t *input = NULL; + _cleanup_free_ char *result = NULL; + uint32_t *output = NULL; + size_t w; + + /* To be invoked after unescaping */ + + assert(encoded); + assert(decoded); + + if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1) + return 0; + + if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0) + return 0; + + input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); + if (!input) + return -ENOMEM; + + output_size = input_size; + output = newa(uint32_t, output_size); + + idna_to_unicode_44i(input, input_size, output, &output_size, 0); + + result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); + if (!result) + return -ENOMEM; + if (w <= 0) + return 0; + if (w+1 > decoded_max) + return -EINVAL; + + memcpy(decoded, result, w+1); + return w; +#else + return 0; +#endif +} + +int dns_name_normalize(const char *s, char **_ret) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + const char *p = s; + bool first = true; + int r; + + assert(s); + + for (;;) { + _cleanup_free_ char *t = NULL; + char label[DNS_LABEL_MAX]; + int k; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) { + if (*p != 0) + return -EINVAL; + break; + } + + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + return k; + if (k > 0) + r = k; + + r = dns_label_escape(label, r, &t); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) + return -ENOMEM; + + if (!first) + ret[n++] = '.'; + else + first = false; + + memcpy(ret + n, t, r); + n += r; + } + + if (n > DNS_NAME_MAX) + return -EINVAL; + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + + ret[n] = 0; + + if (_ret) { + *_ret = ret; + ret = NULL; + } + + return 0; +} + +unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) { + const char *p = s; + unsigned long ul = hash_key[0]; + int r; + + assert(p); + + while (*p) { + char label[DNS_LABEL_MAX+1]; + int k; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + break; + + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + break; + if (k > 0) + r = k; + + label[r] = 0; + ascii_strlower(label); + + ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key); + } + + return ul; +} + +int dns_name_compare_func(const void *a, const void *b) { + const char *x = a, *y = b; + int r, q, k, w; + + assert(a); + assert(b); + + for (;;) { + char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; + + if (*x == 0 && *y == 0) + return 0; + + r = dns_label_unescape(&x, la, sizeof(la)); + q = dns_label_unescape(&y, lb, sizeof(lb)); + if (r < 0 || q < 0) + return r - q; + + k = dns_label_undo_idna(la, r, la, sizeof(la)); + w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); + if (k < 0 || w < 0) + return k - w; + if (k > 0) + r = k; + if (w > 0) + r = w; + + la[r] = lb[q] = 0; + r = strcasecmp(la, lb); + if (r != 0) + return r; + } +} + +const struct hash_ops dns_name_hash_ops = { + .hash = dns_name_hash_func, + .compare = dns_name_compare_func +}; + +int dns_name_equal(const char *x, const char *y) { + int r, q, k, w; + + assert(x); + assert(y); + + for (;;) { + char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; + + if (*x == 0 && *y == 0) + return true; + + r = dns_label_unescape(&x, la, sizeof(la)); + if (r < 0) + return r; + + k = dns_label_undo_idna(la, r, la, sizeof(la)); + if (k < 0) + return k; + if (k > 0) + r = k; + + q = dns_label_unescape(&y, lb, sizeof(lb)); + if (q < 0) + return q; + w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); + if (w < 0) + return w; + if (w > 0) + q = w; + + la[r] = lb[q] = 0; + if (strcasecmp(la, lb)) + return false; + } +} + +int dns_name_endswith(const char *name, const char *suffix) { + const char *n, *s, *saved_n = NULL; + int r, q, k, w; + + assert(name); + assert(suffix); + + n = name; + s = suffix; + + for (;;) { + char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; + + r = dns_label_unescape(&n, ln, sizeof(ln)); + if (r < 0) + return r; + k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); + if (k < 0) + return k; + if (k > 0) + r = k; + + if (!saved_n) + saved_n = n; + + q = dns_label_unescape(&s, ls, sizeof(ls)); + if (q < 0) + return q; + w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); + if (w < 0) + return w; + if (w > 0) + q = w; + + if (r == 0 && q == 0) + return true; + if (r == 0 && saved_n == n) + return false; + + ln[r] = ls[q] = 0; + + if (r != q || strcasecmp(ln, ls)) { + + /* Not the same, let's jump back, and try with the next label again */ + s = suffix; + n = saved_n; + saved_n = NULL; + } + } +} + +int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { + const uint8_t *p; + int r; + + assert(a); + assert(ret); + + p = (const uint8_t*) a; + + if (family == AF_INET) + r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); + else if (family == AF_INET6) + r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", + hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), + hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), + hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), + hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), + hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), + hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), + hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), + hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); + else + return -EAFNOSUPPORT; + if (r < 0) + return -ENOMEM; + + return 0; +} + +int dns_name_address(const char *p, int *family, union in_addr_union *address) { + int r; + + assert(p); + assert(family); + assert(address); + + r = dns_name_endswith(p, "in-addr.arpa"); + if (r < 0) + return r; + if (r > 0) { + uint8_t a[4]; + unsigned i; + + for (i = 0; i < ELEMENTSOF(a); i++) { + char label[DNS_LABEL_MAX+1]; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + if (r > 3) + return -EINVAL; + + r = safe_atou8(label, &a[i]); + if (r < 0) + return r; + } + + r = dns_name_equal(p, "in-addr.arpa"); + if (r <= 0) + return r; + + *family = AF_INET; + address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | + ((uint32_t) a[2] << 16) | + ((uint32_t) a[1] << 8) | + (uint32_t) a[0]); + + return 1; + } + + r = dns_name_endswith(p, "ip6.arpa"); + if (r < 0) + return r; + if (r > 0) { + struct in6_addr a; + unsigned i; + + for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { + char label[DNS_LABEL_MAX+1]; + int x, y; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r <= 0) + return r; + if (r != 1) + return -EINVAL; + x = unhexchar(label[0]); + if (x < 0) + return -EINVAL; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r <= 0) + return r; + if (r != 1) + return -EINVAL; + y = unhexchar(label[0]); + if (y < 0) + return -EINVAL; + + a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; + } + + r = dns_name_equal(p, "ip6.arpa"); + if (r <= 0) + return r; + + *family = AF_INET6; + address->in6 = a; + return 1; + } + + return 0; +} + +int dns_name_root(const char *name) { + char label[DNS_LABEL_MAX+1]; + int r; + + assert(name); + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + return r; + + return r == 0 && *name == 0; +} + +int dns_name_single_label(const char *name) { + char label[DNS_LABEL_MAX+1]; + int r; + + assert(name); + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) + return 0; + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + return r; + + return r == 0 && *name == 0; +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h new file mode 100644 index 000000000..516d244f7 --- /dev/null +++ b/src/shared/dns-domain.h @@ -0,0 +1,50 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#pragma once + + +#include "hashmap.h" +#include "in-addr-util.h" + +#define DNS_LABEL_MAX 63 +#define DNS_NAME_MAX 255 + +int dns_label_unescape(const char **name, char *dest, size_t sz); +int dns_label_escape(const char *p, size_t l, char **ret); + +int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); +int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); + +int dns_name_normalize(const char *s, char **_ret); + +unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]); +int dns_name_compare_func(const void *a, const void *b); +extern const struct hash_ops dns_name_hash_ops; + +int dns_name_equal(const char *x, const char *y); +int dns_name_endswith(const char *name, const char *suffix); + +int dns_name_reverse(int family, const union in_addr_union *a, char **ret); +int dns_name_address(const char *p, int *family, union in_addr_union *a); + +int dns_name_root(const char *name); +int dns_name_single_label(const char *name); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c new file mode 100644 index 000000000..527cdd3b5 --- /dev/null +++ b/src/test/test-dns-domain.c @@ -0,0 +1,192 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include "macro.h" +#include "dns-domain.h" + +static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) { + char buffer[buffer_sz]; + int r; + + r = dns_label_unescape(&what, buffer, buffer_sz); + assert_se(r == ret); + + if (r < 0) + return; + + assert_se(streq(buffer, expect)); +} + +static void test_dns_label_unescape(void) { + test_dns_label_unescape_one("hallo", "hallo", 6, 5); + test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC); + test_dns_label_unescape_one("", "", 10, 0); + test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12); + test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5); + test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL); + test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL); + test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7); + test_dns_label_unescape_one(".", "", 20, 0); + test_dns_label_unescape_one("..", "", 20, -EINVAL); + test_dns_label_unescape_one(".foobar", "", 20, -EINVAL); + test_dns_label_unescape_one("foobar.", "foobar", 20, 6); +} + +static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) { + _cleanup_free_ char *t = NULL; + int r; + + r = dns_label_escape(what, l, &t); + assert_se(r == ret); + + if (r < 0) + return; + + assert_se(streq_ptr(expect, t)); +} + +static void test_dns_label_escape(void) { + test_dns_label_escape_one("", 0, "", 0); + test_dns_label_escape_one("hallo", 5, "hallo", 5); + test_dns_label_escape_one("hallo", 6, NULL, -EINVAL); + test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31); +} + +static void test_dns_name_normalize_one(const char *what, const char *expect, int ret) { + _cleanup_free_ char *t = NULL; + int r; + + r = dns_name_normalize(what, &t); + assert_se(r == ret); + + if (r < 0) + return; + + assert_se(streq_ptr(expect, t)); +} + +static void test_dns_name_normalize(void) { + test_dns_name_normalize_one("", "", 0); + test_dns_name_normalize_one("f", "f", 0); + test_dns_name_normalize_one("f.waldi", "f.waldi", 0); + test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0); + test_dns_name_normalize_one("\\000", NULL, -EINVAL); + test_dns_name_normalize_one("..", NULL, -EINVAL); + test_dns_name_normalize_one(".foobar", NULL, -EINVAL); + test_dns_name_normalize_one("foobar.", "foobar", 0); + test_dns_name_normalize_one(".", "", 0); +} + +static void test_dns_name_equal_one(const char *a, const char *b, int ret) { + int r; + + r = dns_name_equal(a, b); + assert_se(r == ret); + + r = dns_name_equal(b, a); + assert_se(r == ret); +} + +static void test_dns_name_equal(void) { + test_dns_name_equal_one("", "", true); + test_dns_name_equal_one("x", "x", true); + test_dns_name_equal_one("x", "x.", true); + test_dns_name_equal_one("abc.def", "abc.def", true); + test_dns_name_equal_one("abc.def", "ABC.def", true); + test_dns_name_equal_one("abc.def", "CBA.def", false); + test_dns_name_equal_one("", "xxx", false); + test_dns_name_equal_one("ab", "a", false); + test_dns_name_equal_one("\\000", "xxxx", -EINVAL); + test_dns_name_equal_one(".", "", true); + test_dns_name_equal_one(".", ".", true); + test_dns_name_equal_one("..", "..", -EINVAL); +} + +static void test_dns_name_endswith_one(const char *a, const char *b, int ret) { + assert_se(dns_name_endswith(a, b) == ret); +} + +static void test_dns_name_endswith(void) { + test_dns_name_endswith_one("", "", true); + test_dns_name_endswith_one("", "xxx", false); + test_dns_name_endswith_one("xxx", "", true); + test_dns_name_endswith_one("x", "x", true); + test_dns_name_endswith_one("x", "y", false); + test_dns_name_endswith_one("x.y", "y", true); + test_dns_name_endswith_one("x.y", "Y", true); + test_dns_name_endswith_one("x.y", "x", false); + test_dns_name_endswith_one("x.y.z", "Z", true); + test_dns_name_endswith_one("x.y.z", "y.Z", true); + test_dns_name_endswith_one("x.y.z", "x.y.Z", true); + test_dns_name_endswith_one("x.y.z", "waldo", false); + test_dns_name_endswith_one("x.y.z.u.v.w", "y.z", false); + test_dns_name_endswith_one("x.y.z.u.v.w", "u.v.w", true); + test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL); +} + +static void test_dns_name_root(void) { + assert_se(dns_name_root("") == true); + assert_se(dns_name_root(".") == true); + assert_se(dns_name_root("xxx") == false); + assert_se(dns_name_root("xxx.") == false); + assert_se(dns_name_root("..") == -EINVAL); +} + +static void test_dns_name_single_label(void) { + assert_se(dns_name_single_label("") == false); + assert_se(dns_name_single_label(".") == false); + assert_se(dns_name_single_label("..") == -EINVAL); + assert_se(dns_name_single_label("x") == true); + assert_se(dns_name_single_label("x.") == true); + assert_se(dns_name_single_label("xx.yy") == false); +} + +static void test_dns_name_reverse_one(const char *address, const char *name) { + _cleanup_free_ char *p = NULL; + union in_addr_union a, b = {}; + int familya, familyb; + + assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0); + assert_se(dns_name_reverse(familya, &a, &p) >= 0); + assert_se(streq(p, name)); + assert_se(dns_name_address(p, &familyb, &b) > 0); + assert_se(familya == familyb); + assert_se(in_addr_equal(familya, &a, &b)); +} + +static void test_dns_name_reverse(void) { + test_dns_name_reverse_one("47.11.8.15", "15.8.11.47.in-addr.arpa"); + test_dns_name_reverse_one("fe80::47", "7.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa"); +} + +int main(int argc, char *argv[]) { + + test_dns_label_unescape(); + test_dns_label_escape(); + test_dns_name_normalize(); + test_dns_name_equal(); + test_dns_name_endswith(); + test_dns_name_root(); + test_dns_name_single_label(); + test_dns_name_reverse(); + + return 0; +}