From a0571d385184de9de97eb0c381656aa0b600a399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 01/11] Rework libidn2 detection Clean up the parts of configure.in responsible for handling libidn2 detection and adjust other pieces of the build system to match these cleanups: - use pkg-config when --with-libidn2 is used without an explicit path, - look for idn2_to_ascii_lz() rather than idn2_to_ascii_8z() as the former is used in BIND while the latter is not, - do not look for idn2_to_unicode_8zlz() as it is present in all libidn2 versions which have idn2_to_ascii_lz(), - check whether the header is usable, - set LDFLAGS in the Makefile for dig so that, if specified, the requested libidn2 path is used when linking with libidn2, - override CPPFLAGS when looking for libidn2 components so that the configure script does not produce warnings when libidn2 is not installed system-wide, - merge the AS_CASE() call into the AS_IF() call below it to simplify code, - indicate the default value of --with-libidn2 in "./configure --help" output, - use $with_libidn2 rather than $use_libidn2 to better match the name of the configure script argument, - stop differentiating between IDN "in" and "out" support, i.e. make dig either support libidn2 or not; remove WITH_* Autoconf macros and use a new one, HAVE_LIBIDN2, to determine whether libidn2 support should be enabled. --- bin/dig/Makefile.in | 2 + bin/dig/dig.c | 4 +- bin/dig/dighost.c | 62 +++------ bin/tests/system/feature-test.c | 2 +- config.h.in | 15 +-- configure | 219 ++++++++++++++++++++++---------- configure.in | 63 +++++---- 7 files changed, 214 insertions(+), 153 deletions(-) diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in index 69d540ffe8..62cd72361e 100644 --- a/bin/dig/Makefile.in +++ b/bin/dig/Makefile.in @@ -64,6 +64,8 @@ MANOBJS = ${MANPAGES} ${HTMLPAGES} @BIND9_MAKE_RULES@ +LDFLAGS = @LIBIDN2_LDFLAGS@ + dig@EXEEXT@: dig.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS} export BASEOBJS="dig.@O@ dighost.@O@ ${UOBJS}"; \ export LIBS0="${DNSLIBS} ${IRSLIBS}"; \ diff --git a/bin/dig/dig.c b/bin/dig/dig.c index 320d2490d1..5b4e8a78c2 100644 --- a/bin/dig/dig.c +++ b/bin/dig/dig.c @@ -1051,7 +1051,7 @@ plus_option(char *option, isc_boolean_t is_batchfile, switch (cmd[3]) { case 'i': FULLCHECK("idnin"); -#ifndef WITH_IDN_SUPPORT +#ifndef HAVE_LIBIDN2 fprintf(stderr, ";; IDN input support" " not enabled\n"); #else @@ -1060,7 +1060,7 @@ plus_option(char *option, isc_boolean_t is_batchfile, break; case 'o': FULLCHECK("idnout"); -#ifndef WITH_IDN_OUT_SUPPORT +#ifndef HAVE_LIBIDN2 fprintf(stderr, ";; IDN output support" " not enabled\n"); #else diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 5928392a67..5c447f60f4 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -30,12 +30,9 @@ #include #endif -#ifdef WITH_IDN_SUPPORT - -#ifdef WITH_LIBIDN2 +#ifdef HAVE_LIBIDN2 #include -#endif -#endif /* WITH_IDN_SUPPORT */ +#endif /* HAVE_LIBIDN2 */ #include #include @@ -135,14 +132,11 @@ int lookup_counter = 0; static char servercookie[256]; -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 static void idn_initialize(void); static isc_result_t idn_locale_to_ace(const char *from, char *to, size_t tolen); -#endif /* WITH_IDN_SUPPORT */ - -#ifdef WITH_IDN_OUT_SUPPORT static isc_result_t idn_ace_to_locale(const char *from, char *to, size_t tolen); @@ -150,8 +144,7 @@ static isc_result_t output_filter(isc_buffer_t *buffer, unsigned int used_org, isc_boolean_t absolute); #define MAXDLEN 256 - -#endif /* WITH_IDN_OUT_SUPPORT */ +#endif /* HAVE_LIBIDN2 */ isc_socket_t *keep = NULL; isc_sockaddr_t keepaddr; @@ -638,16 +631,13 @@ make_empty_lookup(void) { looknew->ttlunits = ISC_FALSE; looknew->ttlunits = ISC_FALSE; looknew->qr = ISC_FALSE; -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 looknew->idnin = ISC_TRUE; -#else - looknew->idnin = ISC_FALSE; -#endif -#ifdef WITH_IDN_OUT_SUPPORT looknew->idnout = ISC_TRUE; #else + looknew->idnin = ISC_FALSE; looknew->idnout = ISC_FALSE; -#endif +#endif /* HAVE_LIBIDN2 */ looknew->udpsize = 0; looknew->edns = -1; looknew->recurse = ISC_TRUE; @@ -1301,15 +1291,13 @@ setup_system(isc_boolean_t ipv4only, isc_boolean_t ipv6only) { (void)setlocale(LC_ALL, ""); #endif -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 idn_initialize(); -#endif -#ifdef WITH_IDN_OUT_SUPPORT /* Set domain name -> text post-conversion filter. */ result = dns_name_settotextfilter(output_filter); check_result(result, "dns_name_settotextfilter"); -#endif +#endif /* HAVE_LIBIDN2 */ if (keyfile[0] != 0) setup_file_key(); @@ -2033,15 +2021,13 @@ setup_lookup(dig_lookup_t *lookup) { char cookiebuf[256]; char *origin = NULL; char *textname = NULL; -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 char idn_origin[MXNAME], idn_textname[MXNAME]; -#endif -#ifdef WITH_IDN_OUT_SUPPORT result = dns_name_settotextfilter(lookup->idnout ? output_filter : NULL); check_result(result, "dns_name_settotextfilter"); -#endif +#endif /* HAVE_LIBIDN2 */ REQUIRE(lookup != NULL); INSIST(!free_now); @@ -2076,14 +2062,14 @@ setup_lookup(dig_lookup_t *lookup) { * TLD. */ textname = lookup->textname; -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 if (lookup->idnin) { result = idn_locale_to_ace(textname, idn_textname, sizeof(idn_textname)); check_result(result, "convert textname to IDN encoding"); debug("idn_textname: %s", idn_textname); textname = idn_textname; } -#endif +#endif /* HAVE_LIBIDN2 */ /* * If the name has too many dots, force the origin to be NULL @@ -2112,14 +2098,14 @@ setup_lookup(dig_lookup_t *lookup) { dns_name_init(lookup->oname, NULL); /* XXX Helper funct to conv char* to name? */ origin = lookup->origin->origin; -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 if (lookup->idnin) { result = idn_locale_to_ace(origin, idn_origin, sizeof(idn_origin)); check_result(result, "convert origin to IDN encoding"); debug("trying idn origin %s", idn_origin); origin = idn_origin; } -#endif +#endif /* HAVE_LIBIDN2 */ len = (unsigned int) strlen(origin); isc_buffer_init(&b, origin, len); isc_buffer_add(&b, len); @@ -4144,9 +4130,9 @@ cancel_all(void) { */ void destroy_libs(void) { -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 isc_result_t result; -#endif +#endif /* HAVE_LIBIDN2 */ if (keep != NULL) isc_socket_detach(&keep); @@ -4178,10 +4164,10 @@ destroy_libs(void) { clear_searchlist(); -#ifdef WITH_IDN_SUPPORT +#ifdef HAVE_LIBIDN2 result = dns_name_settotextfilter(NULL); check_result(result, "dns_name_settotextfilter"); -#endif +#endif /* HAVE_LIBIDN2 */ dns_name_destroy(); if (commctx != NULL) { @@ -4221,7 +4207,7 @@ destroy_libs(void) { isc_mem_destroy(&mctx); } -#ifdef WITH_IDN_OUT_SUPPORT +#ifdef HAVE_LIBIDN2 static isc_result_t output_filter(isc_buffer_t *buffer, unsigned int used_org, isc_boolean_t absolute) @@ -4274,10 +4260,7 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org, return (ISC_R_SUCCESS); } -#endif -#ifdef WITH_IDN_SUPPORT -#ifdef WITH_LIBIDN2 static void idn_initialize(void) { } @@ -4325,7 +4308,6 @@ idn_locale_to_ace(const char *from, char *to, size_t tolen) { return ISC_R_FAILURE; } -#ifdef WITH_IDN_OUT_SUPPORT static isc_result_t idn_ace_to_locale(const char *from, char *to, size_t tolen) { int res; @@ -4350,6 +4332,4 @@ idn_ace_to_locale(const char *from, char *to, size_t tolen) { fatal("'%s' is not a legal IDN name (%s), use +noidnout", from, idn2_strerror(res)); return ISC_R_FAILURE; } -#endif /* WITH_IDN_OUT_SUPPORT */ -#endif /* WITH_LIBIDN2 */ -#endif /* WITH_IDN_SUPPORT */ +#endif /* HAVE_LIBIDN2 */ diff --git a/bin/tests/system/feature-test.c b/bin/tests/system/feature-test.c index 0c20930cc4..f95ce4742a 100644 --- a/bin/tests/system/feature-test.c +++ b/bin/tests/system/feature-test.c @@ -153,7 +153,7 @@ main(int argc, char **argv) { } if (strcmp(argv[1], "--with-idn") == 0) { -#ifdef WITH_LIBIDN2 +#ifdef HAVE_LIBIDN2 return (0); #else return (1); diff --git a/config.h.in b/config.h.in index 94f5aab4d0..deee256e1b 100644 --- a/config.h.in +++ b/config.h.in @@ -293,6 +293,9 @@ int sigwait(const unsigned int *set, int *sig); /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_KRB5_H +/* Define to 1 if you have the header file. */ +#undef HAVE_IDN2_H + /* Define to 1 if you have the if_nametoindex function. */ #undef HAVE_IF_NAMETOINDEX @@ -323,6 +326,9 @@ int sigwait(const unsigned int *set, int *sig); /* Define to 1 if you have the `c_r' library (-lc_r). */ #undef HAVE_LIBC_R +/* Define if libidn2 was found */ +#undef HAVE_LIBIDN2 + /* Define to 1 if you have the `nsl' library (-lnsl). */ #undef HAVE_LIBNSL @@ -620,15 +626,6 @@ int sigwait(const unsigned int *set, int *sig); /* Define to enable very verbose query trace logging. */ #undef WANT_QUERYTRACE -/* define if IDN output support is to be included. */ -#undef WITH_IDN_OUT_SUPPORT - -/* define if IDN input support is to be included. */ -#undef WITH_IDN_SUPPORT - -/* define if libidn2 support is to be included. */ -#undef WITH_LIBIDN2 - /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD diff --git a/configure b/configure index 4c48cb51c8..258b61b62f 100755 --- a/configure +++ b/configure @@ -676,6 +676,7 @@ ATFBUILD LD_WRAP_TESTS CMOCKA_LIBS CMOCKA_CFLAGS +LIBIDN2_LDFLAGS LIBIDN2_LIBS LIBIDN2_CFLAGS XSLT_DBLATEX_FASTBOOK @@ -1059,6 +1060,8 @@ CPP PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR +LIBIDN2_CFLAGS +LIBIDN2_LIBS CMOCKA_CFLAGS CMOCKA_LIBS' @@ -1762,7 +1765,8 @@ Optional Packages: --with-protobuf-c=path Path where protobuf-c is installed, for dnstap --with-libfstrm=path Path where libfstrm is installed, for dnstap --with-docbook-xsl=PATH specify path for Docbook-XSL stylesheets - --with-libidn2=PATH enable IDN support using GNU libidn2 [yes|no|path] + --with-libidn2=PATH enable IDN support using GNU libidn2 + [yes|no(default)|path] --with-cmocka=no enable cmocka based tests (default is no) --with-atf support Automated Test Framework --with-tuning=ARG Specify server tuning (large or default) @@ -1801,6 +1805,10 @@ Some influential environment variables: directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path + LIBIDN2_CFLAGS + C compiler flags for LIBIDN2, overriding pkg-config + LIBIDN2_LIBS + linker flags for LIBIDN2, overriding pkg-config CMOCKA_CFLAGS C compiler flags for CMOCKA, overriding pkg-config CMOCKA_LIBS linker flags for CMOCKA, overriding pkg-config @@ -22416,34 +22424,135 @@ LIBIDN2_LIBS= # Check whether --with-libidn2 was given. if test "${with_libidn2+set}" = set; then : - withval=$with_libidn2; use_libidn2="$withval" + withval=$with_libidn2; with_libidn2="$withval" else - use_libidn2="no" + with_libidn2="no" fi -case $use_libidn2 in #( - no) : - : ;; #( +case $with_libidn2 in #( yes) : - : ;; #( - *) : - LIBIDN2_CFLAGS="-I$use_libidn2/include" - LIBIDN2_LDFLAGS="-L$use_libidn2/lib" - ;; #( - *) : - ;; -esac +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libidn2" >&5 +$as_echo_n "checking for libidn2... " >&6; } -if test "$use_libidn2" != "no"; then : - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - save_LDFLAGS="$LDFLAGS" - CFLAGS="$LIBIDN2_CFLAGS $CFLAGS" - LDFLAGS="$LIBIDN2_LDFLAGS $LDFLAGS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing idn2_to_ascii_8z" >&5 -$as_echo_n "checking for library containing idn2_to_ascii_8z... " >&6; } -if ${ac_cv_search_idn2_to_ascii_8z+:} false; then : +if test -n "$LIBIDN2_CFLAGS"; then + pkg_cv_LIBIDN2_CFLAGS="$LIBIDN2_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libidn2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBIDN2_CFLAGS=`$PKG_CONFIG --cflags "libidn2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBIDN2_LIBS"; then + pkg_cv_LIBIDN2_LIBS="$LIBIDN2_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libidn2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBIDN2_LIBS=`$PKG_CONFIG --libs "libidn2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBIDN2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libidn2" 2>&1` + else + LIBIDN2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libidn2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBIDN2_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libidn2) were not met: + +$LIBIDN2_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables LIBIDN2_CFLAGS +and LIBIDN2_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables LIBIDN2_CFLAGS +and LIBIDN2_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see . +See \`config.log' for more details" "$LINENO" 5; } +else + LIBIDN2_CFLAGS=$pkg_cv_LIBIDN2_CFLAGS + LIBIDN2_LIBS=$pkg_cv_LIBIDN2_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_LIBIDN2 1" >>confdefs.h + +fi ;; #( + no) : + ;; #( + *) : + save_CFLAGS="$CFLAGS" + save_CPPFLAGS="$CPPFLAGS" + save_LDFLAGS="$LDFLAGS" + save_LIBS="$LIBS" + LIBIDN2_CFLAGS="-I$with_libidn2/include" + LIBIDN2_LDFLAGS="-L$with_libidn2/lib" + CFLAGS="$LIBIDN2_CFLAGS $CFLAGS" + CPPFLAGS="$LIBIDN2_CFLAGS $CPPFLAGS" + LDFLAGS="$LIBIDN2_LDFLAGS $LDFLAGS" + for ac_header in idn2.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "idn2.h" "ac_cv_header_idn2_h" "$ac_includes_default" +if test "x$ac_cv_header_idn2_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_IDN2_H 1 +_ACEOF + +else + as_fn_error $? "idn2.h not found" "$LINENO" 5 +fi + +done + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing idn2_to_ascii_lz" >&5 +$as_echo_n "checking for library containing idn2_to_ascii_lz... " >&6; } +if ${ac_cv_search_idn2_to_ascii_lz+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -22456,11 +22565,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char idn2_to_ascii_8z (); +char idn2_to_ascii_lz (); int main () { -return idn2_to_ascii_8z (); +return idn2_to_ascii_lz (); ; return 0; } @@ -22473,66 +22582,42 @@ for ac_lib in '' idn2; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_idn2_to_ascii_8z=$ac_res + ac_cv_search_idn2_to_ascii_lz=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_idn2_to_ascii_8z+:} false; then : + if ${ac_cv_search_idn2_to_ascii_lz+:} false; then : break fi done -if ${ac_cv_search_idn2_to_ascii_8z+:} false; then : +if ${ac_cv_search_idn2_to_ascii_lz+:} false; then : else - ac_cv_search_idn2_to_ascii_8z=no + ac_cv_search_idn2_to_ascii_lz=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_idn2_to_ascii_8z" >&5 -$as_echo "$ac_cv_search_idn2_to_ascii_8z" >&6; } -ac_res=$ac_cv_search_idn2_to_ascii_8z +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_idn2_to_ascii_lz" >&5 +$as_echo "$ac_cv_search_idn2_to_ascii_lz" >&6; } +ac_res=$ac_cv_search_idn2_to_ascii_lz if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + LIBIDN2_LIBS="$ac_cv_search_idn2_to_ascii_lz" -$as_echo "#define WITH_IDN_SUPPORT 1" >>confdefs.h +$as_echo "#define HAVE_LIBIDN2 1" >>confdefs.h - -$as_echo "#define WITH_LIBIDN2 1" >>confdefs.h - - LIBIDN2_LIBS="$LIBIDN2_LDFLAGS -lidn2" else as_fn_error $? "libidn2 requested, but not found" "$LINENO" 5 fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -idn2_to_unicode_8zlz(".", NULL, IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -$as_echo "#define WITH_IDN_OUT_SUPPORT 1" >>confdefs.h - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CFLAGS="$save_CFLAGS" - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" - -fi + CFLAGS="$save_CFLAGS" + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" ;; #( + *) : + ;; +esac @@ -26684,7 +26769,7 @@ report() { test "X$JSONSTATS" = "X" || echo " JSON statistics (--with-libjson)" test "X$ZLIB" = "X" || echo " HTTP zlib compression (--with-zlib)" test "X$NZD_TOOLS" = "X" || echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)" - test "no" = "$use_libidn2" || echo " IDN support (--with-libidn2)" + test "no" = "$with_libidn2" || echo " IDN support (--with-libidn2)" fi if test "no" != "$use_pkcs11"; then @@ -26781,7 +26866,7 @@ report() { test "X$JSONSTATS" = "X" && echo " JSON statistics (--with-libjson)" test "X$ZLIB" = "X" && echo " HTTP zlib compression (--with-zlib)" test "X$NZD_TOOLS" = "X" && echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)" - test "no" = "$use_libidn2" && echo " IDN support (--with-libidn2)" + test "no" = "$with_libidn2" && echo " IDN support (--with-libidn2)" echo "-------------------------------------------------------------------------------" echo "Configured paths:" diff --git a/configure.in b/configure.in index 7824531d2b..17506d2e9a 100644 --- a/configure.in +++ b/configure.in @@ -4521,38 +4521,35 @@ NOM_PATH_FILE(XSLT_DBLATEX_FASTBOOK, xsl/latex_book_fast.xsl, $dblatex_xsl_trees LIBIDN2_CFLAGS= LIBIDN2_LDFLAGS= LIBIDN2_LIBS= -AC_ARG_WITH(libidn2, - AS_HELP_STRING([--with-libidn2[=PATH]], [enable IDN support using GNU libidn2 [yes|no|path]]), - use_libidn2="$withval", use_libidn2="no") -AS_CASE([$use_libidn2], - [no],[:], - [yes],[:], - [*],[ - LIBIDN2_CFLAGS="-I$use_libidn2/include" - LIBIDN2_LDFLAGS="-L$use_libidn2/lib" - ]) - -AS_IF([test "$use_libidn2" != "no"], - [save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - save_LDFLAGS="$LDFLAGS" - CFLAGS="$LIBIDN2_CFLAGS $CFLAGS" - LDFLAGS="$LIBIDN2_LDFLAGS $LDFLAGS" - AC_SEARCH_LIBS([idn2_to_ascii_8z], [idn2], - [AC_DEFINE(WITH_IDN_SUPPORT, 1, [define if IDN input support is to be included.]) - AC_DEFINE(WITH_LIBIDN2, 1, [define if libidn2 support is to be included.]) - LIBIDN2_LIBS="$LIBIDN2_LDFLAGS -lidn2"], - [AC_MSG_ERROR([libidn2 requested, but not found])]) - AC_TRY_LINK([#include ], - [idn2_to_unicode_8zlz(".", NULL, IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT);], - [AC_MSG_RESULT(yes) - AC_DEFINE(WITH_IDN_OUT_SUPPORT, 1, [define if IDN output support is to be included.])], - [AC_MSG_RESULT([no])]) - CFLAGS="$save_CFLAGS" - LIBS="$save_LIBS" - LDFLAGS="$save_LDFLAGS" - ]) +AC_ARG_WITH([libidn2], + [AS_HELP_STRING([--with-libidn2[=PATH]], [enable IDN support using GNU libidn2 [yes|no(default)|path]])], + [with_libidn2="$withval"], [with_libidn2="no"]) +AS_CASE([$with_libidn2], + [yes], [PKG_CHECK_MODULES([LIBIDN2], [libidn2], + [AC_DEFINE([HAVE_LIBIDN2], [1], [Define if libidn2 was found])])], + [no], [], + [*], [save_CFLAGS="$CFLAGS" + save_CPPFLAGS="$CPPFLAGS" + save_LDFLAGS="$LDFLAGS" + save_LIBS="$LIBS" + LIBIDN2_CFLAGS="-I$with_libidn2/include" + LIBIDN2_LDFLAGS="-L$with_libidn2/lib" + CFLAGS="$LIBIDN2_CFLAGS $CFLAGS" + CPPFLAGS="$LIBIDN2_CFLAGS $CPPFLAGS" + LDFLAGS="$LIBIDN2_LDFLAGS $LDFLAGS" + AC_CHECK_HEADERS([idn2.h], + [], + [AC_MSG_ERROR([idn2.h not found])]) + AC_SEARCH_LIBS([idn2_to_ascii_lz], [idn2], + [LIBIDN2_LIBS="$ac_cv_search_idn2_to_ascii_lz" + AC_DEFINE([HAVE_LIBIDN2], [1], [Define if libidn2 was found])], + [AC_MSG_ERROR([libidn2 requested, but not found])]) + CFLAGS="$save_CFLAGS" + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS"]) AC_SUBST([LIBIDN2_CFLAGS]) +AC_SUBST([LIBIDN2_LDFLAGS]) AC_SUBST([LIBIDN2_LIBS]) # @@ -5313,7 +5310,7 @@ report() { test "X$JSONSTATS" = "X" || echo " JSON statistics (--with-libjson)" test "X$ZLIB" = "X" || echo " HTTP zlib compression (--with-zlib)" test "X$NZD_TOOLS" = "X" || echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)" - test "no" = "$use_libidn2" || echo " IDN support (--with-libidn2)" + test "no" = "$with_libidn2" || echo " IDN support (--with-libidn2)" fi if test "no" != "$use_pkcs11"; then @@ -5410,7 +5407,7 @@ report() { test "X$JSONSTATS" = "X" && echo " JSON statistics (--with-libjson)" test "X$ZLIB" = "X" && echo " HTTP zlib compression (--with-zlib)" test "X$NZD_TOOLS" = "X" && echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)" - test "no" = "$use_libidn2" && echo " IDN support (--with-libidn2)" + test "no" = "$with_libidn2" && echo " IDN support (--with-libidn2)" echo "-------------------------------------------------------------------------------" echo "Configured paths:" From fafc7c7b8a88085eff18c299d91b12ef31bdc764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 02/11] Remove empty idn_initialize() function --- bin/dig/dighost.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 5c447f60f4..c0ddf8c65a 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -133,7 +133,6 @@ int lookup_counter = 0; static char servercookie[256]; #ifdef HAVE_LIBIDN2 -static void idn_initialize(void); static isc_result_t idn_locale_to_ace(const char *from, char *to, size_t tolen); @@ -1292,8 +1291,6 @@ setup_system(isc_boolean_t ipv4only, isc_boolean_t ipv6only) { #endif #ifdef HAVE_LIBIDN2 - idn_initialize(); - /* Set domain name -> text post-conversion filter. */ result = dns_name_settotextfilter(output_filter); check_result(result, "dns_name_settotextfilter"); @@ -4261,10 +4258,6 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org, return (ISC_R_SUCCESS); } -static void -idn_initialize(void) { -} - static isc_result_t idn_locale_to_ace(const char *from, char *to, size_t tolen) { int res; From 9a25368c8c93208c65071b9f50f573cbd3318ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 03/11] Remove redundant dns_name_settotextfilter() call There is no need to call dns_name_settotextfilter() in setup_system() because setup_lookup() determines whether IDN output processing should be enabled for a specific lookup (taking the global setting into consideration) and calls dns_name_settotextfilter() anyway if it is. Remove the dns_name_settotextfilter() call from setup_system(). --- bin/dig/dighost.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index c0ddf8c65a..3d59b4a80f 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -1290,12 +1290,6 @@ setup_system(isc_boolean_t ipv4only, isc_boolean_t ipv6only) { (void)setlocale(LC_ALL, ""); #endif -#ifdef HAVE_LIBIDN2 - /* Set domain name -> text post-conversion filter. */ - result = dns_name_settotextfilter(output_filter); - check_result(result, "dns_name_settotextfilter"); -#endif /* HAVE_LIBIDN2 */ - if (keyfile[0] != 0) setup_file_key(); else if (keysecret[0] != 0) From 59cdaef4f70af346b431714b8d34185f485b9dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 04/11] Remove IDNA2003 fallback from dig Certain characters, like symbols, are allowed by IDNA2003, but not by IDNA2008. Make dig reject such symbols when IDN input processing is enabled to ensure BIND only supports IDNA2008. Update the "idna" system test so that it uses one of such symbols rather than one which is disallowed by both IDNA2003 and IDNA2008. --- bin/dig/dighost.c | 8 ++------ bin/tests/system/idna/tests.sh | 13 +++++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 3d59b4a80f..6d0c9b86e2 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -4258,10 +4258,6 @@ idn_locale_to_ace(const char *from, char *to, size_t tolen) { char *tmp_str = NULL; res = idn2_to_ascii_lz(from, &tmp_str, IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT); - if (res == IDN2_DISALLOWED) { - res = idn2_to_ascii_lz(from, &tmp_str, IDN2_TRANSITIONAL|IDN2_NFC_INPUT); - } - if (res == IDN2_OK) { /* * idn2_to_ascii_lz() normalizes all strings to lowerl case, @@ -4291,7 +4287,7 @@ idn_locale_to_ace(const char *from, char *to, size_t tolen) { return ISC_R_SUCCESS; } - fatal("'%s' is not a legal IDN name (%s), use +noidnin", from, idn2_strerror(res)); + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", from, idn2_strerror(res)); return ISC_R_FAILURE; } @@ -4316,7 +4312,7 @@ idn_ace_to_locale(const char *from, char *to, size_t tolen) { return ISC_R_SUCCESS; } - fatal("'%s' is not a legal IDN name (%s), use +noidnout", from, idn2_strerror(res)); + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout", from, idn2_strerror(res)); return ISC_R_FAILURE; } #endif /* HAVE_LIBIDN2 */ diff --git a/bin/tests/system/idna/tests.sh b/bin/tests/system/idna/tests.sh index 3a9b91b442..d7a968306a 100644 --- a/bin/tests/system/idna/tests.sh +++ b/bin/tests/system/idna/tests.sh @@ -339,7 +339,8 @@ idna_enabled_test() { # Tests of a valid unicode string but an invalid U-label # - # Symbols are not valid IDNA names. + # Symbols are not valid IDNA2008 names. Check whether dig rejects them 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. @@ -348,11 +349,11 @@ idna_enabled_test() { # The +[no]idnout options should not have any effect on the test. text="Checking invalid 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" + 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" } From bcf4d206033b909744af0d772dcd99f69de1e198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 05/11] Simplify idn_locale_to_ace() idn_locale_to_ace() is a static function which is always used with a buffer of size MXNAME, i.e. one that can fit any valid domain name. Since libidn2 detects invalid domain names and libidn2 errors are considered fatal, remove size checks from idn_locale_to_ace(). This makes returning an isc_result_t from it unnecessary. Do not process success cases in conditional branches for improved consistency with the rest of BIND source code. Add a comment explaining the purpose of idn_locale_to_ace(). Rename that function's parameters to match common BIND naming pattern. --- bin/dig/dighost.c | 74 ++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 6d0c9b86e2..30c3e109e8 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -133,9 +133,7 @@ int lookup_counter = 0; static char servercookie[256]; #ifdef HAVE_LIBIDN2 -static isc_result_t idn_locale_to_ace(const char *from, - char *to, - size_t tolen); +static void idn_locale_to_ace(const char *src, char *dst, size_t dstlen); static isc_result_t idn_ace_to_locale(const char *from, char *to, size_t tolen); @@ -2055,8 +2053,7 @@ setup_lookup(dig_lookup_t *lookup) { textname = lookup->textname; #ifdef HAVE_LIBIDN2 if (lookup->idnin) { - result = idn_locale_to_ace(textname, idn_textname, sizeof(idn_textname)); - check_result(result, "convert textname to IDN encoding"); + idn_locale_to_ace(textname, idn_textname, sizeof(idn_textname)); debug("idn_textname: %s", idn_textname); textname = idn_textname; } @@ -2091,8 +2088,8 @@ setup_lookup(dig_lookup_t *lookup) { origin = lookup->origin->origin; #ifdef HAVE_LIBIDN2 if (lookup->idnin) { - result = idn_locale_to_ace(origin, idn_origin, sizeof(idn_origin)); - check_result(result, "convert origin to IDN encoding"); + idn_locale_to_ace(origin, idn_origin, + sizeof(idn_origin)); debug("trying idn origin %s", idn_origin); origin = idn_origin; } @@ -4252,43 +4249,40 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org, return (ISC_R_SUCCESS); } -static isc_result_t -idn_locale_to_ace(const char *from, char *to, size_t tolen) { +/*% + * Convert 'src', which is a string using the current locale's character + * encoding, into an ACE string suitable for use in the DNS, storing the + * conversion result in 'dst', which is 'dstlen' bytes large. + * + * 'dst' MUST be large enough to hold any valid domain name. + */ +static void +idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { + const char *final_src; + char *ascii_src; int res; - char *tmp_str = NULL; - res = idn2_to_ascii_lz(from, &tmp_str, IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT); - if (res == IDN2_OK) { - /* - * idn2_to_ascii_lz() normalizes all strings to lowerl case, - * but we generally don't want to lowercase all input strings; - * make sure to return the original case if the two strings - * differ only in case - */ - if (!strcasecmp(from, tmp_str)) { - if (strlen(from) >= tolen) { - debug("from string is too long"); - idn2_free(tmp_str); - return ISC_R_NOSPACE; - } - idn2_free(tmp_str); - (void) strlcpy(to, from, tolen); - return ISC_R_SUCCESS; - } - /* check the length */ - if (strlen(tmp_str) >= tolen) { - debug("ACE string is too long"); - idn2_free(tmp_str); - return ISC_R_NOSPACE; - } - - (void) strlcpy(to, tmp_str, tolen); - idn2_free(tmp_str); - return ISC_R_SUCCESS; + /* + * We trust libidn2 to return an error if 'src' is too large to be a + * valid domain name. + */ + res = idn2_to_ascii_lz(src, &ascii_src, + IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", + src, idn2_strerror(res)); } - fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", from, idn2_strerror(res)); - return ISC_R_FAILURE; + /* + * idn2_to_ascii_lz() normalizes all strings to lower case, but we + * generally don't want to lowercase all input strings; make sure to + * return the original case if the two strings differ only in case. + */ + final_src = (strcasecmp(src, ascii_src) == 0 ? src : ascii_src); + + (void)strlcpy(dst, final_src, dstlen); + + idn2_free(ascii_src); } static isc_result_t From 5106a18e9ee43543101358f4184329e15443d165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 06/11] Simplify idn_ace_to_locale() idn_ace_to_locale() may return a string longer than MAXDLEN because it is using the current locale's character encoding. Rather then imposing an arbitrary limit on the length of the string that function can return, make it pass the string prepared by libidn2 back to the caller verbatim, making the latter responsible for freeing that string. In conjunction with the fact that libidn2 errors are considered fatal, this makes returning an isc_result_t from idn_ace_to_locale() unnecessary. Do not process success cases in conditional branches for improved consistency with the rest of BIND source code. Add a comment explaining the purpose of idn_ace_to_locale(). Rename that function's parameters to match common BIND naming pattern. --- bin/dig/dighost.c | 53 +++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 30c3e109e8..75ca4239cc 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -134,9 +134,7 @@ static char servercookie[256]; #ifdef HAVE_LIBIDN2 static void idn_locale_to_ace(const char *src, char *dst, size_t dstlen); -static isc_result_t idn_ace_to_locale(const char *from, - char *to, - size_t tolen); +static void idn_ace_to_locale(const char *src, char **dst); static isc_result_t output_filter(isc_buffer_t *buffer, unsigned int used_org, isc_boolean_t absolute); @@ -4200,10 +4198,9 @@ static isc_result_t output_filter(isc_buffer_t *buffer, unsigned int used_org, isc_boolean_t absolute) { - char tmp1[MAXDLEN], tmp2[MAXDLEN]; + char tmp1[MAXDLEN], *tmp2; size_t fromlen, tolen; isc_boolean_t end_with_dot; - isc_result_t result; /* * Copy contents of 'buffer' to 'tmp1', supply trailing dot @@ -4227,10 +4224,8 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org, /* * Convert contents of 'tmp1' to local encoding. */ - result = idn_ace_to_locale(tmp1, tmp2, sizeof(tmp2)); - if (result != ISC_R_SUCCESS) { - return (ISC_R_SUCCESS); - } + idn_ace_to_locale(tmp1, &tmp2); + /* * Copy the converted contents in 'tmp1' back to 'buffer'. * If we have appended trailing dot, remove it. @@ -4239,12 +4234,15 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org, if (absolute && !end_with_dot && tmp2[tolen - 1] == '.') tolen--; - if (isc_buffer_length(buffer) < used_org + tolen) + if (isc_buffer_length(buffer) < used_org + tolen) { + idn2_free(tmp2); return (ISC_R_NOSPACE); + } isc_buffer_subtract(buffer, isc_buffer_usedlength(buffer) - used_org); memmove(isc_buffer_used(buffer), tmp2, tolen); isc_buffer_add(buffer, (unsigned int)tolen); + idn2_free(tmp2); return (ISC_R_SUCCESS); } @@ -4285,28 +4283,25 @@ idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { idn2_free(ascii_src); } -static isc_result_t -idn_ace_to_locale(const char *from, char *to, size_t tolen) { +/*% + * Convert 'src', which is an ACE string suitable for use in the DNS, into a + * string using the current locale's character encoding, storing the conversion + * result in 'dst'. + * + * The caller MUST subsequently release 'dst' using idn2_free(). + */ +static void +idn_ace_to_locale(const char *src, char **dst) { + char *local_src; int res; - char *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; + res = idn2_to_unicode_8zlz(src, &local_src, + IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout", + src, idn2_strerror(res)); } - fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout", from, idn2_strerror(res)); - return ISC_R_FAILURE; + *dst = local_src; } #endif /* HAVE_LIBIDN2 */ From 19c42d46e87c68275fbe99dd3c538e6812742de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 07/11] Simplify and rename output_filter() output_filter() does not need to dot-terminate its input name because libidn2 properly handles both dot-terminated and non-dot-terminated names. libidn2 also does not implicitly dot-terminate names passed to it, so parts of output_filter() handling dot termination can simply be removed. Fix a logical condition to make sure 'src' can fit the terminating NULL byte. Replace the MAXDLEN macro with the MXNAME macro used in the rest of dig source code. Tweak comments and variable names. Rename output_filter() to idn_output_filter() so that it can be easily associated with IDN and other idn_*() functions. --- bin/dig/dighost.c | 71 ++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 75ca4239cc..4865ac7e94 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -135,10 +135,9 @@ static char servercookie[256]; #ifdef HAVE_LIBIDN2 static void idn_locale_to_ace(const char *src, char *dst, size_t dstlen); static void idn_ace_to_locale(const char *src, char **dst); -static isc_result_t output_filter(isc_buffer_t *buffer, - unsigned int used_org, - isc_boolean_t absolute); -#define MAXDLEN 256 +static isc_result_t idn_output_filter(isc_buffer_t *buffer, + unsigned int used_org, + isc_boolean_t absolute); #endif /* HAVE_LIBIDN2 */ isc_socket_t *keep = NULL; @@ -2012,7 +2011,7 @@ setup_lookup(dig_lookup_t *lookup) { char idn_origin[MXNAME], idn_textname[MXNAME]; result = dns_name_settotextfilter(lookup->idnout ? - output_filter : NULL); + idn_output_filter : NULL); check_result(result, "dns_name_settotextfilter"); #endif /* HAVE_LIBIDN2 */ @@ -4195,54 +4194,50 @@ destroy_libs(void) { #ifdef HAVE_LIBIDN2 static isc_result_t -output_filter(isc_buffer_t *buffer, unsigned int used_org, - isc_boolean_t absolute) +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org, + isc_boolean_t absolute) { - char tmp1[MAXDLEN], *tmp2; - size_t fromlen, tolen; - isc_boolean_t end_with_dot; + char src[MXNAME], *dst; + size_t srclen, dstlen; + + UNUSED(absolute); /* - * Copy contents of 'buffer' to 'tmp1', supply trailing dot - * if 'absolute' is true, and terminate with NUL. + * Copy name from 'buffer' to 'src' and terminate it with NULL. */ - fromlen = isc_buffer_usedlength(buffer) - used_org; - if (fromlen >= MAXDLEN) + srclen = isc_buffer_usedlength(buffer) - used_org; + if (srclen > sizeof(src)) { + warn("Input name too long to perform IDN conversion"); return (ISC_R_SUCCESS); - - memmove(tmp1, (char *)isc_buffer_base(buffer) + used_org, fromlen); - end_with_dot = (tmp1[fromlen - 1] == '.') ? ISC_TRUE : ISC_FALSE; - if (absolute && !end_with_dot) { - fromlen++; - if (fromlen >= MAXDLEN) - return (ISC_R_SUCCESS); - tmp1[fromlen - 1] = '.'; } - - tmp1[fromlen] = '\0'; + memmove(src, (char *)isc_buffer_base(buffer) + used_org, srclen); + src[srclen] = '\0'; /* - * Convert contents of 'tmp1' to local encoding. + * Convert 'src' to the current locale's character encoding. */ - idn_ace_to_locale(tmp1, &tmp2); + idn_ace_to_locale(src, &dst); /* - * Copy the converted contents in 'tmp1' back to 'buffer'. - * If we have appended trailing dot, remove it. + * Check whether the converted name will fit back into 'buffer'. */ - tolen = strlen(tmp2); - if (absolute && !end_with_dot && tmp2[tolen - 1] == '.') - tolen--; - - if (isc_buffer_length(buffer) < used_org + tolen) { - idn2_free(tmp2); + dstlen = strlen(dst); + if (isc_buffer_length(buffer) < used_org + dstlen) { + idn2_free(dst); return (ISC_R_NOSPACE); } - isc_buffer_subtract(buffer, isc_buffer_usedlength(buffer) - used_org); - memmove(isc_buffer_used(buffer), tmp2, tolen); - isc_buffer_add(buffer, (unsigned int)tolen); - idn2_free(tmp2); + /* + * Put the converted name back into 'buffer'. + */ + isc_buffer_subtract(buffer, srclen); + memmove(isc_buffer_used(buffer), dst, dstlen); + isc_buffer_add(buffer, dstlen); + + /* + * Clean up. + */ + idn2_free(dst); return (ISC_R_SUCCESS); } From e5ef038134904853f9cebc3b409c68cf1bb56aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 08/11] Remove redundant dns_name_totextfilter_t argument Since idn_output_filter() no longer uses its 'absolute' argument and no other callback is used with dns_name_settotextfilter(), remove the 'absolute' argument from the dns_name_totextfilter_t prototype. --- bin/dig/dighost.c | 9 ++------- lib/dns/include/dns/name.h | 3 +-- lib/dns/name.c | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 4865ac7e94..30d5231375 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -136,8 +136,7 @@ static char servercookie[256]; static void idn_locale_to_ace(const char *src, char *dst, size_t dstlen); static void idn_ace_to_locale(const char *src, char **dst); static isc_result_t idn_output_filter(isc_buffer_t *buffer, - unsigned int used_org, - isc_boolean_t absolute); + unsigned int used_org); #endif /* HAVE_LIBIDN2 */ isc_socket_t *keep = NULL; @@ -4194,14 +4193,10 @@ destroy_libs(void) { #ifdef HAVE_LIBIDN2 static isc_result_t -idn_output_filter(isc_buffer_t *buffer, unsigned int used_org, - isc_boolean_t absolute) -{ +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org) { char src[MXNAME], *dst; size_t srclen, dstlen; - UNUSED(absolute); - /* * Copy name from 'buffer' to 'src' and terminate it with NULL. */ diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index ea6d772361..74d114e1ef 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -192,8 +192,7 @@ LIBDNS_EXTERNAL_DATA extern const dns_name_t *dns_wildcardname; * is from 'buffer'->base + 'used_org' to the end of the used region. */ typedef isc_result_t (*dns_name_totextfilter_t)(isc_buffer_t *target, - unsigned int used_org, - isc_boolean_t absolute); + unsigned int used_org); /*** *** Initialization diff --git a/lib/dns/name.c b/lib/dns/name.c index 5dc3e35e6f..75225f3ee3 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -1517,7 +1517,7 @@ dns_name_totext2(const dns_name_t *name, unsigned int options, totext_filter_proc = *mem; #endif if (totext_filter_proc != NULL) - return ((*totext_filter_proc)(target, oused, saw_root)); + return ((*totext_filter_proc)(target, oused)); return (ISC_R_SUCCESS); } From b896fc497212e97cc36119adcf4fe9a8a406cbce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 09/11] 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. --- bin/dig/dighost.c | 51 +++++++++++++++++++-- bin/tests/system/idna/tests.sh | 84 ++++++++++++---------------------- 2 files changed, 77 insertions(+), 58 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 30d5231375..b31900934b 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -4282,16 +4282,61 @@ idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { */ 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 */ diff --git a/bin/tests/system/idna/tests.sh b/bin/tests/system/idna/tests.sh index d7a968306a..644aa3eccb 100644 --- a/bin/tests/system/idna/tests.sh +++ b/bin/tests/system/idna/tests.sh @@ -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,10 +288,11 @@ 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 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. @@ -348,12 +300,34 @@ 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" "\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" } From bf6efbc9a99a102232f69a83701c2793381ef91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 10/11] Do not set IDN2_NFC_INPUT explicitly IDN2_NFC_INPUT is always set implicitly by idn2_to_ascii_lz(), so there is no need to set it explicitly. --- bin/dig/dighost.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index b31900934b..f5b29002ce 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -4254,8 +4254,7 @@ idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { * We trust libidn2 to return an error if 'src' is too large to be a * valid domain name. */ - res = idn2_to_ascii_lz(src, &ascii_src, - IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_NONTRANSITIONAL); if (res != IDN2_OK) { fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", src, idn2_strerror(res)); From e05784fd64e20edc9d6a63a17d0508ff91b7af46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Tue, 10 Jul 2018 14:34:35 +0200 Subject: [PATCH 11/11] Add CHANGES entry 4989. [cleanup] IDN support in dig has been reworked. IDNA2003 fallbacks were removed in the process. [GL #384] --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index f528315d10..05171d4be7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +4989. [cleanup] IDN support in dig has been reworked. IDNA2003 + fallbacks were removed in the process. [GL #384] + 4988. [bug] Don't synthesize NXDOMAIN from NSEC for records under a DNAME.