Hi, I am submitting patch enabling IDN usage for input parameters. We have support for IDN, but because locale is not properly initialized, no parameters using special characters are accepted.
Patch #1 adds domain_len to more places, quite similar to struct server and struct serv_local. It computes length of saved domain only once and uses it on various processing. Just small optimization on not critical bits, since main lookup_domain already uses it. I think it should be used on more places. I think we could define someting like struct binstring { char *str, u16 len }; and use it more in code. Pairs of string and its length are common and handled different way. Because just basic char * is used on many places, strlen is called again and again, often not required. Especially might be useful for DHCP binary strings processing. Patch #2 enables --address=/münchen.de/háčkyčárky.cz/ on input even on builds without defined LOCALEDIR. Which is missing on Fedora and it seems IDN support is effectively inactive that way. Adds --auth-zone support for IDN too. Patch #3 disables IDN_TRANSITIONAL mode. I wanted to try some refused IDN name, but transitional mode accepts almost any character. It accepts also --address=/💻🚱.test/. I think there is no need to support such obscure names, but we should accept normal names supported by browsers. Since I think IDN 2003 support were not properly enabled before, I think we should start just from IDN 2008 standard. I think there is still something weird, because --address=/испытание./ is accepted but --address=/испытание/ is not. Improvement anyway. -- Petr Menšík Software Engineer Red Hat, http://www.redhat.com/ email: pemen...@redhat.com PGP: DFCF908DB7C87E8E529925BC4931CA5B6C9FC5CB
From f1a6dca099305128ab5304a89748a96932f32256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com> Date: Thu, 30 Sep 2021 22:19:45 +0200 Subject: [PATCH 3/4] Disable transitional IDN rules, accept only sane names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transitional encoding accepts every emoticon you can think about. Because setlocale were not enabled before, IDN 2003 input was not accepted by dnsmasq. It makes no sense therefore to maintain backward compatibility. Accept only proper encoded unicode names and reject random unicode characters. Signed-off-by: Petr Menšík <pemen...@redhat.com> --- src/util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util.c b/src/util.c index e641905..7bef630 100644 --- a/src/util.c +++ b/src/util.c @@ -233,8 +233,6 @@ char *canonicalise(char *in, int *nomem) { # ifdef HAVE_LIBIDN2 rc = idn2_to_ascii_lz(in, &ret, IDN2_NONTRANSITIONAL); - if (rc == IDN2_DISALLOWED) - rc = idn2_to_ascii_lz(in, &ret, IDN2_TRANSITIONAL); # else rc = idna_to_ascii_lz(in, &ret, 0); # endif -- 2.31.1
From c079c47803029f620c51d16b807d9c48a23c99dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com> Date: Thu, 30 Sep 2021 22:15:39 +0200 Subject: [PATCH 2/4] Enable locale support for IDN at startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --address=/münchen.de/ is not accepted unless LOCALEDIR is defined on build. It is not by default. If LIBIDN1 or 2 is defined, call setlocale to initialize locale required to translate domains to ascii form. Signed-off-by: Petr Menšík <pemen...@redhat.com> --- src/dnsmasq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dnsmasq.c b/src/dnsmasq.c index c7fa024..cfbbbf1 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -17,6 +17,9 @@ /* Declare static char *compiler_opts in config.h */ #define DNSMASQ_COMPILE_OPTS +#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR) +#include <locale.h> +#endif #include "dnsmasq.h" struct daemon *daemon; @@ -69,8 +72,10 @@ int main (int argc, char **argv) int tftp_prefix_missing = 0; #endif -#ifdef LOCALEDIR +#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR) setlocale(LC_ALL, ""); +#endif +#ifdef LOCALEDIR bindtextdomain("dnsmasq", LOCALEDIR); textdomain("dnsmasq"); #endif -- 2.31.1
From 31b925efc54161ccee41defa132270d8bac801ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com> Date: Thu, 30 Sep 2021 17:30:40 +0200 Subject: [PATCH 1/4] Add domain_len to domain parameters in several structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to serv_local structure, there is multiple structures with domain stored from configuration. Compute and store domain length in structure and avoid re-computing it on each use. Add also IDN support to auth-zone. Signed-off-by: Petr Menšík <pemen...@redhat.com> --- src/auth.c | 15 +++++++-------- src/dnsmasq.h | 3 +++ src/forward.c | 6 +++--- src/loop.c | 4 ++-- src/network.c | 6 +++--- src/option.c | 15 +++++++++++++-- 6 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/auth.c b/src/auth.c index aa2403b..52b6ed1 100644 --- a/src/auth.c +++ b/src/auth.c @@ -72,22 +72,21 @@ static int filter_zone(struct auth_zone *zone, int flag, union all_addr *addr_u) int in_zone(struct auth_zone *zone, char *name, char **cut) { size_t namelen = strlen(name); - size_t domainlen = strlen(zone->domain); if (cut) *cut = NULL; - if (namelen >= domainlen && - hostname_isequal(zone->domain, &name[namelen - domainlen])) + if (namelen >= zone->domain_len && + hostname_isequal(zone->domain, &name[namelen - zone->domain_len])) { - - if (namelen == domainlen) + + if (namelen == zone->domain_len) return 1; - - if (name[namelen - domainlen - 1] == '.') + + if (name[namelen - zone->domain_len - 1] == '.') { if (cut) - *cut = &name[namelen - domainlen - 1]; + *cut = &name[namelen - zone->domain_len - 1]; return 1; } } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 3fdc1b0..5132ee1 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -409,6 +409,7 @@ struct addrlist { struct auth_zone { char *domain; + u16 domain_len; struct auth_name_list { char *name; int flags; @@ -611,12 +612,14 @@ struct serv_local { struct rebind_domain { char *domain; + u16 domain_len; struct rebind_domain *next; }; struct ipsets { char **sets; char *domain; + u16 domain_len; struct ipsets *next; }; diff --git a/src/forward.c b/src/forward.c index ceecfcd..f868734 100644 --- a/src/forward.c +++ b/src/forward.c @@ -152,10 +152,10 @@ static void server_send_log(struct server *server, int fd, static int domain_no_rebind(char *domain) { struct rebind_domain *rbd; - size_t tlen, dlen = strlen(domain); + u16 dlen = strlen(domain); for (rbd = daemon->no_rebind; rbd; rbd = rbd->next) - if (dlen >= (tlen = strlen(rbd->domain)) && strcmp(rbd->domain, &domain[dlen - tlen]) == 0) + if (dlen >= rbd->domain_len && strcmp(rbd->domain, &domain[dlen - rbd->domain_len]) == 0) return 1; return 0; @@ -575,7 +575,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai unsigned int matchlen = 0; for (ipset_pos = setlist; ipset_pos; ipset_pos = ipset_pos->next) { - unsigned int domainlen = strlen(ipset_pos->domain); + unsigned int domainlen = ipset_pos->domain_len; const char *matchstart = domain + namelen - domainlen; if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) && (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) && diff --git a/src/loop.c b/src/loop.c index 01f0c28..a66102d 100644 --- a/src/loop.c +++ b/src/loop.c @@ -30,7 +30,7 @@ void loop_send_probes() /* Loop through all upstream servers not for particular domains, and send a query to that server which is identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */ for (serv = daemon->servers; serv; serv = serv->next) - if (strlen(serv->domain) == 0 && + if (serv->domain_len == 0 && !(serv->flags & (SERV_FOR_NODOTS))) { ssize_t len = loop_make_probe(serv->uid); @@ -98,7 +98,7 @@ int detect_loop(char *query, int type) uid = strtol(query, NULL, 16); for (serv = daemon->servers; serv; serv = serv->next) - if (strlen(serv->domain) == 0 && + if (serv->domain_len == 0 && !(serv->flags & SERV_LOOP) && uid == serv->uid) { diff --git a/src/network.c b/src/network.c index 3c1c176..9450bb7 100644 --- a/src/network.c +++ b/src/network.c @@ -1532,7 +1532,7 @@ void check_servers(int no_loop_check) /* Disable DNSSEC validation when using server=/domain/.... servers unless there's a configured trust anchor. */ - if (strlen(serv->domain) != 0) + if (serv->domain_len != 0) { struct ds_config *ds; char *domain = serv->domain; @@ -1592,7 +1592,7 @@ void check_servers(int no_loop_check) if (++count > SERVERS_LOGGED) continue; - if (strlen(serv->domain) != 0 || (serv->flags & SERV_FOR_NODOTS)) + if (serv->domain_len != 0 || (serv->flags & SERV_FOR_NODOTS)) { char *s1, *s2, *s3 = "", *s4 = ""; @@ -1602,7 +1602,7 @@ void check_servers(int no_loop_check) #endif if (serv->flags & SERV_FOR_NODOTS) s1 = _("unqualified"), s2 = _("names"); - else if (strlen(serv->domain) == 0) + else if (serv->domain_len == 0) s1 = _("default"), s2 = ""; else s1 = _("domain"), s2 = serv->domain, s4 = (serv->flags & SERV_WILDCARD) ? "*" : ""; diff --git a/src/option.c b/src/option.c index 4e533be..74d87d0 100644 --- a/src/option.c +++ b/src/option.c @@ -671,6 +671,13 @@ static char *canonicalise_opt(char *s) return ret; } +static char *canonicalise_optlen(char *s, u16 *len) +{ + char *ret = canonicalise_opt(s); + *len = ret ? strlen(ret) : 0; + return ret; +} + static int numeric_check(char *a) { char *p; @@ -2235,7 +2242,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma comma = split(arg); new = opt_malloc(sizeof(struct auth_zone)); - new->domain = opt_string_alloc(arg); + new->domain = canonicalise_optlen(arg, &new->domain_len); + if (!new->domain) + ret_err_free(_("invalid auth-zone"), new); new->subnet = NULL; new->exclude = NULL; new->interface_names = NULL; @@ -2728,7 +2737,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma do { comma = split_chr(arg, '/'); new = opt_malloc(sizeof(struct rebind_domain)); - new->domain = canonicalise_opt(arg); + new->domain = canonicalise_optlen(arg, &new->domain_len); new->next = daemon->no_rebind; daemon->no_rebind = new; arg = comma; @@ -2897,6 +2906,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ipsets = ipsets->next; memset(ipsets, 0, sizeof(struct ipsets)); ipsets->domain = domain; + ipsets->domain_len = strlen(domain); arg = end; } } @@ -2906,6 +2916,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ipsets = ipsets->next; memset(ipsets, 0, sizeof(struct ipsets)); ipsets->domain = ""; + ipsets->domain_len = 0; } if (!arg || !*arg) -- 2.31.1
_______________________________________________ Dnsmasq-discuss mailing list Dnsmasq-discuss@lists.thekelleys.org.uk https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss