mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-18 18:59:15 -04:00
Improve error handling in idn_ace_to_locale()
While idn2_to_unicode_8zlz() takes a 'flags' argument, it is ignored and
thus cannot be used to perform IDN checks on the output string.
The bug in libidn2 versions before 2.0.5 was not that a call to
idn2_to_unicode_8zlz() with certain flags set did not cause IDN checks
to be performed. The bug was that idn2_to_unicode_8zlz() did not check
whether a conversion can be performed between UTF-8 and the current
locale's character encoding. In other words, with libidn2 version
2.0.5+, if the current locale's character encoding is ASCII, then
idn2_to_unicode_8zlz() will fail when it is passed any Punycode string
which decodes to a non-ASCII string, even if it is a valid IDNA2008
name.
Rework idn_ace_to_locale() so that invalid IDNA2008 names are properly
and consistently detected for all libidn2 versions and locales.
Update the "idna" system test accordingly. Add checks for processing a
server response containing Punycode which decodes to an invalid IDNA2008
name. Fix invalid subtest description.
(cherry picked from commit b896fc4972)
This commit is contained in:
parent
4c7eea4437
commit
7fe0f00a3b
2 changed files with 85 additions and 70 deletions
|
|
@ -4389,26 +4389,70 @@ idn_locale_to_ace(const char *from, char *to, size_t tolen) {
|
|||
static isc_result_t
|
||||
idn_ace_to_locale(const char *from, char *to, size_t tolen) {
|
||||
int res;
|
||||
char *tmp_str = NULL;
|
||||
char *utf8_src, *tmp_str = NULL;
|
||||
|
||||
res = idn2_to_unicode_8zlz(from, &tmp_str,
|
||||
IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT);
|
||||
|
||||
if (res == IDN2_OK) {
|
||||
/* check the length */
|
||||
if (strlen(tmp_str) >= tolen) {
|
||||
debug("encoded ASC string is too long");
|
||||
idn2_free(tmp_str);
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
(void) strlcpy(to, tmp_str, tolen);
|
||||
idn2_free(tmp_str);
|
||||
return ISC_R_SUCCESS;
|
||||
/*
|
||||
* We need to:
|
||||
*
|
||||
* 1) check whether 'from' is a valid IDNA2008 name,
|
||||
* 2) if it is, output it in the current locale's character encoding.
|
||||
*
|
||||
* Unlike idn2_to_ascii_*(), idn2_to_unicode_*() functions are unable
|
||||
* to perform IDNA2008 validity checks. Thus, we need to decode any
|
||||
* Punycode in 'from', check if the resulting name is a valid IDNA2008
|
||||
* name, and only once we ensure it is, output that name in the current
|
||||
* locale's character encoding.
|
||||
*
|
||||
* We could just use idn2_to_unicode_8zlz() + idn2_to_ascii_lz(), but
|
||||
* then we would not be able to universally tell invalid names and
|
||||
* character encoding errors apart (if the current locale uses ASCII
|
||||
* for character encoding, the former function would fail even for a
|
||||
* valid IDNA2008 name, as long as it contained any non-ASCII
|
||||
* character). Thus, we need to take a longer route.
|
||||
*
|
||||
* First, convert 'from' to UTF-8, ignoring the current locale.
|
||||
*/
|
||||
res = idn2_to_unicode_8z8z(from, &utf8_src, 0);
|
||||
if (res != IDN2_OK) {
|
||||
fatal("Bad ACE string '%s' (%s), use +noidnout",
|
||||
from, idn2_strerror(res));
|
||||
}
|
||||
|
||||
fatal("'%s' is not a legal IDN name (%s), use +noidnout", from, idn2_strerror(res));
|
||||
return ISC_R_FAILURE;
|
||||
/*
|
||||
* Then, check whether decoded 'from' is a valid IDNA2008 name.
|
||||
*/
|
||||
res = idn2_to_ascii_8z(utf8_src, NULL, IDN2_NONTRANSITIONAL);
|
||||
if (res != IDN2_OK) {
|
||||
fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout",
|
||||
from, idn2_strerror(res));
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, try converting the decoded 'from' into the current locale's
|
||||
* character encoding.
|
||||
*/
|
||||
res = idn2_to_unicode_8zlz(utf8_src, &tmp_str, 0);
|
||||
if (res != IDN2_OK) {
|
||||
fatal("Cannot represent '%s' in the current locale (%s), "
|
||||
"use +noidnout or a different locale",
|
||||
from, idn2_strerror(res));
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the interim conversion result.
|
||||
*/
|
||||
idn2_free(utf8_src);
|
||||
|
||||
/* check the length */
|
||||
if (strlen(tmp_str) >= tolen) {
|
||||
debug("encoded ASC string is too long");
|
||||
idn2_free(tmp_str);
|
||||
return (ISC_R_FAILURE);
|
||||
}
|
||||
|
||||
(void) strlcpy(to, tmp_str, tolen);
|
||||
idn2_free(tmp_str);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
#endif /* WITH_IDN_OUT_SUPPORT */
|
||||
#endif /* WITH_LIBIDN2 */
|
||||
|
|
|
|||
|
|
@ -136,46 +136,6 @@ idna_fail() {
|
|||
status=`expr $status + $ret`
|
||||
}
|
||||
|
||||
# Check if current version of libidn2 is >= a given version
|
||||
#
|
||||
# This requires that:
|
||||
# a) "pkg-config" exists on the system
|
||||
# b) The libidn2 installed has an associated ".pc" file
|
||||
# c) The system sort command supports "-V"
|
||||
#
|
||||
# $1 - Minimum version required
|
||||
#
|
||||
# Returns:
|
||||
# 0 - Version check is OK, libidn2 at required version or greater.
|
||||
# 1 - Version check was made, but libidn2 not at required version.
|
||||
# 2 - Could not carry out version check
|
||||
|
||||
libidn_version_check() {
|
||||
ret=2
|
||||
if [ -n "`command -v pkg-config`" ]; then
|
||||
version=`pkg-config --modversion --silence-errors libidn2`
|
||||
if [ -n "$version" ]; then
|
||||
# Does the sort command have a "-V" flag on this system?
|
||||
sort -V 2>&1 > /dev/null << .
|
||||
.
|
||||
if [ $? -eq 0 ]; then
|
||||
# Sort -V exists. Sort the IDN version and the minimum version
|
||||
# required. If the IDN version is greater than or equal to that
|
||||
# version, it will appear last in the list.
|
||||
last_version=`printf "%s\n" $version $1 | sort -V | tail -1`
|
||||
if [ "$version" = "$last_version" ]; then
|
||||
ret=0
|
||||
else
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
|
||||
# Function to check that case is preserved for an all-ASCII label.
|
||||
#
|
||||
# Without IDNA support, case-preservation is the expected behavior.
|
||||
|
|
@ -310,16 +270,7 @@ idna_enabled_test() {
|
|||
text="Checking fake A-label"
|
||||
idna_fail "$text" "" "xn--ahahah"
|
||||
idna_test "$text" "+noidnin +noidnout" "xn--ahahah" "xn--ahahah."
|
||||
|
||||
# Owing to issues with libdns, the next test will fail for versions of
|
||||
# libidn earlier than 2.0.5. For this reason, get the version (if
|
||||
# available) and compare with 2.0.5.
|
||||
libidn_version_check 2.0.5
|
||||
if [ $? -ne 0 ]; then
|
||||
echo_i "Skipping fake A-label +noidnin +idnout test (libidn2 version issues)"
|
||||
else
|
||||
idna_test "$text" "+noidnin +idnout" "xn--ahahah" "xn--ahahah."
|
||||
fi
|
||||
idna_fail "$text" "+noidnin +idnout" "xn--ahahah"
|
||||
idna_fail "$text" "+idnin +noidnout" "xn--ahahah"
|
||||
idna_fail "$text" "+idnin +idnout" "xn--ahahah"
|
||||
|
||||
|
|
@ -327,7 +278,7 @@ idna_enabled_test() {
|
|||
# BIND rejects such labels: with +idnin
|
||||
|
||||
label="xn--xflod18hstflod18hstflod18hstflod18hstflod18hstflod18-1iejjjj"
|
||||
text="Checking punycode label shorter than minimum valid length"
|
||||
text="Checking punycode label longer than maximum valid length"
|
||||
idna_fail "$text" "" "$label"
|
||||
idna_fail "$text" "+noidnin +noidnout" "$label"
|
||||
idna_fail "$text" "+noidnin +idnout" "$label"
|
||||
|
|
@ -337,7 +288,7 @@ idna_enabled_test() {
|
|||
|
||||
|
||||
|
||||
# Tests of a valid unicode string but an invalid U-label
|
||||
# Tests of a valid unicode string but an invalid U-label (input)
|
||||
#
|
||||
# Symbols are not valid IDNA names.
|
||||
#
|
||||
|
|
@ -347,12 +298,32 @@ idna_enabled_test() {
|
|||
#
|
||||
# The +[no]idnout options should not have any effect on the test.
|
||||
|
||||
text="Checking invalid U-label"
|
||||
text="Checking invalid input U-label"
|
||||
idna_fail "$text" "" "🧦.com"
|
||||
idna_test "$text" "+noidnin +noidnout" "🧦.com" "\240\159\167\166.com."
|
||||
idna_test "$text" "+noidnin +idnout" "🧦.com" "\240\159\167\166.com."
|
||||
idna_fail "$text" "+idnin +noidnout" "🧦.com"
|
||||
idna_fail "$text" "+idnin +idnout" "🧦.com"
|
||||
|
||||
# Tests of a valid unicode string but an invalid U-label (output)
|
||||
#
|
||||
# Symbols are not valid IDNA names.
|
||||
#
|
||||
# Note that an invalid U-label is accepted even when +idnin is in effect
|
||||
# because "xn--19g" is valid Punycode.
|
||||
#
|
||||
# +noidnout: "dig" should send the ACE string to the server and display the
|
||||
# returned qname.
|
||||
# +idnout: "dig" should generate an error.
|
||||
#
|
||||
# The +[no]idnin options should not have any effect on the test.
|
||||
|
||||
text="Checking invalid output U-label"
|
||||
idna_fail "$text" "" "xn--19g"
|
||||
idna_test "$text" "+noidnin +noidnout" "xn--19g" "xn--19g."
|
||||
idna_fail "$text" "+noidnin +idnout" "xn--19g"
|
||||
idna_test "$text" "+idnin +noidnout" "xn--19g" "xn--19g."
|
||||
idna_fail "$text" "+idnin +idnout" "xn--19g"
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue