From 0b5fb24f4a00c8431b086e4ebcea7d2f3e0da29f Mon Sep 17 00:00:00 2001
From: Tomas Hozza <thozza@redhat.com>
Date: Sun, 1 Jun 2014 22:28:22 +0200
Subject: [PATCH] Add support for libidn and libidn2

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

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

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

Signed-off-by: Tomas Hozza <thozza@redhat.com>
---
 bin/dig/Makefile.in  |   4 +-
 bin/dig/dig.1        |  15 +-
 bin/dig/dig.docbook  |  11 +-
 bin/dig/dig.html     |  11 +-
 bin/dig/dighost.c    | 466 +++++++++++++++++++++++++++++++++++++++++++--------
 bin/dig/host.1       |  12 +-
 bin/dig/host.docbook |   9 +-
 bin/dig/host.html    |   9 +-
 config.h.in          |  11 +-
 configure            | 138 +++++++++++++--
 configure.in         | 110 ++++++++++--
 11 files changed, 677 insertions(+), 119 deletions(-)

diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in
index 23dd283..0e132dc 100644
--- a/bin/dig/Makefile.in
+++ b/bin/dig/Makefile.in
@@ -48,10 +48,10 @@ DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} \
 		${ISCCFGDEPLIBS} ${LWRESDEPLIBS}
 
 LIBS =		${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCLIBS} @IDNLIBS@ @LIBS@
+		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 NOSYMLIBS =	${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCNOSYMLIBS} @IDNLIBS@ @LIBS@
+		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 SUBDIRS =
 
diff --git a/bin/dig/dig.1 b/bin/dig/dig.1
index de8e7cb..c05b28d 100644
--- a/bin/dig/dig.1
+++ b/bin/dig/dig.1
@@ -628,9 +628,20 @@ If
 \fBdig\fR
 has been built with IDN (internationalized domain name) support, it can accept and display non\-ASCII domain names.
 \fBdig\fR
-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
+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, in case
+\fBdig\fR
+was compiled with idnkit library, define the
 \fBIDN_DISABLE\fR
-environment variable. The IDN support is disabled if the variable is set when
+environment variable. In case
+\fBdig\fR
+was compiled with libidn library, define the
+\fBCHARSET=ASCII\fR
+environment variable. In case
+\fBdig\fR
+was compiled with libidn2 library, define the
+\fBLANG=language[_territory].ASCII\fR
+environment variable.
+The IDN support is disabled if the variable is set when
 \fBdig\fR
 runs.
 .SH "FILES"
diff --git a/bin/dig/dig.docbook b/bin/dig/dig.docbook
index 0a299db..6cd89ef 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -1035,9 +1035,14 @@ 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 
+      If you'd like to turn off the IDN support for some reason, in case
+      <command>dig</command> was compiled with idnkit library, define
+      the <envar>IDN_DISABLE</envar> environment variable. In case
+      <command>dig</command> was compiled with libidn library, define
+      the <envar>CHARSET=ASCII</envar> environment variable.
+      In case <command>dig</command> was compiled with libidn2 library, define
+      the <envar>LANG=language[_territory].ASCII</envar> environment variable.
+      The IDN support is disabled if the variable is set when
       <command>dig</command> runs.
     </para>
   </refsect1>
diff --git a/bin/dig/dig.html b/bin/dig/dig.html
index 66971c8..6928642 100644
--- a/bin/dig/dig.html
+++ b/bin/dig/dig.html
@@ -684,9 +684,14 @@ dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr
       <span><strong class="command">dig</strong></span> 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 <code class="envar">IDN_DISABLE</code> environment variable.
-      The IDN support is disabled if the variable is set when 
+      If you'd like to turn off the IDN support for some reason, in case
+      <span><strong class="command">dig</strong></span> was compiled with idnkit library, define
+      the <code class="envar">IDN_DISABLE </code> environment variable. In case
+      <span><strong class="command">dig</strong></span> was compiled with libidn library, define
+      the <code class="envar">CHARSET=ASCII</code> environment variable. In case
+      <span><strong class="command">dig</strong></span> was compiled with libidn2 library, define
+      the <code class="envar">LANG=language[_territory].ASCII</code> environment variable.
+      The IDN support is disabled if the variable is set when
       <span><strong class="command">dig</strong></span> runs.
     </p>
 </div>
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index d4a13d7..61b9813 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -30,18 +30,31 @@
 #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_LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
+#ifdef WITH_LIBIDN2
+#include <idn2.h>
+#include <iconv.h>
+#endif
+#endif /* WITH_IDN_SUPPORT */
+
 #include <dns/byaddr.h>
 #ifdef DIG_SIGCHASE
 #include <dns/callbacks.h>
@@ -144,18 +157,35 @@ int ndots = -1;
 int tries = 3;
 int lookup_counter = 0;
 
-#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,
+#ifdef WITH_IDN_SUPPORT
+static void idn_initialize(void);
+static isc_result_t idn_locale_to_utf8(const char *from, char *to, size_t tolen);
+static isc_result_t idn_utf8_to_ace(const char *from, char *to, size_t tolen);
+static isc_result_t idn_ace_to_locale(const char *from, char *to, size_t tolen);
+static isc_result_t output_filter(isc_buffer_t *buffer, unsigned int used_org,
+					isc_boolean_t absolute);
+static isc_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;
+#define MAXDLEN 256
+
+#ifdef WITH_IDNKIT
+static isc_result_t idnkit_initialize(void);
+static isc_result_t idnkit_locale_to_utf8(const char *from, char *to, size_t tolen);
+static isc_result_t idnkit_utf8_to_ace(const char *from, char *to, size_t tolen);
+static isc_result_t idnkit_ace_to_locale(const char *from, char *to, size_t tolen);
+
+#elif WITH_LIBIDN
+static isc_result_t libidn_locale_to_utf8(const char *from, char *to, size_t tolen);
+static isc_result_t libidn_utf8_to_ace(const char *from, char *to, size_t tolen);
+static isc_result_t libidn_ace_to_locale(const char *from, char *to, size_t tolen);
+
+#elif WITH_LIBIDN2
+static isc_result_t libidn2_locale_to_utf8(const char *from, char *to, size_t tolen);
+static isc_result_t libidn2_utf8_to_ace(const char *from, char *to, size_t tolen);
+static isc_result_t libidn2_ace_to_locale(const char *from, char *to, size_t tolen);
 #endif
+#endif /* WITH_IDN_SUPPORT */
 
 isc_socket_t *keep = NULL;
 isc_sockaddr_t keepaddr;
@@ -1370,8 +1400,8 @@ setup_system(void) {
 		copy_server_list(lwconf, &server_list);
 	}
 
-#ifdef WITH_IDN
-	initialize_idn();
+#ifdef WITH_IDN_SUPPORT
+	idn_initialize();
 #endif
 
 	if (keyfile[0] != 0)
@@ -2158,12 +2188,11 @@ setup_lookup(dig_lookup_t *lookup) {
 #ifdef ISC_PLATFORM_USESIT
 	char sitbuf[256];
 #endif
-#ifdef WITH_IDN
-	idn_result_t mr;
+#ifdef WITH_IDN_SUPPORT
 	char utf8_textname[MXNAME], utf8_origin[MXNAME], idn_textname[MXNAME];
 #endif
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 	result = dns_name_settotextfilter(output_filter);
 	check_result(result, "dns_name_settotextfilter");
 #endif
@@ -2195,15 +2224,14 @@ setup_lookup(dig_lookup_t *lookup) {
 	isc_buffer_init(&lookup->onamebuf, lookup->onamespace,
 			sizeof(lookup->onamespace));
 
-#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");
+#ifdef WITH_IDN_SUPPORT
+	result = idn_locale_to_utf8(lookup->textname, utf8_textname, sizeof(utf8_textname));
+	check_result(result, "convert textname to UTF-8");
 #endif
 
 	/*
@@ -2214,17 +2242,11 @@ 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
+#ifdef WITH_IDN_SUPPORT
 		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) {
+#endif
 			lookup->origin = NULL; /* Force abs lookup */
 			lookup->done_as_is = ISC_TRUE;
 			lookup->need_search = usesearch;
@@ -2232,24 +2254,18 @@ setup_lookup(dig_lookup_t *lookup) {
 			lookup->origin = ISC_LIST_HEAD(search_list);
 			lookup->need_search = ISC_FALSE;
 		}
-#endif
 	}
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 	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
+		result = idn_locale_to_utf8(lookup->origin->origin, utf8_origin, sizeof(utf8_origin));
+		check_result(result, "convert origin to UTF-8");
+		result = append_textname(utf8_textname, utf8_origin, sizeof(utf8_textname));
+		check_result(result, "append origin to textname");
+	}
+	result = idn_utf8_to_ace(utf8_textname, idn_textname, sizeof(idn_textname));
+	check_result(result, "convert UTF-8 textname to IDN encoding");
+#else /* WITH_IDN_SUPPORT */
 	if (lookup->origin != NULL) {
 		debug("trying origin %s", lookup->origin->origin);
 		result = dns_message_gettempname(lookup->sendmsg,
@@ -2297,21 +2313,18 @@ setup_lookup(dig_lookup_t *lookup) {
 		if (lookup->trace && lookup->trace_root)
 			dns_name_clone(dns_rootname, lookup->name);
 		else {
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 			len = strlen(idn_textname);
 			isc_buffer_init(&b, idn_textname, len);
 			isc_buffer_add(&b, len);
-			result = dns_name_fromtext(lookup->name, &b,
-						   dns_rootname, 0,
-						   &lookup->namebuf);
 #else
 			len = strlen(lookup->textname);
 			isc_buffer_init(&b, lookup->textname, len);
 			isc_buffer_add(&b, len);
+#endif
 			result = dns_name_fromtext(lookup->name, &b,
 						   dns_rootname, 0,
 						   &lookup->namebuf);
-#endif
 		}
 		if (result != ISC_R_SUCCESS) {
 			dns_message_puttempname(lookup->sendmsg,
@@ -4096,7 +4109,7 @@ destroy_libs(void) {
 	void * ptr;
 	dig_message_t *chase_msg;
 #endif
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 	isc_result_t result;
 #endif
 
@@ -4133,7 +4146,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
@@ -4223,21 +4236,21 @@ destroy_libs(void) {
 		isc_mem_destroy(&mctx);
 }
 
-#ifdef WITH_IDN
+#ifdef WITH_IDN_SUPPORT
 static void
-initialize_idn(void) {
-	idn_result_t r;
+idn_initialize(void) {
 	isc_result_t result;
 
 #ifdef HAVE_SETLOCALE
 	/* Set locale */
 	(void)setlocale(LC_ALL, "");
 #endif
+
+#ifdef HAVE_IDNKIT
 	/* Create configuration context. */
-	r = idn_nameinit(1);
-	if (r != idn_success)
-		fatal("idn api initialization failed: %s",
-		      idn_result_tostring(r));
+	result = idnkit_initialize();
+	check_result(result, "idnkit initializationt");
+#endif
 
 	/* Set domain name -> text post-conversion filter. */
 	result = dns_name_settotextfilter(output_filter);
@@ -4245,12 +4258,52 @@ initialize_idn(void) {
 }
 
 static isc_result_t
+idn_locale_to_utf8(const char *from, char *to, size_t tolen) {
+#ifdef WITH_IDNKIT
+	return idnkit_locale_to_utf8(from, to, tolen);
+
+#elif WITH_LIBIDN
+	return libidn_locale_to_utf8(from, to, tolen);
+
+#else /* WITH_LIBIDN2 */
+	return libidn2_locale_to_utf8(from, to, tolen);
+#endif
+}
+
+static isc_result_t
+idn_utf8_to_ace(const char *from, char *to, size_t tolen) {
+#ifdef WITH_IDNKIT
+	return idnkit_utf8_to_ace(from, to, tolen);
+
+#elif WITH_LIBIDN
+	return libidn_utf8_to_ace(from, to, tolen);
+
+#else /* WITH_LIBIDN2 */
+	return libidn2_utf8_to_ace(from, to, tolen);
+#endif
+}
+
+static isc_result_t
+idn_ace_to_locale(const char *from, char *to, size_t tolen) {
+#ifdef WITH_IDNKIT
+	return idnkit_ace_to_locale(from, to, tolen);
+
+#elif WITH_LIBIDN
+	return libidn_ace_to_locale(from, to, tolen);
+
+#else /* WITH_LIBIDN2 */
+	return libidn2_ace_to_locale(from, to, tolen);
+#endif
+}
+
+static isc_result_t
 output_filter(isc_buffer_t *buffer, unsigned int used_org,
 	      isc_boolean_t absolute)
 {
 	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
@@ -4259,6 +4312,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) {
@@ -4267,13 +4321,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)
-		return (ISC_R_SUCCESS);
+	result = idn_ace_to_locale(tmp1, tmp2, sizeof(tmp2));
+	if (result != ISC_R_SUCCESS)
+		return ISC_R_SUCCESS;
+
 	strcpy(tmp1, tmp2);
 
 	/*
@@ -4294,34 +4351,303 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 	return (ISC_R_SUCCESS);
 }
 
-static idn_result_t
+static isc_result_t
 append_textname(char *name, const char *origin, size_t namesize) {
 	size_t namelen = strlen(name);
 	size_t originlen = strlen(origin);
 
 	/* Already absolute? */
 	if (namelen > 0 && name[namelen - 1] == '.')
-		return idn_success;
+		return ISC_R_SUCCESS;
 
 	/* Append dot and origin */
-
-	if (namelen + 1 + originlen >= namesize)
-		return idn_buffer_overflow;
+	if (namelen + 1 + originlen >= namesize) {
+		debug("append_textname failure: name + origin is too long");
+		return ISC_R_FAILURE;
+	}
 
 	if (*origin != '.')
 		name[namelen++] = '.';
+
 	(void)strcpy(name + namelen, origin);
-	return idn_success;
+
+	return ISC_R_SUCCESS;
 }
 
-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));
+#ifdef WITH_IDNKIT
+static isc_result_t
+idnkit_initialize(void) {
+	idn_result_t result;
+
+	result = idn_nameinit(1);
+
+	if (result == idn_success) {
+		return ISC_R_SUCCESS;
+	} else {
+		debug("idnkit api initialization failed: %s", idn_result_tostring(result));
+		return ISC_R_FAILURE;
+	}
+}
+
+static isc_result_t
+idnkit_locale_to_utf8(const char *from, char *to, size_t tolen) {
+	idn_result_t result;
+
+	result = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP, from, to, tolen);
+
+	if (result == idn_success) {
+		return ISC_R_SUCCESS;
+	} else {
+		debug("idnkit idn_encodename failed: %s", idn_result_tostring(result));
+		return ISC_R_FAILURE;
 	}
 }
-#endif /* WITH_IDN */
+
+static isc_result_t
+idnkit_utf8_to_ace(const char *from, char *to, size_t tolen) {
+	idn_result_t result;
+
+	result = idn_encodename(IDN_LOCALMAP | IDN_NAMEPREP |
+			    IDN_IDNCONV | IDN_LENCHECK, from, to, tolen);
+
+	if (result == idn_success) {
+		return ISC_R_SUCCESS;
+	} else {
+		debug("idnkit idn_encodename failed: %s", idn_result_tostring(result));
+		return ISC_R_FAILURE;
+	}
+}
+
+static isc_result_t
+idnkit_ace_to_locale(const char *from, char *to, size_t tolen) {
+	idn_result_t result;
+
+	result = idn_decodename(IDN_DECODE_APP, from, to, tolen);
+
+	if (result == idn_success) {
+		return ISC_R_SUCCESS;
+	} else {
+		debug("idnkit idn_decodename failed: %s", idn_result_tostring(result);
+		return ISC_R_FAILURE;
+	}
+}
+#endif /* WITH_IDNKIT */
+
+#ifdef WITH_LIBIDN
+static isc_result_t
+libidn_locale_to_utf8(const char *from, char *to, size_t tolen) {
+	isc_result_t result = ISC_R_FAILURE;
+	char *tmp_str = NULL;
+
+	tmp_str = stringprep_locale_to_utf8(from);
+
+	if (tmp_str != NULL) {
+		if (strlen(tmp_str) >= tolen) {
+			debug("UTF-8 string is too long");
+			result = ISC_R_FAILURE;
+			goto cleanup;
+		}
+
+		(void) strncpy(to, tmp_str, tolen);
+
+		result = ISC_R_SUCCESS;
+	}
+
+cleanup:
+	free(tmp_str);
+	return result;
+}
+
+static isc_result_t
+libidn_utf8_to_ace(const char *from, char *to, size_t tolen) {
+	int res;
+	isc_result_t result;
+	char *tmp_str = NULL;
+
+	res = idna_to_ascii_8z(from, &tmp_str, 0);
+
+	if (res == IDNA_SUCCESS) {
+		/* check the length */
+		if (strlen(tmp_str) >= tolen) {
+			debug("encoded ASC string is too long");
+			result = ISC_R_FAILURE;
+			goto cleanup;
+		}
+
+		(void) strncpy(to, tmp_str, tolen);
+
+		result = ISC_R_SUCCESS;
+	} else {
+		debug("libidn idna_to_ascii_8z failed: %s", idna_strerror(res));
+		result = ISC_R_FAILURE;
+	}
+
+cleanup:
+	free(tmp_str);
+	return result;
+}
+
+static isc_result_t
+libidn_ace_to_locale(const char *from, char *to, size_t tolen) {
+	int res;
+	isc_result_t result;
+	char *tmp_str = NULL;
+
+	res = idna_to_unicode_8zlz(from, &tmp_str, 0);
+
+	if (res == IDNA_SUCCESS) {
+		/* check the length */
+		if (strlen(tmp_str) >= tolen) {
+			debug("decoded locale string is too long");
+			result = ISC_R_FAILURE;
+			goto cleanup;
+		}
+
+		(void) strncpy(to, tmp_str, tolen);
+
+		result = ISC_R_SUCCESS;
+	} else {
+		debug("libidn idna_to_unicode_8z8l failed: %s", idna_strerror(res));
+		result = ISC_R_FAILURE;
+	}
+
+cleanup:
+	free(tmp_str);
+	return result;
+}
+#endif /* WITH_LIBIDN */
+
+#ifdef WITH_LIBIDN2
+static isc_result_t
+libidn2_locale_to_utf8(const char *from, char *to, size_t tolen) {
+	iconv_t l2u;
+	char *from_str_p;
+	char *to_str_p;
+	char *from_tmp_str = NULL;
+	size_t nconv, inbytesleft, outbytesleft;
+	isc_result_t result;
+
+	/* allocate the temporary string */
+	from_tmp_str = (char *) malloc((strlen(from) + 1) * sizeof(char));
+	if (from_tmp_str == NULL) {
+		debug("libidn2_locale_to_utf8: Not enough memory to allocate temporary string.");
+		result = ISC_R_NOMEMORY;
+		goto cleanup;
+	}
+
+	(void) strcpy(from_tmp_str, from);
+
+	/* get character set conversion descriptor */
+	l2u = iconv_open("UTF-8", "");
+
+	if (l2u == (iconv_t)(-1)) {
+		debug("iconv_open 'locale to UTF-8' failed: (%d) %s", errno, strerror(errno));
+		result = ISC_R_FAILURE;
+		goto cleanup;
+	}
+
+	from_str_p = from_tmp_str;
+	to_str_p = to;
+	inbytesleft = strlen(from);
+	outbytesleft = tolen - 1;
+
+	nconv = iconv(l2u, &from_str_p, &inbytesleft, &to_str_p, &outbytesleft);
+
+	if (nconv == (size_t)(-1)) {
+		debug("iconv failed: (%d) %s", errno, strerror(errno));
+		result = ISC_R_FAILURE;
+	} else {
+		*to_str_p = '\0';
+		result = ISC_R_SUCCESS;
+	}
+
+	iconv_close(l2u);
+
+cleanup:
+	free(from_tmp_str);
+	return result;
+}
+
+static isc_result_t
+libidn2_utf8_to_ace(const char *from, char *to, size_t tolen) {
+	int res;
+	isc_result_t result;
+	char *tmp_str = NULL;
+
+	res = idn2_lookup_u8((const uint8_t *) from, (uint8_t **) &tmp_str, 0);
+
+	if (res == IDN2_OK) {
+		/* check the length */
+		if (strlen(tmp_str) >= tolen) {
+			debug("ACE string is too long");
+			result = ISC_R_FAILURE;
+			goto cleanup;
+		}
+
+		(void) strncpy(to, tmp_str, tolen);
+
+		result = ISC_R_SUCCESS;
+	} else {
+		debug("libidn2 idn2_lookup_u8 failed: %s", idn2_strerror(res));
+		result = ISC_R_FAILURE;
+	}
+
+cleanup:
+	free(tmp_str);
+	return result;
+}
+
+static isc_result_t
+libidn2_ace_to_locale(const char *from, char *to, size_t tolen) {
+	iconv_t a2l;
+	char *from_str_p;
+	char *to_str_p;
+	char *from_tmp_str = NULL;
+	size_t nconv, inbytesleft, outbytesleft;
+	isc_result_t result;
+
+	/* allocate the temporary string */
+	from_tmp_str = (char *) malloc((strlen(from) + 1) * sizeof(char));
+	if (from_tmp_str == NULL) {
+		debug("libidn2_ace_to_locale: Not enough memory to allocate temporary string.");
+		result = ISC_R_NOMEMORY;
+		goto cleanup;
+	}
+
+	(void) strcpy(from_tmp_str, from);
+
+	/* get character set conversion descriptor */
+	a2l = iconv_open("", "ASCII");
+
+	if (a2l == (iconv_t)(-1)) {
+		debug("iconv_open 'ASCII to locale' failed: (%d) %s", errno, strerror(errno));
+		result = ISC_R_FAILURE;
+		goto cleanup;
+	}
+
+	from_str_p = from_tmp_str;
+	to_str_p = to;
+	inbytesleft = strlen(from);
+	outbytesleft = tolen - 1;
+
+	nconv = iconv(a2l, &from_str_p, &inbytesleft, &to_str_p, &outbytesleft);
+
+	if (nconv == (size_t)(-1)) {
+		debug("iconv failed: (%d) %s", errno, strerror(errno));
+		result = ISC_R_FAILURE;
+	} else {
+		*to_str_p = '\0';
+		result = ISC_R_SUCCESS;
+	}
+
+	iconv_close(a2l);
+
+cleanup:
+	free(from_tmp_str);
+	return result;
+}
+#endif /* WITH_LIBIDN2 */
+#endif /* WITH_IDN_SUPPORT */
 
 #ifdef DIG_SIGCHASE
 void
diff --git a/bin/dig/host.1 b/bin/dig/host.1
index 94af9bf..4f465f3 100644
--- a/bin/dig/host.1
+++ b/bin/dig/host.1
@@ -217,8 +217,18 @@ If
 \fBhost\fR
 has been built with IDN (internationalized domain name) support, it can accept and display non\-ASCII domain names.
 \fBhost\fR
-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
+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, in case
+\fBhost\fR
+was compiled with idnkit library, define the
 \fBIDN_DISABLE\fR
+environment variable. In case
+\fBhost\fR
+was compiled with libidn library, define the
+\fBCHARSET=ASCII\fR
+environment variable. In case
+\fBhost\fR
+was compiled with libidn2 library, define the
+\fBLANG=language[_territory].ASCII\fR
 environment variable. The IDN support is disabled if the variable is set when
 \fBhost\fR
 runs.
diff --git a/bin/dig/host.docbook b/bin/dig/host.docbook
index 430a1c6..33c48b1 100644
--- a/bin/dig/host.docbook
+++ b/bin/dig/host.docbook
@@ -263,8 +263,13 @@
       <command>host</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.
+      If you'd like to turn off the IDN support for some reason, in case
+      <command>host</command> was compiled with idnkit library, define
+      the <envar>IDN_DISABLE</envar> environment variable. In case
+      <command>host</command> was compiled with libidn library, define
+      the <envar>CHARSET=ASCII</envar> environment variable.
+      In case <command>host</command> was compiled with libidn2 library, define
+      the <envar>LANG=language[_territory].ASCII</envar> environment variable.
       The IDN support is disabled if the variable is set when
       <command>host</command> runs.
     </para>
diff --git a/bin/dig/host.html b/bin/dig/host.html
index 2103aae..a4ad016 100644
--- a/bin/dig/host.html
+++ b/bin/dig/host.html
@@ -203,8 +203,13 @@
       <span><strong class="command">host</strong></span> 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 <code class="envar">IDN_DISABLE</code> environment variable.
+      If you'd like to turn off the IDN support for some reason, in case
+      <span><strong class="command">host</strong></span> was compiled with idnkit library, define
+      the <code class="envar">IDN_DISABLE </code> environment variable. In case
+      <span><strong class="command">host</strong></span> was compiled with libidn library, define
+      the <code class="envar">CHARSET=ASCII</code> environment variable. In case
+      <span><strong class="command">host</strong></span> was compiled with libidn2 library, define
+      the <code class="envar">LANG=language[_territory].ASCII</code> environment variable.
       The IDN support is disabled if the variable is set when
       <span><strong class="command">host</strong></span> runs.
     </p>
diff --git a/config.h.in b/config.h.in
index 1561ae5..3ce98c9 100644
--- a/config.h.in
+++ b/config.h.in
@@ -511,7 +511,16 @@ int sigwait(const unsigned int *set, int *sig);
 #undef USE_FIONBIO_IOCTL
 
 /* define if idnkit support is to be included. */
-#undef WITH_IDN
+#undef WITH_IDNKIT
+
+/* define if IDN support is to be included. */
+#undef WITH_IDN_SUPPORT
+
+/* define if libidn support is to be included. */
+#undef WITH_LIBIDN
+
+/* 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 3693a21..03ee2e2 100755
--- a/configure
+++ b/configure
@@ -683,7 +683,9 @@ UNITTESTS
 ATFLIBS
 ATFBIN
 ATFBUILD
-IDNLIBS
+LIBIDN2_LIBS
+LIBIDN_LIBS
+IDNKIT_LIBS
 XSLT_DB2LATEX_ADMONITIONS
 XSLT_DB2LATEX_STYLE
 XSLT_DOCBOOK_MAKETOC_XHTML
@@ -1014,10 +1016,12 @@ enable_rpz_nsip
 enable_rpz_nsdname
 enable_filter_aaaa
 with_docbook_xsl
-with_idn
+with_idnkit
 with_libiconv
 with_iconv
 with_idnlib
+with_libidn
+with_libidn2
 with_atf
 with_tuning
 with_dlopen
@@ -1720,10 +1724,12 @@ Optional Packages:
   --with-readline=LIBSPEC    specify readline library default -lreadline
 
   --with-docbook-xsl=PATH specify path for Docbook-XSL stylesheets
-  --with-idn=MPREFIX      enable IDN support using idnkit default PREFIX
+  --with-idnkit=MPREFIX      enable IDN support using idnkit default PREFIX
   --with-libiconv=IPREFIX GNU libiconv are in IPREFIX default PREFIX
   --with-iconv=LIBSPEC    specify iconv library default -liconv
   --with-idnlib=ARG       specify libidnkit
+  --with-libidn=MPREFIX      enable IDN support using GNU libidn default PREFIX
+  --with-libidn2=MPREFIX      enable IDN support using GNU libidn2 default PREFIX
   --with-atf=ARG          support Automated Test Framework
   --with-tuning=ARG       Specify server tuning (large or default)
   --with-dlopen=ARG       support dynamically loadable DLZ drivers
@@ -20148,28 +20154,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
 
@@ -20226,21 +20232,117 @@ if test "$idnlib" = yes; then
 	as_fn_error $? "You must specify ARG for --with-idnlib." "$LINENO" 5
 fi
 
-IDNLIBS=
-if test "$use_idn" != no; then
+IDNKIT_LIBS=
+if test "$use_idnkit" != no; 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 "$idnlib" != no; 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 libidn
+#
+
+# Check whether --with-libidn was given.
+if test "${with_libidn+set}" = set; then :
+  withval=$with_libidn; use_libidn="$withval"
+else
+  use_libidn="no"
+fi
+
+case "$use_libidn" in
+yes)
+	if test X$prefix = XNONE ; then
+		libidn_path=/usr/local
+	else
+		libidn_path=$prefix
+	fi
+	;;
+no)
+	;;
+*)
+	libidn_path="$use_libidn"
+	;;
+esac
+
+LIBIDN_LIBS=
+if test "$use_libidn" != no; then
+
+$as_echo "#define WITH_LIBIDN 1" >>confdefs.h
+
+	STD_CINCLUDES="$STD_CINCLUDES -I$libidn_path/include"
+	LIBIDN_LIBS="-lidn"
+fi
+
+
+#
+# IDN support using libidn2
+#
+
+
+# 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
+yes)
+	if test X$prefix = XNONE ; then
+		libidn2_path=/usr/local
+	else
+		libidn2_path=$prefix
+	fi
+	;;
+no)
+	;;
+*)
+	libidn2_path="$use_libidn2"
+	;;
+esac
+
+LIBIDN2_LIBS=
+if test "$use_libidn2" != no; then
+
+$as_echo "#define WITH_LIBIDN2 1" >>confdefs.h
+
+	STD_CINCLUDES="$STD_CINCLUDES -I$libidn2_path/include"
+	LIBIDN_LIBS="-lidn2"
+fi
+
+
+#
+# IDN support in general
+#
+
+# check if idnkit, libidn and libidn2 are not used at the same time
+if test "$use_idnkit" != no && test "$use_libidn" != no; then
+    as_fn_error $? "idnkit and libidn cannot be used at the same time." "$LINENO" 5
+fi
+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
+if test "$use_libidn" != no && test "$use_libidn2" != no; then
+    as_fn_error $? "libidn and libidn2 cannot be used at the same time." "$LINENO" 5
+fi
+
+# the IDN support is on
+if test "$use_idnkit" != no || test "$use_libidn" != no || test "$use_libidn2" != no; then
+
+$as_echo "#define WITH_IDN_SUPPORT 1" >>confdefs.h
+
+fi
+
+
+#
 # Check whether to build Automated Test Framework unit tests
 #
 
diff --git a/configure.in b/configure.in
index bd35473..f7af471 100644
--- a/configure.in
+++ b/configure.in
@@ -3898,23 +3898,23 @@ fi
 AC_SUBST(XSLT_DB2LATEX_ADMONITIONS)
 
 #
-# IDN support
+# IDN support using idnkit
 #
-AC_ARG_WITH(idn,
-	[  --with-idn[=MPREFIX]      enable IDN support using idnkit [default PREFIX]],
-	use_idn="$withval", use_idn="no")
-case "$use_idn" in
+AC_ARG_WITH(idnkit,
+	[  --with-idnkit[=MPREFIX]      enable IDN support using idnkit [default PREFIX]],
+	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
 
@@ -3958,17 +3958,97 @@ if test "$idnlib" = yes; then
 	AC_MSG_ERROR([You must specify ARG for --with-idnlib.])
 fi
 
-IDNLIBS=
-if test "$use_idn" != no; 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 "$use_idnkit" != no; then
+	AC_DEFINE(WITH_IDNKIT, 1, [define if idnkit support is to be included.])
+	STD_CINCLUDES="$STD_CINCLUDES -I$idnkit_path/include"
 	if test "$idnlib" != no; 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(IDNLIBS)
+AC_SUBST(IDNKIT_LIBS)
+
+#
+# IDN support using libidn
+#
+AC_ARG_WITH(libidn,
+	[  --with-libidn[=MPREFIX]      enable IDN support using GNU libidn [default PREFIX]],
+	use_libidn="$withval", use_libidn="no")
+case "$use_libidn" in
+yes)
+	if test X$prefix = XNONE ; then
+		libidn_path=/usr/local
+	else
+		libidn_path=$prefix
+	fi
+	;;
+no)
+	;;
+*)
+	libidn_path="$use_libidn"
+	;;
+esac
+
+LIBIDN_LIBS=
+if test "$use_libidn" != no; then
+	AC_DEFINE(WITH_LIBIDN, 1, [define if libidn support is to be included.])
+	STD_CINCLUDES="$STD_CINCLUDES -I$libidn_path/include"
+	LIBIDN_LIBS="-lidn"
+fi
+AC_SUBST(LIBIDN_LIBS)
+
+#
+# IDN support using libidn2
+#
+
+AC_ARG_WITH(libidn2,
+	[  --with-libidn2[=MPREFIX]      enable IDN support using GNU libidn2 [default PREFIX]],
+	use_libidn2="$withval", use_libidn2="no")
+case "$use_libidn2" in
+yes)
+	if test X$prefix = XNONE ; then
+		libidn2_path=/usr/local
+	else
+		libidn2_path=$prefix
+	fi
+	;;
+no)
+	;;
+*)
+	libidn2_path="$use_libidn2"
+	;;
+esac
+
+LIBIDN2_LIBS=
+if test "$use_libidn2" != no; then
+	AC_DEFINE(WITH_LIBIDN2, 1, [define if libidn2 support is to be included.])
+	STD_CINCLUDES="$STD_CINCLUDES -I$libidn2_path/include"
+	LIBIDN_LIBS="-lidn2"
+fi
+AC_SUBST(LIBIDN2_LIBS)
+
+#
+# IDN support in general
+#
+
+# check if idnkit, libidn and libidn2 are not used at the same time
+if test "$use_idnkit" != no && test "$use_libidn" != no; then
+    AC_MSG_ERROR([idnkit and libidn cannot be used at the same time.])
+fi
+if test "$use_idnkit" != no && test "$use_libidn2" != no; then
+    AC_MSG_ERROR([idnkit and libidn2 cannot be used at the same time.])
+fi
+if test "$use_libidn" != no && test "$use_libidn2" != no; then
+    AC_MSG_ERROR([libidn and libidn2 cannot be used at the same time.])
+fi
+
+# the IDN support is on
+if test "$use_idnkit" != no || test "$use_libidn" != no || test "$use_libidn2" != no; then
+	AC_DEFINE(WITH_IDN_SUPPORT, 1, [define if IDN support is to be included.])
+fi
+
 
 #
 # Check whether to build Automated Test Framework unit tests
-- 
1.9.3

