From 089b190e72713c36b89629ab40c56799c7627fb9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Wed, 16 Aug 2017 16:42:23 +0200
Subject: [PATCH 3/4] Removed iconv, convert directly from locale to ACE
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

Document parsing of IDN input

Put idnoptions back into use for idnkit

Signed-off-by: Petr Menšík <pemensik@redhat.com>
---
 bin/dig/dig.c             |  27 ++-
 bin/dig/dig.docbook       |  18 +-
 bin/dig/dighost.c         | 432 +++++++++++++++-------------------------------
 bin/dig/host.c            |  10 +-
 bin/dig/include/dig/dig.h |   3 +-
 config.h.in               |   3 +
 configure.in              |  38 +++-
 7 files changed, 218 insertions(+), 313 deletions(-)

diff --git a/bin/dig/dig.c b/bin/dig/dig.c
index e38d036..42edc64 100644
--- a/bin/dig/dig.c
+++ b/bin/dig/dig.c
@@ -189,7 +189,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"
@@ -1098,12 +1099,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 0b8d6d9..5e20ce9 100644
--- a/bin/dig/dig.docbook
+++ b/bin/dig/dig.docbook
@@ -762,6 +762,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>
@@ -1271,10 +1282,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 f5f167a..cc78853 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -42,7 +42,6 @@
 
 #ifdef WITH_LIBIDN2
 #include <idn2.h>
-#include <iconv.h>
 #endif
 #endif /* WITH_IDN_SUPPORT */
 
@@ -159,59 +158,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);
-static isc_result_t idn_utf8_to_ace(const char *from,
+
+#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 WITH_LIBIDN
+static isc_result_t libidn_locale_to_ace(const char *from,
 		char *to,
 		size_t tolen);
+
+#elif 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,
-		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 */
+#endif /* WITH_IDN_OUT_SUPPORT */
 
 isc_socket_t *keep = NULL;
 isc_sockaddr_t keepaddr;
@@ -846,7 +841,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;
@@ -966,6 +966,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;
 #ifdef DIG_SIGCHASE
 	looknew->sigchase = lookold->sigchase;
@@ -1530,6 +1531,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)
@@ -2344,11 +2351,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");
@@ -2386,10 +2395,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
 
 	/*
@@ -2400,11 +2414,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;
@@ -2415,21 +2425,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,
@@ -2437,8 +2432,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);
@@ -2448,7 +2453,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) {
@@ -2459,8 +2464,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 &&
@@ -2485,20 +2490,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,
@@ -4644,47 +4642,34 @@ 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) {
-#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) {
+idn_locale_to_ace(const char *from, char *to, size_t tolen) {
 #ifdef WITH_IDNKIT
-	return (idnkit_utf8_to_ace(from, to, tolen));
+	return (idnkit_locale_to_ace(from, to, tolen));
 #elif WITH_LIBIDN
-	return (libidn_utf8_to_ace(from, to, tolen));
+	return (libidn_locale_to_ace(from, to, tolen));
 #else /* WITH_LIBIDN2 */
-	return (libidn2_utf8_to_ace(from, to, tolen));
+	return (libidn2_locale_to_ace(from, to, tolen));
 #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
@@ -4750,75 +4735,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)strcpy(name + namelen, origin);
-
-	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
@@ -4826,69 +4775,52 @@ 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;
 		}
-
-		(void) strncpy(to, tmp_str, tolen);
-
-		result = ISC_R_SUCCESS;
 	}
+	else
+		return ISC_R_FAILURE;
 
-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);
-
+	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_FAILURE;
-			goto cleanup;
+			result = ISC_R_NOSPACE;
+		} else {
+			(void) strncpy(to, ace_str, tolen);
+			result = ISC_R_SUCCESS;
 		}
-
-		(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);
+	free(ace_str);
+	if (res != IDNA_SUCCESS) {
+		fatal("idna_to_ascii_8z failed: %s", idna_strerror(res));
+	}
 	return (result);
 }
 
@@ -4912,7 +4844,7 @@ libidn_ace_to_locale(const char *from, char *to, size_t tolen) {
 
 		result = ISC_R_SUCCESS;
 	} else {
-		debug("libidn idna_to_unicode_8z8l failed: %s",
+		debug("idna_to_unicode_8z8l failed: %s",
 		      idna_strerror(res));
 		result = ISC_R_FAILURE;
 	}
@@ -4925,139 +4857,59 @@ cleanup:
 
 #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) {
+libidn2_locale_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);
+	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");
-			result = ISC_R_FAILURE;
-			goto cleanup;
+			idn2_free(tmp_str);
+			return ISC_R_NOSPACE;
 		}
 
-		(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;
+		(void) strcpy(to, tmp_str);
+		idn2_free(tmp_str);
+		return ISC_R_SUCCESS;
 	}
 
-cleanup:
-	free(tmp_str);
-	return (result);
+	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) {
-	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;
-	}
+	int res;
+	char *tmp_str = NULL;
 
-	from_str_p = from_tmp_str;
-	to_str_p = to;
-	inbytesleft = strlen(from);
-	outbytesleft = tolen - 1;
+	res = idn2_to_unicode_8zlz(from, &tmp_str,
+			       IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT);
 
-	nconv = iconv(a2l, &from_str_p, &inbytesleft, &to_str_p,
-		      &outbytesleft);
+	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;
+		}
 
-	if (nconv == (size_t)(-1)) {
-		debug("iconv failed: (%d) %s", errno, strerror(errno));
-		result = ISC_R_FAILURE;
+		(void) strncpy(to, tmp_str, tolen);
+		free(tmp_str);
+		return ISC_R_SUCCESS;
 	} else {
-		*to_str_p = '\0';
-		result = ISC_R_SUCCESS;
+		debug("libidn idna_to_ascii_8z failed: %s",
+		      idn2_strerror(res));
 	}
 
-	iconv_close(a2l);
-
-cleanup:
-	free(from_tmp_str);
-	return (result);
+	return ISC_R_FAILURE;
 }
+#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 ed075b3..d7f278c 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>
@@ -731,7 +731,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) {
@@ -746,7 +746,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) {
@@ -781,7 +781,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;
@@ -897,7 +897,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 b24daa1..7805938 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -138,6 +138,7 @@ struct dig_lookup {
 		use_usec,
 		nocrypto,
 		ttlunits,
+		idnin,
 		idnout,
 		qr;
 #ifdef DIG_SIGCHASE
@@ -302,7 +303,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 89fdb14..0f36d5b 100644
--- a/config.h.in
+++ b/config.h.in
@@ -588,6 +588,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 7967f19..1f29a8f 100644
--- a/configure.in
+++ b/configure.in
@@ -4670,7 +4670,7 @@ NOM_PATH_FILE(XSLT_DBLATEX_FASTBOOK, xsl/latex_book_fast.xsl, $dblatex_xsl_trees
 # IDN support using idnkit
 #
 AC_ARG_WITH(idnkit,
-	[  --with-idnkit[=MPREFIX]   enable IDN support using idnkit [default PREFIX]],
+	[  --with-idnkit[=PATH]      enable IDN support using idnkit yes|no|path],
 	use_idnkit="$withval", use_idnkit="no")
 case "$use_idnkit" in
 yes)
@@ -4742,8 +4742,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]],
+	[  --with-libidn[=PATH]      enable IDN support using GNU libidn yes|no|path],
 	use_libidn="$withval", use_libidn="no")
 case "$use_libidn" in
 yes)
@@ -4752,19 +4753,19 @@ 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)
 
@@ -4772,8 +4773,9 @@ AC_SUBST(LIBIDN_LIBS)
 # IDN support using libidn2
 #
 
+LIBIDN2_LIBS=
 AC_ARG_WITH(libidn2,
-	[  --with-libidn2[=MPREFIX]  enable IDN support using GNU libidn2 [default PREFIX]],
+	[  --with-libidn2[=PATH]     enable IDN support using GNU libidn2 yes|no|path],
 	use_libidn2="$withval", use_libidn2="no")
 case "$use_libidn2" in
 yes)
@@ -4782,19 +4784,36 @@ yes)
 	else
 		libidn2_path=$prefix
 	fi
+	LIBIDN2_LIBS="-lidn2"
 	;;
 no)
 	;;
 *)
 	libidn2_path="$use_libidn2"
+	LIBIDN2_LIBS="-L$libidn2_path/lib -lidn2"
 	;;
 esac
 
-LIBIDN2_LIBS=
 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"
-	LIBIDN_LIBS="-lidn2"
+	LIBS="$saved_libs"
+	CFLAGS="$saved_cflags"
 fi
 AC_SUBST(LIBIDN2_LIBS)
 
@@ -4815,7 +4834,10 @@ 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.])
+	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
 
 #
-- 
2.9.4

