*/
static void
idn_ace_to_locale(const char *src, char **dst) {
- char *local_src;
+ char *local_src, *utf8_src;
int res;
- res = idn2_to_unicode_8zlz(src, &local_src,
- IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
+ /*
+ * We need to:
+ *
+ * 1) check whether 'src' 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 'src', 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 'src' to UTF-8, ignoring the current locale.
+ */
+ res = idn2_to_unicode_8z8z(src, &utf8_src, 0);
+ if (res != IDN2_OK) {
+ fatal("Bad ACE string '%s' (%s), use +noidnout",
+ src, idn2_strerror(res));
+ }
+
+ /*
+ * Then, check whether decoded 'src' 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",
src, idn2_strerror(res));
}
+ /*
+ * Finally, try converting the decoded 'src' into the current locale's
+ * character encoding.
+ */
+ res = idn2_to_unicode_8zlz(utf8_src, &local_src, 0);
+ if (res != IDN2_OK) {
+ fatal("Cannot represent '%s' in the current locale (%s), "
+ "use +noidnout or a different locale",
+ src, idn2_strerror(res));
+ }
+
+ /*
+ * Free the interim conversion result.
+ */
+ idn2_free(utf8_src);
+
*dst = local_src;
}
#endif /* HAVE_LIBIDN2 */
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.
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"
# 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"
- # 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 IDNA2008 names. Check whether dig rejects them to
- # ensure no IDNA2003 fallbacks are in place.
+ # Symbols are not valid IDNA2008 names. Check whether dig rejects them
+ # when they are supplied on the command line to ensure no IDNA2003
+ # fallbacks are in place.
#
# +noidnin: "dig" should send unicode octets to the server and display the
# returned qname in the same form.
#
# 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" "\226\136\154.com."
idna_test "$text" "+noidnin +idnout" "√.com" "\226\136\154.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 IDNA2008 names. Check whether dig rejects them
+ # when they are received in DNS responses to ensure no IDNA2003 fallbacks
+ # are in place.
+ #
+ # 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"
}