mirror of
https://github.com/systemd/systemd.git
synced 2024-12-17 06:03:35 +08:00
resolved: handle IDNA domains
Make sure we format UTF-8 labels as IDNA when writing them to DNS packets, and as native UTF-8 when writing them to mDNS or LLMNR packets. When comparing or processing labels always consider native UTF-8 and IDNA formats equivalent.
This commit is contained in:
parent
afbc4f267b
commit
bdf10b5b4d
@ -4778,7 +4778,8 @@ systemd_resolved_LDADD = \
|
||||
libsystemd-label.la \
|
||||
libsystemd-internal.la \
|
||||
libsystemd-shared.la \
|
||||
-lm
|
||||
-lm \
|
||||
$(LIBIDN_LIBS)
|
||||
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-resolved
|
||||
@ -4829,7 +4830,8 @@ test_dns_domain_LDADD = \
|
||||
libsystemd-network.la \
|
||||
libsystemd-label.la \
|
||||
libsystemd-internal.la \
|
||||
libsystemd-shared.la
|
||||
libsystemd-shared.la \
|
||||
$(LIBIDN_LIBS)
|
||||
|
||||
libnss_resolve_la_SOURCES = \
|
||||
src/nss-resolve/nss-resolve.sym \
|
||||
@ -4867,7 +4869,8 @@ systemd_resolve_host_SOURCES = \
|
||||
systemd_resolve_host_LDADD = \
|
||||
libsystemd-internal.la \
|
||||
libsystemd-shared.la \
|
||||
-lm
|
||||
-lm \
|
||||
$(LIBIDN_LIBS)
|
||||
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-resolve-host
|
||||
|
16
configure.ac
16
configure.ac
@ -810,6 +810,21 @@ if test "x$enable_libcurl" != "xno"; then
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_LIBCURL, [test "$have_libcurl" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_libidn=no
|
||||
AC_ARG_ENABLE(libidn, AS_HELP_STRING([--disable-libidn], [Disable optional LIBIDN support]))
|
||||
if test "x$enable_libidn" != "xno"; then
|
||||
PKG_CHECK_MODULES(LIBIDN, [libidn],
|
||||
[AC_DEFINE(HAVE_LIBIDN, 1, [Define if libidn is available])
|
||||
have_libidn=yes
|
||||
M4_DEFINES="$M4_DEFINES -DHAVE_LIBIDN"],
|
||||
[have_libidn=no])
|
||||
if test "x$have_libidn" = "xno" -a "x$enable_libidn" = "xyes"; then
|
||||
AC_MSG_ERROR([*** libidn support requested but libraries not found])
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_LIBIDN, [test "$have_libidn" = "yes"])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
have_binfmt=no
|
||||
AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool]))
|
||||
@ -1326,6 +1341,7 @@ AC_MSG_RESULT([
|
||||
CHKCONFIG: ${have_chkconfig}
|
||||
GNUTLS: ${have_gnutls}
|
||||
libcurl: ${have_libcurl}
|
||||
libidn: ${have_libidn}
|
||||
ELFUTILS: ${have_elfutils}
|
||||
binfmt: ${have_binfmt}
|
||||
vconsole: ${have_vconsole}
|
||||
|
@ -19,6 +19,11 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_LIBIDN
|
||||
#include <idna.h>
|
||||
#include <stringprep.h>
|
||||
#endif
|
||||
|
||||
#include "resolved-dns-domain.h"
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
||||
@ -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;
|
||||
@ -176,6 +258,7 @@ int dns_name_normalize(const char *s, char **_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)
|
||||
@ -186,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;
|
||||
@ -227,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)
|
||||
return k;
|
||||
if (k > 0)
|
||||
r = k;
|
||||
|
||||
label[r] = 0;
|
||||
ascii_strlower(label);
|
||||
|
||||
@ -243,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);
|
||||
@ -259,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)
|
||||
@ -267,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);
|
||||
@ -282,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))
|
||||
@ -294,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);
|
||||
@ -308,6 +424,11 @@ 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;
|
||||
@ -315,6 +436,11 @@ int dns_name_endswith(const char *name, const char *suffix) {
|
||||
q = dns_label_unescape(&s, ls, sizeof(ls));
|
||||
if (r < 0)
|
||||
return r;
|
||||
w = dns_label_undo_idna(ls, r, ls, sizeof(ls));
|
||||
if (w < 0)
|
||||
return w;
|
||||
if (w > 0)
|
||||
q = w;
|
||||
|
||||
if (r == 0 && q == 0)
|
||||
return true;
|
||||
|
@ -30,6 +30,9 @@
|
||||
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]);
|
||||
|
@ -390,6 +390,7 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
char label[DNS_LABEL_MAX];
|
||||
size_t n;
|
||||
int k;
|
||||
|
||||
n = PTR_TO_SIZE(hashmap_get(p->names, name));
|
||||
if (n > 0) {
|
||||
@ -414,6 +415,17 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (p->protocol == DNS_PROTOCOL_DNS)
|
||||
k = dns_label_apply_idna(label, r, label, sizeof(label));
|
||||
else
|
||||
k = dns_label_undo_idna(label, r, label, sizeof(label));
|
||||
if (k < 0) {
|
||||
r = k;
|
||||
goto fail;
|
||||
}
|
||||
if (k > 0)
|
||||
r = k;
|
||||
|
||||
r = dns_packet_append_label(p, label, r, &n);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
Loading…
Reference in New Issue
Block a user