From 58d359e1905af0086d5aa734e664cf409580962c 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

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>
---
 bin/dig/Makefile.in |   4 +-
 bin/dig/dighost.c   | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure.in        |  40 ++++++++++++-
 3 files changed, 199 insertions(+), 4 deletions(-)

diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in
index eef03bf..6a17c59 100644
--- a/bin/dig/Makefile.in
+++ b/bin/dig/Makefile.in
@@ -38,10 +38,10 @@ DEPLIBS =	${DNSDEPLIBS} ${BIND9DEPLIBS} ${ISCDEPLIBS} \
 		${ISCCFGDEPLIBS} ${LWRESDEPLIBS}
 
 LIBS =		${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBS@
+		${ISCLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 NOSYMLIBS =	${LWRESLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \
-		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBS@
+		${ISCNOSYMLIBS} @IDNKIT_LIBS@ @LIBIDN_LIBS@ @LIBIDN2_LIBS@ @LIBS@
 
 SUBDIRS =
 
diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 50354c9..f5f167a 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -39,6 +39,11 @@
 #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>
@@ -194,6 +199,17 @@ static isc_result_t libidn_utf8_to_ace(const char *from,
 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 */
 
@@ -4653,6 +4669,8 @@ idn_locale_to_utf8(const char *from, char *to, size_t tolen) {
 	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
 }
 
@@ -4662,6 +4680,8 @@ idn_utf8_to_ace(const char *from, char *to, size_t tolen) {
 	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
 }
 
@@ -4671,6 +4691,8 @@ idn_ace_to_locale(const char *from, char *to, size_t tolen) {
 	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
 }
 
@@ -4900,6 +4922,143 @@ cleanup:
 	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
diff --git a/configure.in b/configure.in
index 13d68bc..7967f19 100644
--- a/configure.in
+++ b/configure.in
@@ -4769,16 +4769,52 @@ 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 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
+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
 
-- 
2.9.4

