Commit 94757c15 authored by Petr Menšík's avatar Petr Menšík Committed by Ondřej Surý

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.

Remove immediate idn_locale_to_ace and idn_ace_to_locale.
Signed-off-by: Petr Menšík's avatarPetr Menšík <pemensik@redhat.com>
parent 505f6734
......@@ -190,7 +190,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"
......@@ -1045,12 +1046,28 @@ plus_option(const char *option, isc_boolean_t is_batchfile,
lookup->identify = state;
break;
case 'n':
FULLCHECK("idnout");
switch (cmd[3]) {
case 'i':
FULLCHECK("idnin");
#ifndef WITH_IDN_SUPPORT
fprintf(stderr, ";; IDN support not enabled\n");
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;
......
......@@ -776,6 +776,17 @@
</listitem>
</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>
......@@ -1288,10 +1299,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>
......
......@@ -148,59 +148,31 @@ static char servercookie[256];
#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,
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);
#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);
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,
int idnoptions = 0;
#endif
static isc_result_t idn_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;
......@@ -701,7 +673,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;
......@@ -846,6 +823,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;
......@@ -1353,6 +1331,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)
......@@ -2081,11 +2065,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");
......@@ -2123,10 +2109,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
/*
......@@ -2137,11 +2128,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;
......@@ -2152,26 +2139,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));
if (lookup->origin != NULL && result == ISC_R_NOSPACE) {
dns_message_puttempname(lookup->sendmsg,
&lookup->name);
return ISC_FALSE;
}
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,
......@@ -2179,8 +2146,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);
......@@ -2190,7 +2167,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) {
......@@ -2201,8 +2178,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 &&
......@@ -2227,20 +2204,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,
......@@ -4286,57 +4256,23 @@ destroy_libs(void) {
#ifdef WITH_IDN_SUPPORT
static void
idn_initialize(void) {
#ifdef WITH_IDNKIT
isc_result_t result;
#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) {
#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
}
#ifdef WITH_IDN_OUT_SUPPORT
static isc_result_t
output_filter(isc_buffer_t *buffer, unsigned int used_org,
isc_boolean_t absolute)
......@@ -4369,8 +4305,9 @@ output_filter(isc_buffer_t *buffer, unsigned int used_org,
* Convert contents of 'tmp1' to local encoding.
*/
result = idn_ace_to_locale(tmp1, tmp2, sizeof(tmp2));
if (result != ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
return (ISC_R_SUCCESS);
}
strlcpy(tmp1, tmp2, MAXDLEN);
/*
......@@ -4390,146 +4327,97 @@ 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_NOSPACE);
#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));
if (result == idn_invalid_length)
return (ISC_R_NOSPACE);
else
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) {
idn_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
idnkit_ace_to_locale(const char *from, char *to, size_t tolen) {
idn_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) {
idn_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;
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");
return ISC_R_FAILURE;
result = ISC_R_NOSPACE;
} else {
(void) strncpy(to, ace_str, tolen);
result = ISC_R_SUCCESS;
}
(void) strncpy(to, tmp_str, tolen);
free(tmp_str);
return ISC_R_SUCCESS;
} else {
debug("libidn idna_to_ascii_8z failed: %s",
idna_strerror(res));
}
return ISC_R_FAILURE;
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_ace_to_locale(const char *from, char *to, size_t tolen) {
idn_ace_to_locale(const char *from, char *to, size_t tolen) {
int res;
isc_result_t result;
char *tmp_str = NULL;
......@@ -4548,7 +4436,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;
}
......@@ -4560,9 +4448,8 @@ cleanup:
#endif /* WITH_LIBIDN */
#ifdef WITH_LIBIDN2
/* Converts name from locale directly into ACE format, skip UTF-8 step */
static isc_result_t
libidn2_locale_to_utf8(const char *from, char *to, size_t tolen) {
idn_locale_to_ace(const char *from, char *to, size_t tolen) {
int res;
char *tmp_str = NULL;
......@@ -4574,40 +4461,22 @@ libidn2_locale_to_utf8(const char *from, char *to, size_t tolen) {
/* check the length */
if (strlen(tmp_str) >= tolen) {
debug("ACE string is too long");
free(tmp_str);
return ISC_R_FAILURE;
idn2_free(tmp_str);
return ISC_R_NOSPACE;
}
(void) strncpy(to, tmp_str, tolen);
free(tmp_str);
(void) strlcpy(to, tmp_str, tolen);
idn2_free(tmp_str);
return ISC_R_SUCCESS;
}
debug("libidn2 idn2_lookup_u8 failed: %s", idn2_strerror(res));
fatal("idn2_lookup_ul failed: %s", idn2_strerror(res));
return ISC_R_FAILURE;
}
#ifdef WITH_IDN_OUT_SUPPORT
static isc_result_t
libidn2_utf8_to_ace(const char *from, char *to, size_t tolen) {
int res;
/* Just check the format again. */
res = idn2_to_unicode_8zlz(from, NULL,
IDN2_NONTRANSITIONAL|IDN2_NFC_INPUT);
if (res == IDN2_OK) {
strncpy(to, from, tolen);
return ISC_R_SUCCESS;
}
debug("libidn2 idn2_to_unicode_8zlz failed: %s", idn2_strerror(res));
if (res == IDN2_TOO_BIG_DOMAIN)
return ISC_R_NOSPACE;
else
return ISC_R_FAILURE;
}
static isc_result_t
libidn2_ace_to_locale(const char *from, char *to, size_t tolen) {
idn_ace_to_locale(const char *from, char *to, size_t tolen) {
int res;
char *tmp_str = NULL;
......@@ -4626,11 +4495,12 @@ libidn2_ace_to_locale(const char *from, char *to, size_t tolen) {
free(tmp_str);
return ISC_R_SUCCESS;
} else {
debug("libidn idna_to_ascii_8z failed: %s",
debug("idn2_to_unicode_8zlz failed: %s",
idn2_strerror(res));
}
return ISC_R_FAILURE;
}
#endif /* WITH_IDN_OUT_SUPPORT */
#endif /* WITH_LIBIDN2 */
#endif /* WITH_IDN_SUPPORT */
......@@ -19,7 +19,7 @@
#include <locale.h>
#endif
#ifdef WITH_IDN
#ifdef WITH_IDNKIT
#include <idn/result.h>
#include <idn/log.h>
#include <idn/resconf.h>
......
......@@ -129,6 +129,7 @@ struct dig_lookup {
use_usec,
nocrypto,
ttlunits,
idnin,
idnout,
qr;
char textname[MXNAME]; /*% Name we're going to be looking up */
......@@ -268,7 +269,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
......
......@@ -599,6 +599,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
......
......@@ -4701,8 +4701,8 @@ NOM_PATH_FILE(XSLT_DBLATEX_FASTBOOK, xsl/latex_book_fast.xsl, $dblatex_xsl_trees