Commit 7c3d48c9 authored by Daniel Salzman's avatar Daniel Salzman

utils: add IDN support

refs #145
parent ac207a54
......@@ -139,8 +139,9 @@ src/libknot/consts.c
src/libknot/consts.h
src/libknot/dname.c
src/libknot/dname.h
src/libknot/dnssec/cleanup.h
src/libknot/dnssec/config.h
src/libknot/dnssec/crypto.c
src/libknot/dnssec/crypto.h
src/libknot/dnssec/key.c
src/libknot/dnssec/key.h
src/libknot/dnssec/nsec-bitmap.h
......
......@@ -285,6 +285,20 @@ AS_IF([test "$with_openssl" != "no"],[
])
])
AC_ARG_WITH(libidn, AC_HELP_STRING([--with-libidn=[DIR]],
[Support IDN (needs GNU Libidn)]),
libidn=$withval, libidn=yes)
if test "$libidn" != "no"; then
if test "$libidn" != "yes"; then
LDFLAGS="${LDFLAGS} -L$libidn/lib"
CPPFLAGS="${CPPFLAGS} -I$libidn/include"
fi
AC_CHECK_HEADER(idna.h, AC_CHECK_LIB(idn, stringprep_check_version,
[libidn=yes LIBS="${LIBS} -lidn"], libidn=no),
libidn=no)
AC_DEFINE(LIBIDN, 1, [Define to 1 if you want IDN support.])
fi
AC_SEARCH_LIBS([pow], [m])
AC_SEARCH_LIBS([pthread_create], [pthread], [], [AC_MSG_ERROR([pthreads not found])])
AC_SEARCH_LIBS([dlopen], [dl])
......
......@@ -159,6 +159,9 @@ Don't use TCP automatically if truncated reply is received.
.BR + [ no ] nsid
Request nameserver identifier (NSID).
.TP
.BR + [ no ] idn
Enable IDN transformation to ASCII and vice versa. IDN support depends on libidn availability during project building!
.TP
.BR + [ no ] edns= N
Use EDNS version (default is 0).
.TP
......@@ -191,8 +194,6 @@ Options \fB\-f\fR and \fB\-m\fR and query options:
.BR + [ no ] topdown ,\ + [ no ] nssearch ,\ + [ no ] trace.
.TP
Per-user file configuration via ${HOME}/.digrc.
.TP
Internationalized domain name (IDN) is not supported.
.SS Differences with regard to ISC dig
Optional port specification has a form of [address]:port instead of address#port.
.TP
......
......@@ -88,6 +88,14 @@ knsupdate_SOURCES = \
utils/nsupdate/nsupdate_exec.c
knsec3hash_SOURCES = \
utils/common/msg.h \
utils/common/msg.c \
utils/common/netio.h \
utils/common/netio.c \
utils/common/resolv.h \
utils/common/resolv.c \
utils/common/params.h \
utils/common/params.c \
utils/nsec3hash/nsec3hash_main.c
libknot_la_SOURCES = \
......
......@@ -59,7 +59,8 @@ const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT = {
.show_ttl = true,
.verbose = false,
.human_ttl = false,
.human_tmstamp = true
.human_tmstamp = true,
.dname_to_str = NULL
};
static void dump_string(rrset_dump_params_t *p, const char *str)
......@@ -834,6 +835,11 @@ static void wire_dname_to_str(rrset_dump_params_t *p)
// Write dname string.
char *dname_str = knot_dname_to_str(p->in);
if (p->style->dname_to_str != NULL) {
char *idn = p->style->dname_to_str(dname_str);
free(dname_str);
dname_str = idn;
}
int ret = snprintf(p->out, p->out_max, "%s", dname_str);
free(dname_str);
if (ret < 0 || (size_t)ret >= p->out_max) {
......@@ -1915,6 +1921,11 @@ int knot_rrset_txt_dump_header(const knot_rrset_t *rrset,
// Dump rrset owner.
char *name = knot_dname_to_str(rrset->owner);
if (style->dname_to_str != NULL) {
char *idn = style->dname_to_str(name);
free(name);
name = idn;
}
char sep = strlen(name) < 4 * TAB_WIDTH ? '\t' : ' ';
ret = snprintf(dst + len, maxlen - len, "%-20s%c", name, sep);
free(name);
......
......@@ -45,6 +45,8 @@ typedef struct {
bool human_ttl;
/*!< Format timestamp as YYYYMMDDHHmmSS. */
bool human_tmstamp;
/*!< Dname to string callback. */
char *(*dname_to_str)(const char *dname);
} knot_dump_style_t;
/*! \brief Default dump style. */
......
......@@ -291,6 +291,11 @@ static void print_section_host(const knot_rrset_t **rrsets,
char *owner;
owner = knot_dname_to_str(rrset->owner);
if (style->style.dname_to_str != NULL) {
char *idn = style->style.dname_to_str(owner);
free(owner);
owner = idn;
}
descr = knot_lookup_by_id(rtypes, rrset->type);
for (size_t j = 0; j < rrset->rdata_count; j++) {
......@@ -328,7 +333,8 @@ static void print_section_host(const knot_rrset_t **rrsets,
}
static void print_error_host(const uint8_t code,
const knot_packet_t *packet)
const knot_packet_t *packet,
const style_t *style)
{
const char *rcode_str = "NULL";
char type[32] = "NULL";
......@@ -337,6 +343,11 @@ static void print_error_host(const uint8_t code,
knot_lookup_table_t *rcode;
owner = knot_dname_to_str(knot_packet_qname(packet));
if (style->style.dname_to_str != NULL) {
char *idn = style->style.dname_to_str(owner);
free(owner);
owner = idn;
}
rcode = knot_lookup_by_id(knot_rcode_names, code);
if (rcode != NULL) {
rcode_str = rcode->name;
......@@ -394,6 +405,11 @@ void print_header_xfr(const knot_packet_t *packet, const style_t *style)
if (style->show_header) {
char *owner = knot_dname_to_str(knot_packet_qname(packet));
if (style->style.dname_to_str != NULL) {
char *idn = style->style.dname_to_str(owner);
free(owner);
owner = idn;
}
if (owner != NULL) {
printf(";; %s for %s\n", xfr, owner);
free(owner);
......@@ -495,7 +511,7 @@ void print_packet(const knot_packet_t *packet,
print_section_host(packet->answer, ancount,
style);
} else {
print_error_host(rcode, packet);
print_error_host(rcode, packet, style);
}
break;
case FORMAT_NSUPDATE:
......
......@@ -34,6 +34,34 @@
#define IPV4_REVERSE_DOMAIN "in-addr.arpa."
#define IPV6_REVERSE_DOMAIN "ip6.arpa."
#ifdef LIBIDN
#include <idna.h>
char* name_from_idn(const char* idn_name) {
char *name;
int rc = idna_to_ascii_lz(idn_name, &name, 0);
if (rc != IDNA_SUCCESS) {
ERR("IDNA (%s)\n", idna_strerror(rc));
return NULL;
}
return name;
}
char* name_to_idn(const char* name) {
char *idn_name;
int rc = idna_to_unicode_8zlz(name, &idn_name, 0);
if (rc != IDNA_SUCCESS) {
ERR("IDNA (%s)\n", idna_strerror(rc));
return strdup(name);
}
return idn_name;
}
#endif
/*!
* \brief Checks if string is a prefix of reference string.
*
......
......@@ -115,6 +115,29 @@ typedef struct {
param_handle_f handler;
} param_t;
#ifdef LIBIDN
#include <idna.h>
/*!
* \brief Transforms localized IDN string to ASCII punycode.
*
* \param idn_name IDN name to transform.
*
* \retval NULL if transformation fails.
* \retval string if ok.
*/
char* name_from_idn(const char* idn_name);
/*!
* \brief Transforms ASCII punycode to localized IDN string.
*
* \param idn_name ASCII name to transform.
*
* \retval string transformed string or idn_name if error.
*/
char* name_to_idn(const char* name);
#endif
int best_param(const char *str, const size_t str_len, const param_t *tbl,
bool *unique);
......
......@@ -22,6 +22,10 @@
#include <getopt.h> // getopt
#include <stdlib.h> // free
#ifdef LIBIDN
#include <locale.h>
#endif
#include "common/lists.h" // list
#include "common/errcode.h" // KNOT_EOK
#include "common/descriptor.h" // KNOT_CLASS_IN
......@@ -51,7 +55,12 @@ static const style_t DEFAULT_STYLE_DIG = {
.show_ttl = true,
.verbose = false,
.human_ttl = false,
.human_tmstamp = true
.human_tmstamp = true,
#ifdef LIBIDN
.dname_to_str = name_to_idn
#else
.dname_to_str = NULL
#endif
},
.show_query = false,
.show_header = true,
......@@ -517,6 +526,28 @@ static int opt_noignore(const char *arg, void *query)
return KNOT_EOK;
}
static int opt_idn(const char *arg, void *query)
{
query_t *q = query;
q->idn = true;
#ifdef LIBIDN
q->style.style.dname_to_str = name_to_idn;
#endif
return KNOT_EOK;
}
static int opt_noidn(const char *arg, void *query)
{
query_t *q = query;
q->idn = false;
q->style.style.dname_to_str = NULL;
return KNOT_EOK;
}
static int opt_nsid(const char *arg, void *query)
{
query_t *q = query;
......@@ -697,6 +728,9 @@ static const param_t dig_opts2[] = {
{ "ignore", ARG_NONE, opt_ignore },
{ "noignore", ARG_NONE, opt_noignore },
{ "idn", ARG_NONE, opt_idn },
{ "noidn", ARG_NONE, opt_noidn },
{ "nsid", ARG_NONE, opt_nsid },
{ "nonsid", ARG_NONE, opt_nonsid },
......@@ -750,6 +784,11 @@ query_t* query_create(const char *owner, const query_t *conf)
query->xfr_serial = 0;
query->flags = DEFAULT_FLAGS_DIG;
query->style = DEFAULT_STYLE_DIG;
#ifdef LIBIDN
query->idn = true;
#else
query->idn = false;
#endif
query->nsid = false;
} else {
if (conf->local != NULL) {
......@@ -776,6 +815,7 @@ query_t* query_create(const char *owner, const query_t *conf)
query->xfr_serial = conf->xfr_serial;
query->flags = conf->flags;
query->style = conf->style;
query->idn = conf->idn;
query->nsid = conf->nsid;
if (knot_copy_key_params(&conf->key_params, &query->key_params)
......@@ -907,10 +947,26 @@ static int parse_local(const char *value, query_t *query)
static int parse_name(const char *value, list_t *queries, const query_t *conf)
{
query_t *query = NULL;
query_t *query = NULL;
char *ascii_name = (char *)value;
#ifdef LIBIDN
if (conf->idn) {
ascii_name = name_from_idn(value);
if (ascii_name == NULL) {
return KNOT_EINVAL;
}
}
#endif
// If name is not FQDN, append trailing dot.
char *fqd_name = get_fqd_name(value);
char *fqd_name = get_fqd_name(ascii_name);
#ifdef LIBIDN
if (conf->idn) {
free(ascii_name);
}
#endif
// Create new query.
query = query_create(fqd_name, conf);
......@@ -1167,6 +1223,7 @@ static void dig_help(void)
" +[no]tcp Use TCP protocol.\n"
" +[no]fail Stop if SERVFAIL.\n"
" +[no]ignore Don't use TCP automatically if truncated.\n"
" +[no]idn Enable IDN transformation.\n"
" +[no]nsid Request NSID.\n"
" +[no]edns=N Use EDNS (=version).\n"
" +time=T Set wait for reply interval in seconds.\n"
......@@ -1453,6 +1510,15 @@ int dig_parse(dig_params_t *params, int argc, char *argv[])
return KNOT_ERROR;
}
#ifdef LIBIDN
// Set up localization.
if (setlocale(LC_CTYPE, "") == NULL) {
WARN("can't setlocale, disabling IDN\n");
params->config->idn = false;
params->config->style.style.dname_to_str = NULL;
}
#endif
// Command line parameters processing.
for (int i = 1; i < argc; i++) {
int ret = KNOT_ERROR;
......
......@@ -102,6 +102,8 @@ typedef struct {
flags_t flags;
/*!< Output settings. */
style_t style;
/*!< IDN conversion. */
bool idn;
/*!< Query for NSID. */
bool nsid;
/*!< EDNS version (8unsigned + -1 uninitialized). */
......
......@@ -22,10 +22,15 @@
#include <getopt.h> // getopt
#include <stdlib.h> // free
#ifdef LIBIDN
#include <locale.h>
#endif
#include "common/lists.h" // list
#include "common/errcode.h" // KNOT_EOK
#include "common/descriptor.h" // KNOT_CLASS_IN
#include "utils/common/msg.h" // WARN
#include "utils/common/params.h" // name_to_idn
#include "utils/dig/dig_params.h" // dig_params_t
#include "utils/common/resolv.h" // get_nameservers
......@@ -40,7 +45,12 @@ static const style_t DEFAULT_STYLE_HOST = {
.show_ttl = true,
.verbose = false,
.human_ttl = false,
.human_tmstamp = true
.human_tmstamp = true,
#ifdef LIBIDN
.dname_to_str = name_to_idn
#else
.dname_to_str = NULL
#endif
},
.show_query = false,
.show_header = false,
......@@ -69,6 +79,11 @@ static int host_init(dig_params_t *params)
params->config->servfail_stop = false;
params->config->class_num = KNOT_CLASS_IN;
params->config->style = DEFAULT_STYLE_HOST;
#ifdef LIBIDN
params->config->idn = true;
#else
params->config->idn = false;
#endif
// Check port.
if (params->config->port == NULL) {
......@@ -92,11 +107,26 @@ void host_clean(dig_params_t *params)
static int parse_name(const char *value, list_t *queries, const query_t *conf)
{
char *reverse = get_reverse_name(value);
char *fqd_name = NULL;
char *ascii_name = (char *)value;
query_t *query;
#ifdef LIBIDN
if (conf->idn) {
ascii_name = name_from_idn(value);
if (ascii_name == NULL) {
return KNOT_EINVAL;
}
}
#endif
// If name is not FQDN, append trailing dot.
fqd_name = get_fqd_name(value);
char *fqd_name = get_fqd_name(ascii_name);
#ifdef LIBIDN
if (conf->idn) {
free(ascii_name);
}
#endif
// RR type is known.
if (conf->type_num >= 0) {
......@@ -211,6 +241,14 @@ int host_parse(dig_params_t *params, int argc, char *argv[])
return KNOT_ERROR;
}
#ifdef LIBIDN
// Set up localization.
if (setlocale(LC_CTYPE, "") == NULL) {
params->config->idn = false;
params->config->style.style.dname_to_str = NULL;
}
#endif
query_t *conf = params->config;
uint16_t rclass, rtype;
uint32_t serial;
......
......@@ -20,6 +20,11 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#ifdef LIBIDN
#include <locale.h>
#endif
#include "utils/common/params.h"
#include "common/base32hex.h"
#include "common/errcode.h"
#include "common/hex.h"
......@@ -93,6 +98,15 @@ int main(int argc, char *argv[])
{ NULL }
};
#ifdef LIBIDN
bool enable_idn = false;
// Set up localization.
if (setlocale(LC_CTYPE, "") != NULL) {
enable_idn = true;
}
#endif
int opt = 0;
int li = 0;
while ((opt = getopt_long(argc, argv, "hV", options, &li)) != -1) {
......@@ -130,7 +144,19 @@ int main(int argc, char *argv[])
goto fail;
}
#ifdef LIBIDN
if (enable_idn) {
char *ascii_name = name_from_idn(argv[4]);
if (ascii_name == NULL) {
fprintf(stderr, "Cannot transform IDN domain name.\n");
goto fail;
}
dname = knot_dname_from_str(ascii_name);
free(ascii_name);
}
#else
dname = knot_dname_from_str(argv[4]);
#endif
if (dname == NULL) {
fprintf(stderr, "Cannot parse domain name.\n");
goto fail;
......
......@@ -39,7 +39,8 @@ static const style_t DEFAULT_STYLE_NSUPDATE = {
.show_ttl = true,
.verbose = false,
.human_ttl = false,
.human_tmstamp = true
.human_tmstamp = true,
.dname_to_str = NULL
},
.show_query = false,
.show_header = true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment