X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-domain.c;h=8ed1ecf0a480cdebac763faab5b4094cfe743490;hp=eea73f6d5476851a1cfa8448e2782ccc7f9f0728;hb=be754d5443e5fe44ead733ad509b127409cdb0f0;hpb=322345fdb9865ef2477fba8e4bdde0e1183ef505 diff --git a/src/resolve/resolved-dns-domain.c b/src/resolve/resolved-dns-domain.c index eea73f6d5..8ed1ecf0a 100644 --- a/src/resolve/resolved-dns-domain.c +++ b/src/resolve/resolved-dns-domain.c @@ -19,6 +19,11 @@ along with systemd; If not, see . ***/ +#ifdef HAVE_LIBIDN +#include +#include +#endif + #include "resolved-dns-domain.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { @@ -88,7 +93,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++); @@ -141,13 +146,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; @@ -164,6 +169,83 @@ int dns_label_escape(const char *p, size_t l, char **ret) { 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; @@ -172,11 +254,11 @@ int dns_name_normalize(const char *s, char **_ret) { int r; assert(s); - assert(_ret); for (;;) { _cleanup_free_ char *t = NULL; char label[DNS_LABEL_MAX]; + int k; r = dns_label_unescape(&p, label, sizeof(label)); if (r < 0) @@ -187,6 +269,12 @@ int dns_name_normalize(const char *s, char **_ret) { 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; @@ -210,8 +298,11 @@ int dns_name_normalize(const char *s, char **_ret) { return -ENOMEM; ret[n] = 0; - *_ret = ret; - ret = NULL; + + if (_ret) { + *_ret = ret; + ret = NULL; + } return 0; } @@ -225,11 +316,18 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_ 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); @@ -241,7 +339,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) { const char *x = a, *y = b; - int r, q; + int r, q, k, w; assert(a); assert(b); @@ -257,6 +355,15 @@ int dns_name_compare_func(const void *a, const void *b) { 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) @@ -265,7 +372,7 @@ int dns_name_compare_func(const void *a, const void *b) { } int dns_name_equal(const char *x, const char *y) { - int r, q; + int r, q, k, w; assert(x); assert(y); @@ -280,9 +387,20 @@ int dns_name_equal(const char *x, const char *y) { 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)) @@ -292,7 +410,7 @@ int dns_name_equal(const char *x, const char *y) { int dns_name_endswith(const char *name, const char *suffix) { const char *n, *s, *saved_n = NULL; - int r, q; + int r, q, k, w; assert(name); assert(suffix); @@ -306,13 +424,23 @@ int dns_name_endswith(const char *name, const char *suffix) { 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 (r < 0) - return r; + 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; @@ -360,6 +488,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; @@ -382,7 +597,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;