]> git.proxmox.com Git - systemd.git/blobdiff - src/shared/dns-domain.c
New upstream version 240
[systemd.git] / src / shared / dns-domain.c
index de2fcca8b235c7181175c64195ebeea99b769412..4b31cb36ede3790fc1cd760c1ad8ac9af37d561a 100644 (file)
@@ -1,6 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- ***/
 
 #if HAVE_LIBIDN2
 #  include <idn2.h>
@@ -19,6 +17,7 @@
 #include "dns-domain.h"
 #include "hashmap.h"
 #include "hexdecoct.h"
+#include "hostname-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
 #include "parse-util.h"
@@ -26,9 +25,9 @@
 #include "strv.h"
 #include "utf8.h"
 
-int dns_label_unescape(const char **name, char *dest, size_t sz) {
+int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
         const char *n;
-        char *d;
+        char *d, last_char = 0;
         int r = 0;
 
         assert(name);
@@ -38,13 +37,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
         d = dest;
 
         for (;;) {
-                if (*n == '.') {
-                        n++;
-                        break;
-                }
+                if (*n == 0 || *n == '.') {
+                        if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
+                                /* Trailing dash */
+                                return -EINVAL;
 
-                if (*n == 0)
+                        if (*n == '.')
+                                n++;
                         break;
+                }
 
                 if (r >= DNS_LABEL_MAX)
                         return -EINVAL;
@@ -54,6 +55,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
 
                 if (*n == '\\') {
                         /* Escaped character */
+                        if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
+                                return -EINVAL;
 
                         n++;
 
@@ -64,6 +67,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                         else if (IN_SET(*n, '\\', '.')) {
                                 /* Escaped backslash or dot */
 
+                                if (FLAGS_SET(flags, DNS_LABEL_LDH))
+                                        return -EINVAL;
+
+                                last_char = *n;
                                 if (d)
                                         *(d++) = *n;
                                 sz--;
@@ -92,6 +99,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                                 if (k > 255)
                                         return -EINVAL;
 
+                                if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
+                                    !valid_ldh_char((char) k))
+                                        return -EINVAL;
+
+                                last_char = (char) k;
                                 if (d)
                                         *(d++) = (char) k;
                                 sz--;
@@ -105,6 +117,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
 
                         /* Normal character */
 
+                        if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
+                                if (!valid_ldh_char(*n))
+                                        return -EINVAL;
+                                if (r == 0 && *n == '-')
+                                        /* Leading dash */
+                                        return -EINVAL;
+                        }
+
+                        last_char = *n;
                         if (d)
                                 *(d++) = *n;
                         sz--;
@@ -186,7 +207,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
                 terminal--;
         }
 
-        r = dns_label_unescape(&name, dest, sz);
+        r = dns_label_unescape(&name, dest, sz, 0);
         if (r < 0)
                 return r;
 
@@ -380,7 +401,7 @@ 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, char **_ret) {
+int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
         _cleanup_free_ char *ret = NULL;
         size_t n = 0, allocated = 0;
         const char *p;
@@ -397,7 +418,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
         for (;;) {
                 char label[DNS_LABEL_MAX];
 
-                r = dns_label_unescape(&p, label, sizeof(label));
+                r = dns_label_unescape(&p, label, sizeof label, flags);
                 if (r < 0)
                         return r;
                 if (r == 0) {
@@ -462,8 +483,7 @@ finish:
         return 0;
 }
 
-void dns_name_hash_func(const void *s, struct siphash *state) {
-        const char *p = s;
+void dns_name_hash_func(const char *p, struct siphash *state) {
         int r;
 
         assert(p);
@@ -471,7 +491,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
         for (;;) {
                 char label[DNS_LABEL_MAX+1];
 
-                r = dns_label_unescape(&p, label, sizeof(label));
+                r = dns_label_unescape(&p, label, sizeof label, 0);
                 if (r < 0)
                         break;
                 if (r == 0)
@@ -486,15 +506,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
         string_hash_func("", state);
 }
 
-int dns_name_compare_func(const void *a, const void *b) {
+int dns_name_compare_func(const char *a, const char *b) {
         const char *x, *y;
         int r, q;
 
         assert(a);
         assert(b);
 
-        x = (const char *) a + strlen(a);
-        y = (const char *) b + strlen(b);
+        x = a + strlen(a);
+        y = b + strlen(b);
 
         for (;;) {
                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
@@ -505,7 +525,7 @@ int dns_name_compare_func(const void *a, const void *b) {
                 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
                 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
                 if (r < 0 || q < 0)
-                        return r - q;
+                        return CMP(r, q);
 
                 r = ascii_strcasecmp_nn(la, r, lb, q);
                 if (r != 0)
@@ -513,10 +533,7 @@ int dns_name_compare_func(const void *a, const void *b) {
         }
 }
 
-const struct hash_ops dns_name_hash_ops = {
-        .hash = dns_name_hash_func,
-        .compare = dns_name_compare_func
-};
+DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
 
 int dns_name_equal(const char *x, const char *y) {
         int r, q;
@@ -527,11 +544,11 @@ int dns_name_equal(const char *x, const char *y) {
         for (;;) {
                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
 
-                r = dns_label_unescape(&x, la, sizeof(la));
+                r = dns_label_unescape(&x, la, sizeof la, 0);
                 if (r < 0)
                         return r;
 
-                q = dns_label_unescape(&y, lb, sizeof(lb));
+                q = dns_label_unescape(&y, lb, sizeof lb, 0);
                 if (q < 0)
                         return q;
 
@@ -558,14 +575,14 @@ int dns_name_endswith(const char *name, const char *suffix) {
         for (;;) {
                 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
 
-                r = dns_label_unescape(&n, ln, sizeof(ln));
+                r = dns_label_unescape(&n, ln, sizeof ln, 0);
                 if (r < 0)
                         return r;
 
                 if (!saved_n)
                         saved_n = n;
 
-                q = dns_label_unescape(&s, ls, sizeof(ls));
+                q = dns_label_unescape(&s, ls, sizeof ls, 0);
                 if (q < 0)
                         return q;
 
@@ -596,13 +613,13 @@ int dns_name_startswith(const char *name, const char *prefix) {
         for (;;) {
                 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
 
-                r = dns_label_unescape(&p, lp, sizeof(lp));
+                r = dns_label_unescape(&p, lp, sizeof lp, 0);
                 if (r < 0)
                         return r;
                 if (r == 0)
                         return true;
 
-                q = dns_label_unescape(&n, ln, sizeof(ln));
+                q = dns_label_unescape(&n, ln, sizeof ln, 0);
                 if (q < 0)
                         return q;
 
@@ -631,14 +648,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
                 if (!saved_before)
                         saved_before = n;
 
-                r = dns_label_unescape(&n, ln, sizeof(ln));
+                r = dns_label_unescape(&n, ln, sizeof ln, 0);
                 if (r < 0)
                         return r;
 
                 if (!saved_after)
                         saved_after = n;
 
-                q = dns_label_unescape(&s, ls, sizeof(ls));
+                q = dns_label_unescape(&s, ls, sizeof ls, 0);
                 if (q < 0)
                         return q;
 
@@ -661,7 +678,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
         /* Found it! Now generate the new name */
         prefix = strndupa(name, saved_before - name);
 
-        r = dns_name_concat(prefix, new_suffix, ret);
+        r = dns_name_concat(prefix, new_suffix, 0, ret);
         if (r < 0)
                 return r;
 
@@ -739,7 +756,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
                 for (i = 0; i < ELEMENTSOF(a); i++) {
                         char label[DNS_LABEL_MAX+1];
 
-                        r = dns_label_unescape(&p, label, sizeof(label));
+                        r = dns_label_unescape(&p, label, sizeof label, 0);
                         if (r < 0)
                                 return r;
                         if (r == 0)
@@ -776,7 +793,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
                         char label[DNS_LABEL_MAX+1];
                         int x, y;
 
-                        r = dns_label_unescape(&p, label, sizeof(label));
+                        r = dns_label_unescape(&p, label, sizeof label, 0);
                         if (r <= 0)
                                 return r;
                         if (r != 1)
@@ -785,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
                         if (x < 0)
                                 return -EINVAL;
 
-                        r = dns_label_unescape(&p, label, sizeof(label));
+                        r = dns_label_unescape(&p, label, sizeof label, 0);
                         if (r <= 0)
                                 return r;
                         if (r != 1)
@@ -853,7 +870,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
                  * dns_label_unescape() returns 0 when it hits the end
                  * of the domain name, which we rely on here to encode
                  * the trailing NUL byte. */
-                r = dns_label_unescape(&domain, (char *) out, len);
+                r = dns_label_unescape(&domain, (char *) out, len, 0);
                 if (r < 0)
                         return r;
 
@@ -920,7 +937,7 @@ bool dns_srv_type_is_valid(const char *name) {
 
                 /* This more or less implements RFC 6335, Section 5.1 */
 
-                r = dns_label_unescape(&name, label, sizeof(label));
+                r = dns_label_unescape(&name, label, sizeof label, 0);
                 if (r < 0)
                         return false;
                 if (r == 0)
@@ -980,7 +997,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
                 return -EINVAL;
 
         if (!name)
-                return dns_name_concat(type, domain, ret);
+                return dns_name_concat(type, domain, 0, ret);
 
         if (!dns_service_name_is_valid(name))
                 return -EINVAL;
@@ -989,11 +1006,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
         if (r < 0)
                 return r;
 
-        r = dns_name_concat(type, domain, &n);
+        r = dns_name_concat(type, domain, 0, &n);
         if (r < 0)
                 return r;
 
-        return dns_name_concat(escaped, n, ret);
+        return dns_name_concat(escaped, n, 0, ret);
 }
 
 static bool dns_service_name_label_is_valid(const char *label, size_t n) {
@@ -1018,7 +1035,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
         assert(joined);
 
         /* Get first label from the full name */
-        an = dns_label_unescape(&p, a, sizeof(a));
+        an = dns_label_unescape(&p, a, sizeof(a), 0);
         if (an < 0)
                 return an;
 
@@ -1026,7 +1043,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
                 x++;
 
                 /* If there was a first label, try to get the second one */
-                bn = dns_label_unescape(&p, b, sizeof(b));
+                bn = dns_label_unescape(&p, b, sizeof(b), 0);
                 if (bn < 0)
                         return bn;
 
@@ -1035,7 +1052,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
 
                         /* If there was a second label, try to get the third one */
                         q = p;
-                        cn = dns_label_unescape(&p, c, sizeof(c));
+                        cn = dns_label_unescape(&p, c, sizeof(c), 0);
                         if (cn < 0)
                                 return cn;
 
@@ -1085,7 +1102,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
         d = joined;
 
 finish:
-        r = dns_name_normalize(d, &domain);
+        r = dns_name_normalize(d, 0, &domain);
         if (r < 0)
                 return r;
 
@@ -1101,7 +1118,7 @@ finish:
         return 0;
 }
 
-static int dns_name_build_suffix_table(const char *name, const char*table[]) {
+static int dns_name_build_suffix_table(const char *name, const char *table[]) {
         const char *p;
         unsigned n = 0;
         int r;
@@ -1230,12 +1247,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
                 }
 
                 x = a_labels[n - 1 - k];
-                r = dns_label_unescape(&x, la, sizeof(la));
+                r = dns_label_unescape(&x, la, sizeof la, 0);
                 if (r < 0)
                         return r;
 
                 y = b_labels[m - 1 - k];
-                q = dns_label_unescape(&y, lb, sizeof(lb));
+                q = dns_label_unescape(&y, lb, sizeof lb, 0);
                 if (q < 0)
                         return q;
 
@@ -1286,7 +1303,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
 
         log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
         if (r == IDN2_2HYPHEN)
-                /* The name has two hypens — forbidden by IDNA2008 in some cases */
+                /* The name has two hyphens — forbidden by IDNA2008 in some cases */
                 return 0;
         if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
                 return -ENOSPC;
@@ -1303,13 +1320,13 @@ int dns_name_apply_idna(const char *name, char **ret) {
         for (;;) {
                 char label[DNS_LABEL_MAX];
 
-                r = dns_label_unescape(&name, label, sizeof(label));
+                r = dns_label_unescape(&name, label, sizeof label, 0);
                 if (r < 0)
                         return r;
                 if (r == 0)
                         break;
 
-                q = dns_label_apply_idna(label, r, label, sizeof(label));
+                q = dns_label_apply_idna(label, r, label, sizeof label);
                 if (q < 0)
                         return q;
                 if (q > 0)