chiark / gitweb /
resolved: various fixes regarding encoding of UTF8 characters in DNS RRs
[elogind.git] / src / resolve / resolved-dns-domain.c
index 94ffc0fac4abb37df7084cbd927634d082891db6..c2a435944ca5856ca438a79d6612b498031066d3 100644 (file)
@@ -45,6 +45,9 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                 if (sz <= 0)
                         return -ENOSPC;
 
+                if (r >= DNS_LABEL_MAX)
+                        return -EINVAL;
+
                 if (*n == '\\') {
                         /* Escaped character */
 
@@ -85,7 +88,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                         } else
                                 return -EINVAL;
 
-                } else if (*n >= ' ' && *n != 127) {
+                } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
 
                         /* Normal character */
                         *(d++) = *(n++);
@@ -114,6 +117,9 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
         assert(p);
         assert(ret);
 
+        if (l > DNS_LABEL_MAX)
+                return -EINVAL;
+
         s = malloc(l * 4 + 1);
         if (!s)
                 return -ENOMEM;
@@ -135,13 +141,13 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
 
                         /* Proper character */
                         *(q++) = *p;
-                } else if (*p >= ' ' && *p != 127) {
+                } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
 
                         /* Everything else */
                         *(q++) = '\\';
-                        *(q++) = '0' + (char) ((unsigned) *p / 100);
-                        *(q++) = '0' + (char) (((unsigned) *p / 10) % 10);
-                        *(q++) = '0' + (char) ((unsigned) *p % 10);
+                        *(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;
@@ -197,6 +203,9 @@ int dns_name_normalize(const char *s, char **_ret) {
                 n += r;
         }
 
+        if (n > DNS_NAME_MAX)
+                return -EINVAL;
+
         if (!GREEDY_REALLOC(ret, allocated, n + 1))
                 return -ENOMEM;
 
@@ -209,7 +218,7 @@ 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]) {
         const char *p = s;
-        unsigned long ul = 0;
+        unsigned long ul = hash_key[0];
         int r;
 
         assert(p);
@@ -224,7 +233,7 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
                 label[r] = 0;
                 ascii_strlower(label);
 
-                ul = hash_key[0] * ul + ul + string_hash_func(label, hash_key);
+                ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
         }
 
         return ul;
@@ -334,11 +343,15 @@ int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
         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.ip6.arpa",
-                             hexchar(p[15]), hexchar(p[14]), hexchar(p[13]), hexchar(p[12]),
-                             hexchar(p[11]), hexchar(p[10]), hexchar(p[ 9]), hexchar(p[ 8]),
-                             hexchar(p[ 7]), hexchar(p[ 6]), hexchar(p[ 5]), hexchar(p[ 4]),
-                             hexchar(p[ 3]), hexchar(p[ 2]), hexchar(p[ 1]), hexchar(p[ 0]));
+                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)
@@ -347,6 +360,93 @@ int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
         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;
@@ -369,7 +469,6 @@ int dns_name_single_label(const char *name) {
         r = dns_label_unescape(&name, label, sizeof(label));
         if (r < 0)
                 return r;
-
         if (r == 0)
                 return 0;