Blob Blame History Raw
From 3dcf053c775344855bd6476ec4f4bada824cdd41 Mon Sep 17 00:00:00 2001
From: Tomas Hozza <thozza@redhat.com>
Date: Wed, 23 Sep 2015 14:37:16 +0200
Subject: [PATCH] Add support for GNU libidn
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Added new configure option:
--with-libidn - to enable IDN using GNU libidn

Renamed configure option:
--with-idn to --with-idnkit to make the option usage more clear

idnkit and libidn support can not be used at the same time.

Signed-off-by: Tomas Hozza <thozza@redhat.com>
(cherry picked from commit 2320443f63b14ff926d0b91b479a48e29bc02376)
(cherry picked from commit edab22119f5d2b45a18727a6eb92f11b39ca3e73)

Add support for libidn2

Added two new configure options:
--with-libidn2 - to enable IDN using GNU libidn2

idnkit, libidn and libidn2 support can not be used at the same time.

NOTE: libidn2 does not support punycode back to Unicode
characters, so support for this is missing.

Signed-off-by: Tomas Hozza <thozza@redhat.com>

Removed iconv, convert directly from locale to ACE

Fix libidn2 and idnkit origin appending

Make IDN options in help less different

Signed-off-by: Petr Menšík <pemensik@redhat.com>
(cherry picked from commit 505f673451d9f94488b99f5af0ea042e3f8e0915)
(cherry picked from commit 56b66f01c6ae4d9db3a2b8df29af21f43ed65516)

Remove conversion from locale to utf8 from public API

Emit fatal failures on locale to ACE encoding

Separate idnout support, disable it for libidn2 < 2.0

Add custom path to libidn. Leave default path for multilib support.

Allow turning off IDN input processing by dig option

Improve documentation, fix support in host

Fix configure changes to adjust help text

Use strlcpy with size guard

Improve IDN variants choosing. Fix idn2 function name.

Remove immediate idn_locale_to_ace and idn_ace_to_locale.

Signed-off-by: Petr Menšík <pemensik@redhat.com>
(cherry picked from commit 94757c1545259ec3d7d99f3ba7f9baa9ce58e0d3)
(cherry picked from commit 2b031d17592a253297545c6e525019706c17c460)

Remove support for libidn (IDN 2003)

Signed-off-by: Petr Menšík <pemensik@redhat.com>
(cherry picked from commit 8254cf69d34947713c99839dd5139f326d8e5d43)
(cherry picked from commit 9117bbe9a5d637d51ef11c71ff6743e027db5e9e)

Sanitize IDN initialization

Signed-off-by: Petr Menšík <pemensik@redhat.com>
(cherry picked from commit 29b94bbb04014681afe61c1594b99f6ec30f6b1f)
(cherry picked from commit 82914d0a416edb6667e94b54ae828cd0ca8f029b)

Simplify the libidn2 configure checks

(cherry picked from commit 76c05a71fcf464d6a638b2d8fab589100850e3f0)
(cherry picked from commit c08528682254506f4bcf8155d700eff3ce18b601)

Add release notes for IDNA2008

(cherry picked from commit e7590c7528229c7bc761a6c69c33dfb69fbaf82f)
(cherry picked from commit 2ff3b664bca0c084326d2eaf75cb3b382fa26108)
---
 bin/dig/Makefile.in       |   6 +-
 bin/dig/dig.c             |  27 +++-
 bin/dig/dig.docbook       |  18 ++-
 bin/dig/dighost.c         | 304 +++++++++++++++++++++++++++++-----------------
 bin/dig/host.c            |  10 +-
 bin/dig/include/dig/dig.h |   3 +-
 config.h.in               |  11 +-
 configure                 | 181 ++++++++++++++++++++++++---
 configure.in              |  81 +++++++++---
 doc/arm/notes.xml         |  15 +++
 10 files changed, 491 insertions(+), 165 deletions(-)

diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in
index 511fdfe86e..d8080e5b30 100644
--- a/bin/dig/Makefile.in
+++ b/bin/dig/Makefile.in
@@ -16,7 +16,7 @@ READLINE_LIB = @READLINE_LIB@
 
 CINCLUDES =	-I${srcdir}/include ${DNS_INCLUDES} \
 		${BIND9_INCLUDES} ${ISC_INCLUDES} \
-		${LWRES_INCLUDES} ${ISCCFG_INCLUDES} @DST_OPENSSL_INC@
+		${LWRES_INCLUDES} ${ISCCFG_INCLUDES} @LIBIDN2_CFLAGS@ @DST_OPENSSL_INC@
 
 CDEFINES =	-DVERSION=\"${VERSION}\" @CRYPTO@
 CWARNINGS =
@@ -38,10 +38,10 @@ DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} \
 		${ISCCFGDEPLIBS} ${LWRESDEPLIBS}
 
 LIBS =		${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCLIBS} @IDNLIBS@ @LIBS@
+		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 NOSYMLIBS =	${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCNOSYMLIBS} @IDNLIBS@ @LIBS@
+		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 SUBDIRS =
 
diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index 2e1ade8bf3..9a1176353e 100644
--- a/bin/dig/dig.c
+++ b/bin/dig/dig.c
@@ -187,7 +187,8 @@ help(void) {
 "                 +[no]fail           (Don't try next server on SERVFAIL)\n"
 "                 +[no]header-only    (Send query without a question section)\n"
 "                 +[no]identify       (ID responders in short answers)\n"
-"                 +[no]idnout         (convert IDN response)\n"
+"                 +[no]idnin          (Parse IDN names)\n"
+"                 +[no]idnout         (Convert IDN response)\n"
 "                 +[no]ignore         (Don't revert to TCP for TC responses.)\n"
 "                 +[no]keepopen       (Keep the TCP socket open between queries)\n"
 "                 +[no]mapped         (Allow mapped IPv4 over IPv6)\n"
@@ -1091,12 +1092,28 @@ plus_option(const char *option, isc_boolean_t is_batchfile,
 				lookup->identify = state;
 				break;
 			case 'n':
-				FULLCHECK("idnout");
-#ifndef WITH_IDN
-				fprintf(stderr, ";; IDN support not enabled\n");
+				switch (cmd[3]) {
+				case 'i':
+					FULLCHECK("idnin");
+#ifndef WITH_IDN_SUPPORT
+					fprintf(stderr, ";; IDN input support"
+					        " not enabled\n");
+#else
+					lookup->idnin = state;
+#endif
+				break;
+				case 'o':
+					FULLCHECK("idnout");
+#ifndef WITH_IDN_OUT_SUPPORT
+					fprintf(stderr, ";; IDN output support"
+					        " not enabled\n");
 #else
-				lookup->idnout = state;
+					lookup->idnout = state;
 #endif
+					break;
+				default:
+					goto invalid_option;
+				}
 				break;
 			default:
 				goto invalid_option;
diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook
index bee62c6307..da5b7d6515 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -773,6 +773,17 @@
 	  </listitem>
 	</varlistentry>
 
+	<varlistentry>
+	  <term><option>+[no]idnin</option></term>
+	  <listitem>
+	    <para>
+	      Process [do not process] IDN domain names on input.
+	      This requires IDN SUPPORT to have been enabled at
+	      compile time.  The default is to process IDN input.
+	    </para>
+	  </listitem>
+	</varlistentry>
+
 	<varlistentry>
 	  <term><option>+[no]idnout</option></term>
 	  <listitem>
@@ -1265,10 +1276,9 @@ dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr
       <command>dig</command> appropriately converts character encoding of
       domain name before sending a request to DNS server or displaying a
       reply from the server.
-      If you'd like to turn off the IDN support for some reason, defines
-      the <envar>IDN_DISABLE</envar> environment variable.
-      The IDN support is disabled if the variable is set when
-      <command>dig</command> runs.
+      If you'd like to turn off the IDN support for some reason, use 
+      parameters <parameter>+noidnin</parameter> and
+      <parameter>+noidnout</parameter>.
     </para>
   </refsection>
 
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index fdfff399f5..c179b625f1 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -21,18 +21,25 @@
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
+#include <errno.h>
 
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
+#ifdef WITH_IDNKIT
 #include <idn/result.h>
 #include <idn/log.h>
 #include <idn/resconf.h>
 #include <idn/api.h>
 #endif
 
+#ifdef WITH_LIBIDN2
+#include <idn2.h>
+#endif
+#endif /* WITH_IDN_SUPPORT */
+
 #include <dns/byaddr.h>
 #ifdef DIG_SIGCHASE
 #include <dns/callbacks.h>
@@ -145,18 +152,26 @@ int lookup_counter = 0;
 
 static char servercookie[256];
 
-#ifdef WITH_IDN
-static void		initialize_idn(void);
-static isc_result_t	output_filter(isc_buffer_t *buffer,
-				      unsigned int used_org,
-				      isc_boolean_t absolute);
-static idn_result_t	append_textname(char *name, const char *origin,
-					size_t namesize);
-static void		idn_check_result(idn_result_t r, const char *msg);
-
-#define MAXDLEN		256
-int  idnoptions	= 0;
+#ifdef WITH_IDN_SUPPORT
+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);
+static isc_result_t output_filter(isc_buffer_t *buffer,
+		unsigned int used_org,
+		isc_boolean_t absolute);
+#define MAXDLEN 256
+
+#ifdef WITH_IDNKIT
+int idnoptions = 0;
 #endif
+#endif /* WITH_IDN_OUT_SUPPORT */
 
 isc_socket_t *keep = NULL;
 isc_sockaddr_t keepaddr;
@@ -803,7 +818,12 @@ make_empty_lookup(void) {
 	looknew->sendcookie = ISC_FALSE;
 	looknew->seenbadcookie = ISC_FALSE;
 	looknew->badcookie = ISC_TRUE;
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
+	looknew->idnin = ISC_TRUE;
+#else
+	looknew->idnin = ISC_FALSE;
+#endif
+#ifdef WITH_IDN_OUT_SUPPORT
 	looknew->idnout = ISC_TRUE;
 #else
 	looknew->idnout = ISC_FALSE;
@@ -952,6 +972,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
 	}
 	looknew->ednsneg = lookold->ednsneg;
 	looknew->mapped = lookold->mapped;
+	looknew->idnin = lookold->idnin;
 	looknew->idnout = lookold->idnout;
 #ifdef DIG_SIGCHASE
 	looknew->sigchase = lookold->sigchase;
@@ -1512,8 +1533,19 @@ setup_system(isc_boolean_t ipv4only, isc_boolean_t ipv6only) {
 		copy_server_list(lwconf, &server_list);
 	}
 
-#ifdef WITH_IDN
-	initialize_idn();
+#ifdef HAVE_SETLOCALE
+	/* Set locale */
+	(void)setlocale(LC_ALL, "");
+#endif
+
+#ifdef WITH_IDN_SUPPORT
+	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
 
 	if (keyfile[0] != 0)
@@ -2344,12 +2376,13 @@ setup_lookup(dig_lookup_t *lookup) {
 	char store[MXNAME];
 	char ecsbuf[20];
 	char cookiebuf[256];
-#ifdef WITH_IDN
-	idn_result_t mr;
-	char utf8_textname[MXNAME], utf8_origin[MXNAME], idn_textname[MXNAME];
+	char *origin = NULL;
+	char *textname = NULL;
+#ifdef WITH_IDN_SUPPORT
+	char idn_origin[MXNAME], idn_textname[MXNAME];
 #endif
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_OUT_SUPPORT
 	result = dns_name_settotextfilter(lookup->idnout ?
 					  output_filter : NULL);
 	check_result(result, "dns_name_settotextfilter");
@@ -2382,15 +2415,20 @@ setup_lookup(dig_lookup_t *lookup) {
 	isc_buffer_init(&lookup->onamebuf, lookup->oname_space,
 			sizeof(lookup->oname_space));
 
-#ifdef WITH_IDN
 	/*
 	 * We cannot convert `textname' and `origin' separately.
 	 * `textname' doesn't contain TLD, but local mapping needs
 	 * TLD.
 	 */
-	mr = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP, lookup->textname,
-			    utf8_textname, sizeof(utf8_textname));
-	idn_check_result(mr, "convert textname to UTF-8");
+	textname = lookup->textname;
+#ifdef WITH_IDN_SUPPORT
+	if (lookup->idnin) {
+		result = idn_locale_to_ace(lookup->textname, idn_textname,
+					    sizeof(idn_textname));
+		check_result(result, "convert textname to IDN encoding");
+		debug("idn_textname: %s", idn_textname);
+		textname = idn_textname;
+	}
 #endif
 
 	/*
@@ -2401,17 +2439,8 @@ setup_lookup(dig_lookup_t *lookup) {
 	 * is TRUE or we got a domain line in the resolv.conf file.
 	 */
 	if (lookup->new_search) {
-#ifdef WITH_IDN
-		if ((count_dots(utf8_textname) >= ndots) || !usesearch) {
-			lookup->origin = NULL; /* Force abs lookup */
-			lookup->done_as_is = ISC_TRUE;
-			lookup->need_search = usesearch;
-		} else if (lookup->origin == NULL && usesearch) {
-			lookup->origin = ISC_LIST_HEAD(search_list);
-			lookup->need_search = ISC_FALSE;
-		}
-#else
-		if ((count_dots(lookup->textname) >= ndots) || !usesearch) {
+		if ((count_dots(textname) >= ndots) || !usesearch)
+		{
 			lookup->origin = NULL; /* Force abs lookup */
 			lookup->done_as_is = ISC_TRUE;
 			lookup->need_search = usesearch;
@@ -2419,24 +2448,8 @@ setup_lookup(dig_lookup_t *lookup) {
 			lookup->origin = ISC_LIST_HEAD(search_list);
 			lookup->need_search = ISC_FALSE;
 		}
-#endif
 	}
 
-#ifdef WITH_IDN
-	if (lookup->origin != NULL) {
-		mr = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP,
-				    lookup->origin->origin, utf8_origin,
-				    sizeof(utf8_origin));
-		idn_check_result(mr, "convert origin to UTF-8");
-		mr = append_textname(utf8_textname, utf8_origin,
-				     sizeof(utf8_textname));
-		idn_check_result(mr, "append origin to textname");
-	}
-	mr = idn_encodename(idnoptions | IDN_LOCALMAP | IDN_NAMEPREP |
-			    IDN_IDNCONV | IDN_LENCHECK, utf8_textname,
-			    idn_textname, sizeof(idn_textname));
-	idn_check_result(mr, "convert UTF-8 textname to IDN encoding");
-#else
 	if (lookup->origin != NULL) {
 		debug("trying origin %s", lookup->origin->origin);
 		result = dns_message_gettempname(lookup->sendmsg,
@@ -2444,8 +2457,18 @@ setup_lookup(dig_lookup_t *lookup) {
 		check_result(result, "dns_message_gettempname");
 		dns_name_init(lookup->oname, NULL);
 		/* XXX Helper funct to conv char* to name? */
-		len = (unsigned int) strlen(lookup->origin->origin);
-		isc_buffer_init(&b, lookup->origin->origin, len);
+		origin = lookup->origin->origin;
+#ifdef WITH_IDN_SUPPORT
+		if (lookup->idnin) {
+			result = idn_locale_to_ace(lookup->origin->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
+		len = (unsigned int) strlen(origin);
+		isc_buffer_init(&b, origin, len);
 		isc_buffer_add(&b, len);
 		result = dns_name_fromtext(lookup->oname, &b, dns_rootname,
 					   0, &lookup->onamebuf);
@@ -2455,7 +2478,7 @@ setup_lookup(dig_lookup_t *lookup) {
 			dns_message_puttempname(lookup->sendmsg,
 						&lookup->oname);
 			fatal("'%s' is not in legal name syntax (%s)",
-			      lookup->origin->origin,
+			      origin,
 			      isc_result_totext(result));
 		}
 		if (lookup->trace && lookup->trace_root) {
@@ -2466,8 +2489,8 @@ setup_lookup(dig_lookup_t *lookup) {
 
 			dns_fixedname_init(&fixed);
 			name = dns_fixedname_name(&fixed);
-			len = (unsigned int) strlen(lookup->textname);
-			isc_buffer_init(&b, lookup->textname, len);
+			len = (unsigned int) strlen(textname);
+			isc_buffer_init(&b, textname, len);
 			isc_buffer_add(&b, len);
 			result = dns_name_fromtext(name, &b, NULL, 0, NULL);
 			if (result == ISC_R_SUCCESS &&
@@ -2492,28 +2515,17 @@ setup_lookup(dig_lookup_t *lookup) {
 			}
 		}
 		dns_message_puttempname(lookup->sendmsg, &lookup->oname);
-	} else
-#endif
-	{
+	} else {
 		debug("using root origin");
 		if (lookup->trace && lookup->trace_root)
 			dns_name_clone(dns_rootname, lookup->name);
 		else {
-#ifdef WITH_IDN
-			len = (unsigned int) strlen(idn_textname);
-			isc_buffer_init(&b, idn_textname, len);
+			len = (unsigned int) strlen(textname);
+			isc_buffer_init(&b, textname, len);
 			isc_buffer_add(&b, len);
 			result = dns_name_fromtext(lookup->name, &b,
 						   dns_rootname, 0,
 						   &lookup->namebuf);
-#else
-			len = (unsigned int) strlen(lookup->textname);
-			isc_buffer_init(&b, lookup->textname, len);
-			isc_buffer_add(&b, len);
-			result = dns_name_fromtext(lookup->name, &b,
-						   dns_rootname, 0,
-						   &lookup->namebuf);
-#endif
 		}
 		if (result != ISC_R_SUCCESS) {
 			dns_message_puttempname(lookup->sendmsg,
@@ -4512,7 +4524,7 @@ destroy_libs(void) {
 	void * ptr;
 	dig_message_t *chase_msg;
 #endif
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 	isc_result_t result;
 #endif
 
@@ -4549,7 +4561,7 @@ destroy_libs(void) {
 
 	clear_searchlist();
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 	result = dns_name_settotextfilter(NULL);
 	check_result(result, "dns_name_settotextfilter");
 #endif
@@ -4633,27 +4645,7 @@ destroy_libs(void) {
 		isc_mem_destroy(&mctx);
 }
 
-#ifdef WITH_IDN
-static void
-initialize_idn(void) {
-	idn_result_t r;
-	isc_result_t result;
-
-#ifdef HAVE_SETLOCALE
-	/* Set locale */
-	(void)setlocale(LC_ALL, "");
-#endif
-	/* Create configuration context. */
-	r = idn_nameinit(1);
-	if (r != idn_success)
-		fatal("idn api initialization failed: %s",
-		      idn_result_tostring(r));
-
-	/* Set domain name -> text post-conversion filter. */
-	result = dns_name_settotextfilter(output_filter);
-	check_result(result, "dns_name_settotextfilter");
-}
-
+#ifdef WITH_IDN_OUT_SUPPORT
 static isc_result_t
 output_filter(isc_buffer_t *buffer, unsigned int used_org,
 	      isc_boolean_t absolute)
@@ -4661,6 +4653,7 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 	char tmp1[MAXDLEN], tmp2[MAXDLEN];
 	size_t fromlen, tolen;
 	isc_boolean_t end_with_dot;
+	isc_result_t result;
 
 	/*
 	 * Copy contents of 'buffer' to 'tmp1', supply trailing dot
@@ -4669,6 +4662,7 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 	fromlen = isc_buffer_usedlength(buffer) - used_org;
 	if (fromlen >= MAXDLEN)
 		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) {
@@ -4677,13 +4671,16 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 			return (ISC_R_SUCCESS);
 		tmp1[fromlen - 1] = '.';
 	}
+
 	tmp1[fromlen] = '\0';
 
 	/*
 	 * Convert contents of 'tmp1' to local encoding.
 	 */
-	if (idn_decodename(IDN_DECODE_APP, tmp1, tmp2, MAXDLEN) != idn_success)
+	result = idn_ace_to_locale(tmp1, tmp2, sizeof(tmp2));
+	if (result != ISC_R_SUCCESS) {
 		return (ISC_R_SUCCESS);
+	}
 	strlcpy(tmp1, tmp2, MAXDLEN);
 
 	/*
@@ -4703,35 +4700,118 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 
 	return (ISC_R_SUCCESS);
 }
+#endif
 
-static idn_result_t
-append_textname(char *name, const char *origin, size_t namesize) {
-	size_t namelen = strlen(name);
-	size_t originlen = strlen(origin);
+#ifdef WITH_IDN_SUPPORT
+#ifdef WITH_IDNKIT
+static void
+idnkit_check_result(idn_result_t result, const char *msg) {
+	if (result != idn_success) {
+		fatal("%s: %s", msg, idn_result_tostring(result));
+	}
+}
+
+static void
+idn_initialize(void) {
+	idn_result_t result;
 
-	/* Already absolute? */
-	if (namelen > 0 && name[namelen - 1] == '.')
-		return (idn_success);
+	/* Create configuration context. */
+	result = idn_nameinit(1);
+	idnkit_check_result(result, "idnkit api initialization failed");
+	return (ISC_R_SUCCESS);
+}
 
-	/* Append dot and origin */
+static isc_result_t
+idn_locale_to_ace(const char *from, char *to, size_t tolen) {
+	char utf8_textname[MXNAME];
+	idn_result_t result;
+
+	result = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP, from,
+	                        utf8_textname, sizeof(utf8_textname));
+	idnkit_check_result(result, "idnkit idn_encodename to utf8 failed");
+
+	result = idn_encodename(idnoptions | IDN_LOCALMAP | IDN_NAMEPREP |
+		                IDN_IDNCONV | IDN_LENCHECK,
+		                utf8_textname, to, tolen);
+	idnkit_check_result(result, "idnkit idn_encodename to idn failed");
+	return (ISC_R_SUCCESS);
+}
 
-	if (namelen + 1 + originlen >= namesize)
-		return (idn_buffer_overflow);
+static isc_result_t
+idn_ace_to_locale(const char *from, char *to, size_t tolen) {
+	idn_result_t result;
 
-	if (*origin != '.')
-		name[namelen++] = '.';
-	(void)strlcpy(name + namelen, origin, namesize - namelen);
-	return (idn_success);
+	result = idn_decodename(IDN_DECODE_APP, from, to, tolen);
+	if (result != idn_success) {
+		debug("idnkit idn_decodename failed: %s",
+		      idn_result_tostring(result));
+		return (ISC_R_FAILURE);
+	}
+	return (ISC_R_SUCCESS);
 }
+#endif /* WITH_IDNKIT */
 
+#ifdef WITH_LIBIDN2
 static void
-idn_check_result(idn_result_t r, const char *msg) {
-	if (r != idn_success) {
-		exitcode = 1;
-		fatal("%s: %s", msg, idn_result_tostring(r));
+idn_initialize(void) {
+}
+
+static isc_result_t
+idn_locale_to_ace(const char *from, char *to, size_t tolen) {
+	int res;
+	char *tmp_str = NULL;
+
+	res = idn2_lookup_ul(from, &tmp_str, IDN2_NONTRANSITIONAL);
+	if (res == IDN2_DISALLOWED)
+		res = idn2_lookup_ul(from, &tmp_str, IDN2_TRANSITIONAL);
+
+	if (res == IDN2_OK) {
+		/* 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;
 	}
+
+	fatal("idn2_lookup_ul failed: %s", idn2_strerror(res));
+	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;
+	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) strncpy(to, tmp_str, tolen);
+		free(tmp_str);
+		return ISC_R_SUCCESS;
+	} else {
+		debug("idn2_to_unicode_8zlz failed: %s",
+		      idn2_strerror(res));
+	}
+
+	return ISC_R_FAILURE;
 }
-#endif /* WITH_IDN */
+#endif /* WITH_IDN_OUT_SUPPORT */
+#endif /* WITH_LIBIDN2 */
+#endif /* WITH_IDN_SUPPORT */
 
 #ifdef DIG_SIGCHASE
 void
diff --git a/bin/dig/host.c b/bin/dig/host.c
index f9e5b08962..f16483827a 100644
--- a/bin/dig/host.c
+++ b/bin/dig/host.c
@@ -16,7 +16,7 @@
 #include <locale.h>
 #endif
 
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 #include <idn/result.h>
 #include <idn/log.h>
 #include <idn/resconf.h>
@@ -720,7 +720,7 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
 			    lookup->rdtype != dns_rdatatype_axfr)
 				lookup->rdtype = rdtype;
 			lookup->rdtypeset = ISC_TRUE;
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 			idnoptions = 0;
 #endif
 			if (rdtype == dns_rdatatype_axfr) {
@@ -735,7 +735,7 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
 			} else if (rdtype == dns_rdatatype_any) {
 				if (!lookup->tcp_mode_set)
 					lookup->tcp_mode = ISC_TRUE;
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 			} else if (rdtype == dns_rdatatype_a ||
 				   rdtype == dns_rdatatype_aaaa ||
 				   rdtype == dns_rdatatype_mx) {
@@ -767,7 +767,7 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
 			if (!lookup->rdtypeset ||
 			    lookup->rdtype != dns_rdatatype_axfr)
 				lookup->rdtype = dns_rdatatype_any;
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 			idnoptions = 0;
 #endif
 			list_type = dns_rdatatype_any;
@@ -881,7 +881,7 @@ main(int argc, char **argv) {
 	ISC_LIST_INIT(search_list);
 
 	fatalexit = 1;
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 	idnoptions = IDN_ASCCHECK;
 #endif
 
diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h
index 2c7a5aeaa8..446ee027af 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -130,6 +130,7 @@ struct dig_lookup {
 		ednsneg,
 		mapped,
 		print_unknown_format,
+		idnin,
 		idnout;
 #ifdef DIG_SIGCHASE
 isc_boolean_t	sigchase;
@@ -292,7 +293,7 @@ extern char *progname;
 extern int tries;
 extern int fatalexit;
 extern isc_boolean_t verbose;
-#ifdef WITH_IDN
+#ifdef WITH_IDNKIT
 extern int idnoptions;
 #endif
 
diff --git a/config.h.in b/config.h.in
index 29cdb07a50..cd798c288d 100644
--- a/config.h.in
+++ b/config.h.in
@@ -596,7 +596,16 @@ int sigwait(const unsigned int *set, int *sig);
 #undef WANT_QUERYTRACE
 
 /* define if idnkit support is to be included. */
-#undef WITH_IDN
+#undef WITH_IDNKIT
+
+/* 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). */
diff --git a/configure b/configure
index 5ae413202f..ce9f393170 100755
--- a/configure
+++ b/configure
@@ -682,7 +682,9 @@ UNITTESTS
 ATFLIBS
 ATFBIN
 ATFBUILD
-IDNLIBS
+LIBIDN2_LIBS
+LIBIDN2_CFLAGS
+IDNKIT_LIBS
 XSLT_DBLATEX_FASTBOOK
 XSLT_DBLATEX_STYLE
 XSLT_DOCBOOK_MAKETOC_XHTML
@@ -1066,10 +1068,11 @@ enable_dnstap
 with_protobuf_c
 with_libfstrm
 with_docbook_xsl
-with_idn
+with_idnkit
 with_libiconv
 with_iconv
 with_idnlib
+with_libidn2
 with_atf
 with_tuning
 enable_querytrace
@@ -1794,10 +1797,11 @@ 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-idn=MPREFIX      enable IDN support using idnkit [default PREFIX]
+  --with-idnkit=PATH      enable IDN support using idnkit [yes|no|path]
   --with-libiconv=IPREFIX GNU libiconv are in IPREFIX [default PREFIX]
   --with-iconv=LIBSPEC    specify iconv library [default -liconv]
   --with-idnlib=ARG       specify libidnkit
+  --with-libidn2=PATH     enable IDN support using GNU libidn2 [yes|no|path]
   --with-atf              support Automated Test Framework
   --with-tuning=ARG       Specify server tuning (large or default)
   --with-dlopen=ARG       support dynamically loadable DLZ drivers
@@ -22755,28 +22759,28 @@ fi
 
 
 #
-# IDN support
+# IDN support using idnkit
 #
 
-# Check whether --with-idn was given.
-if test "${with_idn+set}" = set; then :
-  withval=$with_idn; use_idn="$withval"
+# Check whether --with-idnkit was given.
+if test "${with_idnkit+set}" = set; then :
+  withval=$with_idnkit; use_idnkit="$withval"
 else
-  use_idn="no"
+  use_idnkit="no"
 fi
 
-case "$use_idn" in
+case "$use_idnkit" in
 yes)
 	if test X$prefix = XNONE ; then
-		idn_path=/usr/local
+		idnkit_path=/usr/local
 	else
-		idn_path=$prefix
+		idnkit_path=$prefix
 	fi
 	;;
 no)
 	;;
 *)
-	idn_path="$use_idn"
+	idnkit_path="$use_idnkit"
 	;;
 esac
 
@@ -22833,20 +22837,161 @@ if test "yes" = "$idnlib"; then
 	as_fn_error $? "You must specify ARG for --with-idnlib." "$LINENO" 5
 fi
 
-IDNLIBS=
-if test "no" != "$use_idn"; then
+IDNKIT_LIBS=
+if test "no" != "$use_idnkit"; then
 
-$as_echo "#define WITH_IDN 1" >>confdefs.h
+$as_echo "#define WITH_IDNKIT 1" >>confdefs.h
 
-	STD_CINCLUDES="$STD_CINCLUDES -I$idn_path/include"
+	STD_CINCLUDES="$STD_CINCLUDES -I$idnkit_path/include"
 	if test "no" != "$idnlib"; then
-		IDNLIBS="$idnlib $iconvlib"
+		IDNKIT_LIBS="$idnlib $iconvlib"
 	else
-		IDNLIBS="-L$idn_path/lib -lidnkit $iconvlib"
+		IDNKIT_LIBS="-L$idnkit_path/lib -lidnkit $iconvlib"
 	fi
 fi
 
 
+#
+# IDN support using libidn2
+#
+
+LIBIDN2_CFLAGS=
+LIBIDN2_LIBS=
+
+# Check whether --with-libidn2 was given.
+if test "${with_libidn2+set}" = set; then :
+  withval=$with_libidn2; use_libidn2="$withval"
+else
+  use_libidn2="no"
+fi
+
+case $use_libidn2 in #(
+  no) :
+    : ;; #(
+  yes) :
+
+	    LIBIDN2_LIBS="-lidn2"
+	 ;; #(
+  *) :
+
+	    LIBIDN2_CFLAGS="-I$use_libidn2/include"
+	    LIBIDN2_LIBS="-L$use_libidn2/lib -lidn2"
+	 ;; #(
+  *) :
+     ;;
+esac
+
+if test "$use_libidn2" != "no"; then :
+  { $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 :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char idn2_to_ascii_8z ();
+int
+main ()
+{
+return idn2_to_ascii_8z ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' idn2; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    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
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_idn2_to_ascii_8z+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_idn2_to_ascii_8z+:} false; then :
+
+else
+  ac_cv_search_idn2_to_ascii_8z=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
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+$as_echo "#define WITH_LIBIDN2 1" >>confdefs.h
+
+else
+  as_fn_error $? "libidn2 requested, but not found" "$LINENO" 5
+fi
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libidn2 supports idn2_to_unicode_8zlz" >&5
+$as_echo_n "checking whether libidn2 supports idn2_to_unicode_8zlz... " >&6; }
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <idn2.h>
+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
+
+fi
+
+
+
+#
+# IDN support in general
+#
+
+# check if idnkit and libidn2 are not used at the same time
+if test "$use_idnkit" != no && test "$use_libidn2" != no; then
+    as_fn_error $? "idnkit and libidn2 cannot be used at the same time." "$LINENO" 5
+fi
+# the IDN support is on
+if test "$use_idnkit" != no || test "$use_libidn2" != no; then
+
+$as_echo "#define WITH_IDN_SUPPORT 1" >>confdefs.h
+
+	if test "$use_libidn2" = no || test "$use_libidn2_out" != no; then
+
+$as_echo "#define WITH_IDN_OUT_SUPPORT 1" >>confdefs.h
+
+	fi
+fi
+
 #
 # Check whether to build Automated Test Framework unit tests
 #
diff --git a/configure.in b/configure.in
index 388356a7c3..63988c97dc 100644
--- a/configure.in
+++ b/configure.in
@@ -4824,24 +4824,24 @@ NOM_PATH_FILE(XSLT_DBLATEX_STYLE, xsl/docbook.xsl, $dblatex_xsl_trees)
 NOM_PATH_FILE(XSLT_DBLATEX_FASTBOOK, xsl/latex_book_fast.xsl, $dblatex_xsl_trees)
 
 #
-# IDN support
+# IDN support using idnkit
 #
-AC_ARG_WITH(idn,
-	    AS_HELP_STRING([--with-idn[=MPREFIX]],
-			   [enable IDN support using idnkit [default PREFIX]]),
-	use_idn="$withval", use_idn="no")
-case "$use_idn" in
+AC_ARG_WITH(idnkit,
+	    AS_HELP_STRING([--with-idnkit[=PATH]],
+			   [enable IDN support using idnkit [yes|no|path]]),
+	use_idnkit="$withval", use_idnkit="no")
+case "$use_idnkit" in
 yes)
 	if test X$prefix = XNONE ; then
-		idn_path=/usr/local
+		idnkit_path=/usr/local
 	else
-		idn_path=$prefix
+		idnkit_path=$prefix
 	fi
 	;;
 no)
 	;;
 *)
-	idn_path="$use_idn"
+	idnkit_path="$use_idnkit"
 	;;
 esac
 
@@ -4887,17 +4887,66 @@ if test "yes" = "$idnlib"; then
 	AC_MSG_ERROR([You must specify ARG for --with-idnlib.])
 fi
 
-IDNLIBS=
-if test "no" != "$use_idn"; then
-	AC_DEFINE(WITH_IDN, 1, [define if idnkit support is to be included.])
-	STD_CINCLUDES="$STD_CINCLUDES -I$idn_path/include"
+IDNKIT_LIBS=
+if test "no" != "$use_idnkit"; then
+	AC_DEFINE(WITH_IDNKIT, 1, [define if idnkit support is to be included.])
+	STD_CINCLUDES="$STD_CINCLUDES -I$idnkit_path/include"
 	if test "no" != "$idnlib"; then
-		IDNLIBS="$idnlib $iconvlib"
+		IDNKIT_LIBS="$idnlib $iconvlib"
 	else
-		IDNLIBS="-L$idn_path/lib -lidnkit $iconvlib"
+		IDNKIT_LIBS="-L$idnkit_path/lib -lidnkit $iconvlib"
+	fi
+fi
+AC_SUBST(IDNKIT_LIBS)
+
+#
+# IDN support using libidn2
+#
+
+LIBIDN2_CFLAGS=
+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_LIBS="-lidn2"
+	],
+	[*],[
+	    LIBIDN2_CFLAGS="-I$use_libidn2/include"
+	    LIBIDN2_LIBS="-L$use_libidn2/lib -lidn2"
+	])
+
+AS_IF([test "$use_libidn2" != "no"],
+      [AC_SEARCH_LIBS([idn2_to_ascii_8z], [idn2],
+		      [AC_DEFINE(WITH_LIBIDN2, 1, [define if libidn2 support is to be included.])],
+		      [AC_MSG_ERROR([libidn2 requested, but not found])])
+       AC_MSG_CHECKING(whether libidn2 supports idn2_to_unicode_8zlz)
+       AC_TRY_LINK([#include <idn2.h>],
+		   [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])])
+      ])
+AC_SUBST([LIBIDN2_CFLAGS])
+AC_SUBST([LIBIDN2_LIBS])
+
+#
+# IDN support in general
+#
+
+# check if idnkit and libidn2 are not used at the same time
+if test "$use_idnkit" != no && test "$use_libidn2" != no; then
+    AC_MSG_ERROR([idnkit and libidn2 cannot be used at the same time.])
+fi
+# the IDN support is on
+if test "$use_idnkit" != no || test "$use_libidn2" != no; then
+	AC_DEFINE(WITH_IDN_SUPPORT, 1, [define if IDN input support is to be included.])
+	if test "$use_libidn2" = no || test "$use_libidn2_out" != no; then
+		AC_DEFINE(WITH_IDN_OUT_SUPPORT, 1, [define if IDN output support is to be included.])
 	fi
 fi
-AC_SUBST(IDNLIBS)
 
 #
 # Check whether to build Automated Test Framework unit tests
diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
index ec761018c8..ea9b61fd73 100644
--- a/doc/arm/notes.xml
+++ b/doc/arm/notes.xml
@@ -166,6 +166,14 @@
 	  [RT #43670]
 	</para>
       </listitem>
+      <listitem>
+	<para>
+	  <command>named</command> will now log a warning if the old
+	  BIND now can be compiled against libidn2 library to add
+	  IDNA2008 support.  Previously BIND only supported IDNA2003
+	  using (now obsolete) idnkit-1 library.
+	</para>
+      </listitem>
     </itemizedlist>
   </section>
 
@@ -242,6 +250,13 @@
 	  Multicast DNS. [RT #44783]
 	</para>
       </listitem>
+      <listitem>
+	<para>
+	  <command>dig +noidnin</command> can be used to disable IDN
+	  processing on the input domain name, when BIND is compiled
+	  with IDN support.
+	</para>
+      </listitem>
     </itemizedlist>
   </section>
 
-- 
2.14.3