IDN support in dig and host utilities

Adam Tkac atkac at redhat.com
Tue Dec 11 17:08:18 UTC 2007


Hi,

some time ago I tried add IDN support to dig and host utilities in
Fedora. After investigation I've found that idnkit which is shipped
in contrib subdirectory is completely unusable. It's old source,
project is not maintained (not sure) and I'm not able to compile it
without heavy patching on modern system. So I've written small patch which
adds support to use libidn (http://www.gnu.org/software/libidn/) which
is good alternative to idnkit. What is your opinion about that? Would
it be possible include it to main source?

Regards, Adam

-- 
Adam Tkac, Red Hat, Inc.
-------------- next part --------------
--- dighost.c.libidn	2007-06-19 01:47:17.000000000 +0200
+++ dighost.c	2007-12-11 17:49:45.000000000 +0100
@@ -44,6 +44,11 @@
 #include <idn/api.h>
 #endif
 
+#ifdef WITH_LIBIDN
+#include <stringprep.h>
+#include <idna.h>
+#endif
+
 #include <dns/byaddr.h>
 #ifdef DIG_SIGCHASE
 #include <dns/dnssec.h>
@@ -147,6 +152,14 @@
 int  idnoptions	= 0;
 #endif
 
+#ifdef WITH_LIBIDN
+static isc_result_t	libidn_locale_to_utf8 (const char* from, char *to);
+static isc_result_t	libidn_utf8_to_ascii (const char* from, char *to);
+static isc_result_t	output_filter (isc_buffer_t *buffer,
+				       unsigned int used_org,
+				       isc_boolean_t absolute);
+#endif
+
 /*%
  * Exit Codes:
  *
@@ -1002,6 +1015,9 @@
 setup_system(void) {
 	dig_searchlist_t *domain = NULL;
 	lwres_result_t lwresult;
+#ifdef WITH_LIBIDN
+	isc_result_t result;
+#endif
 
 	debug("setup_system()");
 
@@ -1050,8 +1066,15 @@
 
 #ifdef WITH_IDN
 	initialize_idn();
+	
+#endif
+#ifdef WITH_LIBIDN
+	result = dns_name_settotextfilter(output_filter);
+	check_result(result, "dns_name_settotextfilter");
+#ifdef HAVE_SETLOCALE
+	setlocale (LC_ALL, "");
+#endif
 #endif
-
 	if (keyfile[0] != 0)
 		setup_file_key();
 	else if (keysecret[0] != 0)
@@ -1740,12 +1763,14 @@
 	idn_result_t mr;
 	char utf8_textname[MXNAME], utf8_origin[MXNAME], idn_textname[MXNAME];
 #endif
+#ifdef WITH_LIBIDN
+	char utf8_str[MXNAME], utf8_name[MXNAME], ascii_name[MXNAME];
+#endif
 
-#ifdef WITH_IDN
+#if defined (WITH_IDN) || defined (WITH_LIBIDN)
 	result = dns_name_settotextfilter(output_filter);
 	check_result(result, "dns_name_settotextfilter");
 #endif
-
 	REQUIRE(lookup != NULL);
 	INSIST(!free_now);
 
@@ -1782,6 +1807,14 @@
 	mr = idn_encodename(IDN_LOCALCONV | IDN_DELIMMAP, lookup->textname,
 			    utf8_textname, sizeof(utf8_textname));
 	idn_check_result(mr, "convert textname to UTF-8");
+#elif defined (WITH_LIBIDN)
+	result = libidn_locale_to_utf8 (lookup->textname, utf8_str);
+	check_result (result, "convert textname to UTF-8");
+	len = strlen (utf8_str);
+	if (len < MXNAME)
+		(void) strcpy (utf8_name, utf8_str);
+	else
+		fatal ("Too long name");
 #endif
 
 	/*
@@ -1794,15 +1827,11 @@
 	if (lookup->new_search) {
 #ifdef WITH_IDN
 		if ((count_dots(utf8_textname) >= ndots) || !usesearch) {
-			lookup->origin = NULL; /* Force abs lookup */
-			lookup->done_as_is = ISC_TRUE;
-			lookup->need_search = usesearch;
-		} else if (lookup->origin == NULL && usesearch) {
-			lookup->origin = ISC_LIST_HEAD(search_list);
-			lookup->need_search = ISC_FALSE;
-		}
+#elif defined (WITH_LIBIDN)
+		if ((count_dots(utf8_name) >= ndots) || !usesearch) {
 #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;
@@ -1810,7 +1839,6 @@
 			lookup->origin = ISC_LIST_HEAD(search_list);
 			lookup->need_search = ISC_FALSE;
 		}
-#endif
 	}
 
 #ifdef WITH_IDN
@@ -1827,6 +1855,17 @@
 			    IDN_IDNCONV | IDN_LENCHECK, utf8_textname,
 			    idn_textname, sizeof(idn_textname));
 	idn_check_result(mr, "convert UTF-8 textname to IDN encoding");
+#elif defined (WITH_LIBIDN)
+	if (lookup->origin != NULL) {
+		result = libidn_locale_to_utf8 (lookup->origin->origin, utf8_str);
+		check_result (result, "convert origin to UTF-8");
+		if (len + strlen (utf8_str) < MXNAME)
+			(void) strcpy (utf8_name + len, utf8_str);
+		else
+			fatal ("Too long name + origin");
+	}
+
+	result = libidn_utf8_to_ascii (utf8_name, ascii_name);
 #else
 	if (lookup->origin != NULL) {
 		debug("trying origin %s", lookup->origin->origin);
@@ -1883,6 +1922,14 @@
 						   dns_rootname,
 						   ISC_FALSE,
 						   &lookup->namebuf);
+#elif defined (WITH_LIBIDN)
+			len = strlen (ascii_name);
+			isc_buffer_init(&b, ascii_name, len);
+			isc_buffer_add(&b, len);
+			result = dns_name_fromtext(lookup->name, &b,
+						   dns_rootname,
+						   ISC_FALSE,
+						   &lookup->namebuf);
 #else
 			len = strlen(lookup->textname);
 			isc_buffer_init(&b, lookup->textname, len);
@@ -3310,7 +3357,7 @@
 	void * ptr;
 	dig_message_t *chase_msg;
 #endif
-#ifdef WITH_IDN
+#if defined (WITH_IDN) || defined (WITH_LIBIDN)
 	isc_result_t result;
 #endif
 
@@ -3349,6 +3396,10 @@
         result = dns_name_settotextfilter(NULL);
         check_result(result, "dns_name_settotextfilter");
 #endif
+#ifdef WITH_LIBIDN
+	result = dns_name_settotextfilter (NULL);
+	check_result(result, "clearing dns_name_settotextfilter");
+#endif
 	dns_name_destroy();
 
 	if (commctx != NULL) {
@@ -3525,6 +3576,88 @@
 	}
 }
 #endif /* WITH_IDN */
+#ifdef WITH_LIBIDN
+static isc_result_t
+libidn_locale_to_utf8 (const char *from, char *to) {
+	char *utf8_str;
+
+	debug ("libidn_locale_to_utf8");
+	utf8_str = stringprep_locale_to_utf8 (from);
+	if (utf8_str != NULL) {
+		(void) strcpy (to, utf8_str);
+		free (utf8_str);
+		return ISC_R_SUCCESS;
+	}
+
+	debug ("libidn_locale_to_utf8: failure");
+	return ISC_R_FAILURE;
+}
+static isc_result_t
+libidn_utf8_to_ascii (const char *from, char *to) {
+	char *ascii;
+	int iresult;
+
+	debug ("libidn_utf8_to_ascii");
+	iresult = idna_to_ascii_8z (from, &ascii, 0);
+	if (iresult != IDNA_SUCCESS) {
+		debug ("idna_to_ascii_8z: %s", idna_strerror (iresult));
+		return ISC_R_FAILURE;
+	}
+
+	(void) strcpy (to, ascii);
+	free (ascii);
+	return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+output_filter (isc_buffer_t *buffer, unsigned int used_org,
+	       isc_boolean_t absolute) {
+
+	char tmp1[MXNAME], *tmp2;
+        size_t fromlen, tolen;
+        isc_boolean_t end_with_dot;
+	int iresult;
+
+	debug ("output_filter");
+
+        fromlen = isc_buffer_usedlength (buffer) - used_org;
+	if (fromlen >= MXNAME)
+		return ISC_R_SUCCESS;
+        memcpy (tmp1, (char *) isc_buffer_base (buffer) + used_org, fromlen);
+        end_with_dot = (tmp1[fromlen - 1] == '.') ? ISC_TRUE : ISC_FALSE;
+        if (absolute && !end_with_dot) {
+                fromlen++;
+		if (fromlen >= MXNAME)
+			return ISC_R_SUCCESS;
+                tmp1[fromlen - 1] = '.';
+        }
+        tmp1[fromlen] = '\0';
+
+	iresult = idna_to_unicode_8z8z (tmp1, &tmp2, 0);
+	if (iresult != IDNA_SUCCESS) {
+		debug ("output_filter: %s", idna_strerror (iresult));
+		return ISC_R_SUCCESS;
+	}
+
+	(void) strcpy (tmp1, tmp2);
+	free (tmp2);
+
+        tolen = strlen (tmp1);
+        if (absolute && !end_with_dot && tmp1[tolen - 1] == '.')
+                tolen--;
+
+        if (isc_buffer_length (buffer) < used_org + tolen)
+                return ISC_R_NOSPACE;
+
+	debug ("%s", tmp1);
+
+        isc_buffer_subtract (buffer, isc_buffer_usedlength (buffer) - used_org);
+        memcpy (isc_buffer_used (buffer), tmp1, tolen);
+        isc_buffer_add (buffer, tolen);
+
+        return ISC_R_SUCCESS;
+}
+#endif /* WITH_LIBIDN*/
 
 #ifdef DIG_SIGCHASE
 void


More information about the bind-workers mailing list