From 941af239052063dc5f370941a40ec40cb5f7359b Mon Sep 17 00:00:00 2001
From: Tomas Hozza <thozza@redhat.com>
Date: Wed, 23 Sep 2015 16:47:37 +0200
Subject: [PATCH 2/4] Add support for libidn2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

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.

Signed-off-by: Petr Menšík <pemensik@redhat.com>
---
 bin/dig/Makefile.in       |   4 +-
 bin/dig/dig.c             |  27 +++-
 bin/dig/dig.docbook       |  18 ++-
 bin/dig/dighost.c         | 349 ++++++++++++++++++++++++----------------------
 bin/dig/host.c            |  10 +-
 bin/dig/include/dig/dig.h |   3 +-
 config.h.in               |   3 +
 configure.in              |  76 ++++++++--
 8 files changed, 299 insertions(+), 191 deletions(-)

diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in
index 8be3b4f..d094b85 100644
--- a/bin/dig/Makefile.in
+++ b/bin/dig/Makefile.in
@@ -38,10 +38,10 @@ DEPLIBS =	${DNSDEPLIBS} ${IRSDEPLIBS} ${BIND9DEPLIBS} \
 		${ISCDEPLIBS} ${ISCCFGDEPLIBS}
 
 LIBS =		${DNSLIBS} ${IRSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBS@
+		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 NOSYMLIBS =	${DNSLIBS} ${IRSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBS@
+		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 SUBDIRS =
 
diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index ec923ac..50296dd 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]keepalive      (Request EDNS TCP keepalive)\n"
 "                 +[no]keepopen       (Keep the TCP socket open between queries)\n"
@@ -1042,12 +1043,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 eca77a5..b56498c 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -764,6 +764,17 @@
 	</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>
 	    <para>
@@ -1274,10 +1285,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 033c666..361e3d3 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -39,6 +39,10 @@
 #include <idna.h>
 #include <stringprep.h>
 #endif
+
+#ifdef WITH_LIBIDN2
+#include <idn2.h>
+#endif
 #endif /* WITH_IDN_SUPPORT */
 
 #include <dns/byaddr.h>
@@ -141,48 +145,55 @@ static char servercookie[256];
 
 #ifdef WITH_IDN_SUPPORT
 static void idn_initialize(void);
-static isc_result_t idn_locale_to_utf8(const char *from,
+static isc_result_t idn_locale_to_ace(const char *from,
+		char *to,
+		size_t tolen);
+
+#ifdef WITH_IDNKIT
+static isc_result_t idnkit_initialize(void);
+static isc_result_t idnkit_locale_to_ace(const char *from,
+		char *to,
+		size_t tolen);
+
+#elif defined(WITH_LIBIDN)
+static isc_result_t libidn_locale_to_ace(const char *from,
 		char *to,
 		size_t tolen);
-static isc_result_t idn_utf8_to_ace(const char *from,
+
+#elif defined(WITH_LIBIDN2)
+static isc_result_t libidn2_locale_to_ace(const char *from,
 		char *to,
 		size_t tolen);
+#endif
+#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);
-static isc_result_t append_textname(char *name,
-		const char *origin,
-		size_t namesize);
-
 #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);
+int idnoptions = 0;
+
 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,
+#elif defined(WITH_LIBIDN)
+static isc_result_t libidn_ace_to_locale(const char *from,
 		char *to,
 		size_t tolen);
-static isc_result_t libidn_ace_to_locale(const char *from,
+
+#elif defined(WITH_LIBIDN2)
+static isc_result_t libidn2_ace_to_locale(const char *from,
 		char *to,
 		size_t tolen);
 #endif
-#endif /* WITH_IDN_SUPPORT */
+#endif /* WITH_IDN_OUT_SUPPORT */
 
 isc_socket_t *keep = NULL;
 isc_sockaddr_t keepaddr;
@@ -683,7 +694,12 @@ make_empty_lookup(void) {
 	looknew->ttlunits = ISC_FALSE;
 	looknew->ttlunits = ISC_FALSE;
 	looknew->qr = ISC_FALSE;
-#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;
@@ -828,6 +844,7 @@ clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
 	looknew->nocrypto = lookold->nocrypto;
 	looknew->ttlunits = lookold->ttlunits;
 	looknew->qr = lookold->qr;
+	looknew->idnin = lookold->idnin;
 	looknew->idnout = lookold->idnout;
 	looknew->udpsize = lookold->udpsize;
 	looknew->edns = lookold->edns;
@@ -1335,6 +1352,12 @@ setup_system(isc_boolean_t ipv4only, isc_boolean_t ipv6only) {
 	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)
 		setup_file_key();
 	else if (keysecret[0] != 0)
@@ -2063,11 +2086,13 @@ setup_lookup(dig_lookup_t *lookup) {
 	char store[MXNAME];
 	char ecsbuf[20];
 	char cookiebuf[256];
+	char *origin = NULL;
+	char *textname = NULL;
 #ifdef WITH_IDN_SUPPORT
-	char utf8_textname[MXNAME], utf8_origin[MXNAME], idn_textname[MXNAME];
+	char idn_origin[MXNAME], idn_textname[MXNAME];
 #endif
 
-#ifdef WITH_IDN_SUPPORT
+#ifdef WITH_IDN_OUT_SUPPORT
 	result = dns_name_settotextfilter(lookup->idnout ?
 					  output_filter : NULL);
 	check_result(result, "dns_name_settotextfilter");
@@ -2105,10 +2130,15 @@ setup_lookup(dig_lookup_t *lookup) {
 	 * `textname' doesn't contain TLD, but local mapping needs
 	 * TLD.
 	 */
+	textname = lookup->textname;
 #ifdef WITH_IDN_SUPPORT
-	result = idn_locale_to_utf8(lookup->textname, utf8_textname,
-				    sizeof(utf8_textname));
-	check_result(result, "convert textname to UTF-8");
+	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
 
 	/*
@@ -2119,11 +2149,7 @@ 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_SUPPORT
-		if ((count_dots(utf8_textname) >= ndots) || !usesearch)
-#else
-		if ((count_dots(lookup->textname) >= ndots) || !usesearch)
-#endif
+		if ((count_dots(textname) >= ndots) || !usesearch)
 		{
 			lookup->origin = NULL; /* Force abs lookup */
 			lookup->done_as_is = ISC_TRUE;
@@ -2134,21 +2160,6 @@ setup_lookup(dig_lookup_t *lookup) {
 		}
 	}
 
-#ifdef WITH_IDN_SUPPORT
-	if (lookup->origin != NULL) {
-		debug("trying origin %s", lookup->origin->origin);
-		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,
@@ -2156,8 +2167,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);
@@ -2167,7 +2188,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) {
@@ -2178,8 +2199,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 &&
@@ -2204,20 +2225,13 @@ 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_SUPPORT
-			len = (unsigned int) strlen(idn_textname);
-			isc_buffer_init(&b, idn_textname, len);
-#else
-			len = (unsigned int) strlen(lookup->textname);
-			isc_buffer_init(&b, lookup->textname, len);
-#endif
+			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,
@@ -4253,49 +4267,48 @@ destroy_libs(void) {
 #ifdef WITH_IDN_SUPPORT
 static void
 idn_initialize(void) {
+#ifdef WITH_IDNKIT
 	isc_result_t result;
-	char *idn_disable_env = NULL;
+#endif
 
 #ifdef HAVE_SETLOCALE
 	/* Set locale */
 	(void)setlocale(LC_ALL, "");
 #endif
 
-#ifdef HAVE_IDNKIT
+#ifdef WITH_IDNKIT
 	/* Create configuration context. */
 	result = idnkit_initialize();
 	check_result(result, "idnkit initializationt");
 #endif
-
-	/* Set domain name -> text post-conversion filter. */
-	result = dns_name_settotextfilter(output_filter);
-	check_result(result, "dns_name_settotextfilter");
 }
 
 static isc_result_t
-idn_locale_to_utf8(const char *from, char *to, size_t tolen) {
+idn_locale_to_ace(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));
-#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));
+	return (idnkit_locale_to_ace(from, to, tolen));
+#elif defined(WITH_LIBIDN)
+	return (libidn_locale_to_ace(from, to, tolen));
+#elif defined(WITH_LIBIDN2)
+	return (libidn2_locale_to_ace(from, to, tolen));
+#else
+	/* Broken configure if this is reached. */
+	return (ISC_R_NOTIMPLEMENTED);
 #endif
 }
 
+#ifdef WITH_IDN_OUT_SUPPORT
 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
+#elif defined(WITH_LIBIDN)
 	return (libidn_ace_to_locale(from, to, tolen));
+#elif defined(WITH_LIBIDN2)
+	return (libidn2_ace_to_locale(from, to, tolen));
+#else
+	/* Broken configure if this is reached. */
+	return (ISC_R_NOTIMPLEMENTED);
 #endif
 }
 
@@ -4352,73 +4365,39 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
 
 	return (ISC_R_SUCCESS);
 }
+#endif
 
-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 (ISC_R_SUCCESS);
-
-	/* Append dot and origin */
-	if (namelen + 1 + originlen >= namesize) {
-		debug("append_textname failure: name + origin is too long");
-		return (ISC_R_FAILURE);
+#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));
 	}
-
-	if (*origin != '.')
-		name[namelen++] = '.';
-	(void)strlcpy(name + namelen, origin, namesize - namelen);
-	return (ISC_R_SUCCESS);
 }
 
-#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);
-	}
+	idnkit_check_result(result, "idnkit api initialization failed");
+	return (ISC_R_SUCCESS);
 }
 
 static isc_result_t
-idnkit_utf8_to_ace(const char *from, char *to, size_t tolen) {
+idnkit_locale_to_ace(const char *from, char *to, size_t tolen) {
+	char utf8_textname[MXNAME];
 	idn_result_t result;
 
-	result = idn_encodename(IDN_LOCALMAP | IDN_NAMEPREP |
-			    IDN_IDNCONV | IDN_LENCHECK, from, to, tolen);
+	result = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP, from,
+	                        utf8_textname, sizeof(utf8_textname));
+	idnkit_check_result(result, "idnkit idn_encodename to utf8 failed");
 
-	if (result == idn_success) {
-		return (ISC_R_SUCCESS);
-	} else {
-		debug("idnkit idn_encodename failed: %s",
-		      idn_result_tostring(result));
-		return (ISC_R_FAILURE);
-	}
+	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);
 }
 
 static isc_result_t
@@ -4426,54 +4405,67 @@ 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 {
+	if (result != idn_success) {
 		debug("idnkit idn_decodename failed: %s",
-		      idn_result_tostring(result) );
+		      idn_result_tostring(result));
 		return (ISC_R_FAILURE);
 	}
+	return (ISC_R_SUCCESS);
 }
 #endif /* WITH_IDNKIT */
 
 #ifdef WITH_LIBIDN
 static isc_result_t
-libidn_locale_to_utf8(const char *from, char *to, size_t tolen) {
+libidn_locale_to_ace(const char *from, char *to, size_t tolen) {
 	isc_result_t result = ISC_R_FAILURE;
+	int res;
 	char *tmp_str = NULL;
+	char *ace_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;
+			free(tmp_str);
+			return ISC_R_NOSPACE;
 		}
+	}
+	else
+		return ISC_R_FAILURE;
 
-		(void) strncpy(to, tmp_str, tolen);
-
-		result = ISC_R_SUCCESS;
+	res = idna_to_ascii_8z(tmp_str, &ace_str, 0);
+	if (res == IDNA_SUCCESS) {
+		/* check the length */
+		if (strlen(tmp_str) >= tolen) {
+			debug("encoded ASC string is too long");
+			result = ISC_R_NOSPACE;
+		} else {
+			(void) strncpy(to, ace_str, tolen);
+			result = ISC_R_SUCCESS;
+		}
 	}
 
-cleanup:
 	free(tmp_str);
+	free(ace_str);
+	if (res != IDNA_SUCCESS) {
+		fatal("idna_to_ascii_8z failed: %s", idna_strerror(res));
+	}
 	return (result);
 }
 
 static isc_result_t
-libidn_utf8_to_ace(const char *from, char *to, size_t tolen) {
+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_ascii_8z(from, &tmp_str, 0);
+	res = idna_to_unicode_8zlz(from, &tmp_str, 0);
 
 	if (res == IDNA_SUCCESS) {
 		/* check the length */
 		if (strlen(tmp_str) >= tolen) {
-			debug("encoded ASC string is too long");
+			debug("decoded locale string is too long");
 			result = ISC_R_FAILURE;
 			goto cleanup;
 		}
@@ -4482,7 +4474,7 @@ libidn_utf8_to_ace(const char *from, char *to, size_t tolen) {
 
 		result = ISC_R_SUCCESS;
 	} else {
-		debug("libidn idna_to_ascii_8z failed: %s",
+		debug("idna_to_unicode_8z8l failed: %s",
 		      idna_strerror(res));
 		result = ISC_R_FAILURE;
 	}
@@ -4491,35 +4483,62 @@ cleanup:
 	free(tmp_str);
 	return (result);
 }
+#endif /* WITH_LIBIDN */
 
+#ifdef WITH_LIBIDN2
 static isc_result_t
-libidn_ace_to_locale(const char *from, char *to, size_t tolen) {
+libidn2_locale_to_ace(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);
+	res = idn2_lookup_ul(from, &tmp_str, IDN2_NONTRANSITIONAL);
+	if (res == IDN2_DISALLOWED)
+		res = idn2_lookup_ul(from, &tmp_str, IDN2_TRANSITIONAL);
 
-	if (res == IDNA_SUCCESS) {
+	if (res == IDN2_OK) {
 		/* check the length */
 		if (strlen(tmp_str) >= tolen) {
-			debug("decoded locale string is too long");
-			result = ISC_R_FAILURE;
-			goto cleanup;
+			debug("ACE string is too long");
+			idn2_free(tmp_str);
+			return ISC_R_NOSPACE;
 		}
 
-		(void) strncpy(to, tmp_str, tolen);
+		(void) strlcpy(to, tmp_str, tolen);
+		idn2_free(tmp_str);
+		return ISC_R_SUCCESS;
+	}
 
-		result = 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
+libidn2_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("libidn idna_to_unicode_8z8l failed: %s",
-		      idna_strerror(res));
-		result = ISC_R_FAILURE;
+		debug("idn2_to_unicode_8zlz failed: %s",
+		      idn2_strerror(res));
 	}
 
-cleanup:
-	free(tmp_str);
-	return (result);
+	return ISC_R_FAILURE;
 }
-#endif /* WITH_LIBIDN */
+#endif /* WITH_IDN_OUT_SUPPORT */
+#endif /* WITH_LIBIDN2 */
 #endif /* WITH_IDN_SUPPORT */
diff --git a/bin/dig/host.c b/bin/dig/host.c
index 1380047..91adbf6 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>
@@ -718,7 +718,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) {
@@ -733,7 +733,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) {
@@ -768,7 +768,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;
@@ -882,7 +882,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 748a0a5..f3f507b 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -126,6 +126,7 @@ struct dig_lookup {
 		use_usec,
 		nocrypto,
 		ttlunits,
+		idnin,
 		idnout,
 		qr;
 	char textname[MXNAME]; /*% Name we're going to be looking up */
@@ -265,7 +266,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 03ead78..608d39e 100644
--- a/config.h.in
+++ b/config.h.in
@@ -605,6 +605,9 @@ int sigwait(const unsigned int *set, int *sig);
 /* define if idnkit support is to be included. */
 #undef WITH_IDN
 
+#undef WITH_IDN_OUT_SUPPORT
+
+/* define if IDN input support is to be included. */
 /* 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.in b/configure.in
index 3e4b035..b56f005 100644
--- a/configure.in
+++ b/configure.in
@@ -4730,8 +4730,8 @@ NOM_PATH_FILE(XSLT_DBLATEX_FASTBOOK, xsl/latex_book_fast.xsl, $dblatex_xsl_trees
 # IDN support using idnkit
 #
 AC_ARG_WITH(idnkit,
-	    AS_HELP_STRING([--with-idn[=PREFIX]],
-			   [enable IDN support using idnkit [default PREFIX]]),
+	    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)
@@ -4805,8 +4805,9 @@ AC_SUBST(IDNKIT_LIBS)
 #
 # IDN support using libidn
 #
+LIBIDN_LIBS=
 AC_ARG_WITH(libidn,
-	[  --with-libidn[=MPREFIX]   enable IDN support using GNU libidn [default PREFIX]],
+	AS_HELP_STRING([--with-libidn[=PATH]], [enable IDN support using GNU libidn [yes|no|path]]),
 	use_libidn="$withval", use_libidn="no")
 case "$use_libidn" in
 yes)
@@ -4815,41 +4816,98 @@ yes)
 	else
 		libidn_path=$prefix
 	fi
+	LIBIDN_LIBS="-lidn"
 	;;
 no)
 	;;
 *)
+	LIBIDN_LIBS="-L$use_libidn/lib -lidn"
 	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
+#
+
+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")
+case "$use_libidn2" in
+yes)
+	if test X$prefix = XNONE ; then
+		libidn2_path=/usr/local
+	else
+		libidn2_path=$prefix
+	fi
+	LIBIDN2_LIBS="-lidn2"
+	;;
+no)
+	;;
+*)
+	libidn2_path="$use_libidn2"
+	LIBIDN2_LIBS="-L$libidn2_path/lib -lidn2"
+	;;
+esac
+
+if test "$use_libidn2" != no; then
+	AC_DEFINE(WITH_LIBIDN2, 1, [define if libidn2 support is to be included.])
+	saved_cflags="$CFLAGS"
+	saved_libs="$LIBS"
+	LIBS="$LIBIDN2_LIBS"
+	CFLAGS="-I$libidn2_path/include"
+
+	AC_MSG_NOTICE(checking for idn2_to_unicode_8zlz)
+AC_TRY_LINK([
+#include <idn2.h>
+],[ idn2_to_unicode_8zlz(".", NULL, IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT); ],
+	[AC_MSG_RESULT(yes)
+	use_libidn2_out=yes],
+	[AC_MSG_RESULT(no)
+	use_libidn2_out=no]
+)
+
+	STD_CINCLUDES="$STD_CINCLUDES -I$libidn2_path/include"
+	LIBS="$saved_libs"
+	CFLAGS="$saved_cflags"
+fi
+AC_SUBST(LIBIDN2_LIBS)
+
+#
 # IDN support in general
 #
 
-# check if idnkit and libidn are not used at the same time
+# 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; then
-	AC_DEFINE(WITH_IDN_SUPPORT, 1, [define if IDN support is to be included.])
+if test "$use_idnkit" != no || test "$use_libidn" != 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
 
 #
 # Check whether to build Automated Test Framework unit tests
 #
 AC_ARG_WITH(atf,
-	    AS_HELP_STRING([--with-atf],[support Automated Test Framework]),
+	    AS_HELP_STRING([--with-atf=ARG],[support Automated Test Framework]),
 	atf="$withval", atf="no")
 if test "yes" = "$atf"; then
 	atf=`pwd`/unit/atf
-- 
2.9.5

