From 7f9f1ecb710eac4d65bb02785ddf288cac098323 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Wed, 23 May 2018 15:26:19 +0200 Subject: Switch IDNA implementation to libidn2 [BZ #19728] [BZ #19729] [BZ #22247] This provides an implementation of the IDNA2008 standard and fixes CVE-2016-6261, CVE-2016-6263, CVE-2017-14062. --- resolv/Makefile | 24 +- resolv/netdb.h | 16 +- resolv/tst-no-libidn2.c | 2 + resolv/tst-resolv-ai_idn-common.c | 569 +++++++++++++++++++++++++++++++++++ resolv/tst-resolv-ai_idn-latin1.c | 50 +++ resolv/tst-resolv-ai_idn-nolibidn2.c | 151 ++++++++++ resolv/tst-resolv-ai_idn.c | 49 +++ 7 files changed, 852 insertions(+), 9 deletions(-) create mode 100644 resolv/tst-no-libidn2.c create mode 100644 resolv/tst-resolv-ai_idn-common.c create mode 100644 resolv/tst-resolv-ai_idn-latin1.c create mode 100644 resolv/tst-resolv-ai_idn-nolibidn2.c create mode 100644 resolv/tst-resolv-ai_idn.c (limited to 'resolv') diff --git a/resolv/Makefile b/resolv/Makefile index 6e70ae9f6b..ea395ac3eb 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -60,6 +60,9 @@ tests += \ # These tests need libdl. ifeq (yes,$(build-shared)) tests += \ + tst-resolv-ai_idn \ + tst-resolv-ai_idn-latin1 \ + tst-resolv-ai_idn-nolibidn2 \ tst-resolv-canonname \ # uses DEPRECATED_RES_USE_INET6 from . @@ -72,7 +75,13 @@ tests-internal += \ tst-resolv-res_ninit \ tst-resolv-threads \ -endif +# Used by tst-resolv-ai_idn-nolibidn2 to disable libidn2 (by not +# providing any functions in libidn2.so.0). +modules-names += tst-no-libidn2 +extra-test-objs += tst-no-libidn2.os +LDFLAGS-tst-no-libidn2.so = -Wl,-soname,libidn2.so.0 + +endif # $(build-shared) # This test accesses __inet_ntop_length, an internal libc function. tests-internal += tst-inet_pton @@ -128,6 +137,9 @@ generated += mtrace-tst-leaks.out tst-leaks.mtrace \ include ../Rules +LOCALES := en_US.UTF-8 en_US.ISO-8859-1 +include ../gen-locales.mk + CFLAGS-res_hconf.c += -fexceptions # The DNS NSS modules needs the resolver. @@ -159,6 +171,16 @@ $(objpfx)mtrace-tst-resolv-res_ninit.out: $(objpfx)tst-resolv-res_ninit.out $(objpfx)tst-bug18665-tcp: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-bug18665: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-res_use_inet6: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-ai_idn: \ + $(libdl) $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-ai_idn-latin1: \ + $(libdl) $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-ai_idn-nolibidn2: \ + $(libdl) $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-ai_idn.out: $(gen-locales) +$(objpfx)tst-resolv-ai_idn-latin1.out: $(gen-locales) +$(objpfx)tst-resolv-ai_idn-nolibidn2.out: \ + $(gen-locales) $(objpfx)tst-no-libidn2.so $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) diff --git a/resolv/netdb.h b/resolv/netdb.h index 66a1baaf65..003800e882 100644 --- a/resolv/netdb.h +++ b/resolv/netdb.h @@ -605,10 +605,10 @@ struct gaicb in the current locale's character set) before looking it up. */ # define AI_CANONIDN 0x0080 /* Translate canonical name from IDN format. */ -# define AI_IDN_ALLOW_UNASSIGNED 0x0100 /* Don't reject unassigned Unicode - code points. */ -# define AI_IDN_USE_STD3_ASCII_RULES 0x0200 /* Validate strings according to - STD3 rules. */ +# define AI_IDN_ALLOW_UNASSIGNED \ + __glibc_macro_warning ("AI_IDN_ALLOW_UNASSIGNED is deprecated") 0x0100 +# define AI_IDN_USE_STD3_ASCII_RULES \ + __glibc_macro_warning ("AI_IDN_USE_STD3_ASCII_RULES is deprecated") 0x0200 # endif # define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ @@ -646,10 +646,10 @@ struct gaicb # define NI_DGRAM 16 /* Look up UDP service rather than TCP. */ # ifdef __USE_GNU # define NI_IDN 32 /* Convert name from IDN format. */ -# define NI_IDN_ALLOW_UNASSIGNED 64 /* Don't reject unassigned Unicode - code points. */ -# define NI_IDN_USE_STD3_ASCII_RULES 128 /* Validate strings according to - STD3 rules. */ +# define NI_IDN_ALLOW_UNASSIGNED \ + __glibc_macro_warning ("NI_IDN_ALLOW_UNASSIGNED is deprecated") 64 +# define NI_IDN_USE_STD3_ASCII_RULES \ + __glibc_macro_warning ("NI_IDN_USE_STD3_ASCII_RULES is deprecated") 128 # endif /* Translate name of a service location and/or a service name to set of diff --git a/resolv/tst-no-libidn2.c b/resolv/tst-no-libidn2.c new file mode 100644 index 0000000000..590696e3fa --- /dev/null +++ b/resolv/tst-no-libidn2.c @@ -0,0 +1,2 @@ +/* Compiled into an empty shared object. Used by + tst-resolv-ai_idn-nolibidn2 to disable libidn2. */ diff --git a/resolv/tst-resolv-ai_idn-common.c b/resolv/tst-resolv-ai_idn-common.c new file mode 100644 index 0000000000..e7c4030e1c --- /dev/null +++ b/resolv/tst-resolv-ai_idn-common.c @@ -0,0 +1,569 @@ +/* Common code for AI_IDN/NI_IDN tests. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* Before including this file, TEST_USE_UTF8 must be defined to 1 or + 0, depending on whether a UTF-8 locale is used or a Latin-1 + locale. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Name of the shared object for libidn2. */ +#define LIBIDN2_SONAME "libidn2.so.0" + +#if TEST_USE_UTF8 +/* UTF-8 encoding of "nämchen" (German for “namelet”). */ +# define NAEMCHEN "n\xC3\xA4mchen" + +/* UTF-8 encoding of "שם" (Hebrew for “name”). */ +# define SHEM "\xD7\xA9\xD7\x9D" + +/* UTF-8 encoding of "buße" (German for “penance”). This used to be + encoded as "busse" (“busses”) in IDNA2003. */ +# define BUSSE "bu\xC3\x9F""e" + +#else +/* Latin-1 encodings, as far as they are available. */ + +# define NAEMCHEN "n\xE4mchen" +# define BUSSE "bu\xDF""e" + +#endif + +/* IDNA encoding of NAEMCHEN. */ +#define NAEMCHEN_IDNA "xn--nmchen-bua" + +/* IDNA encoding of NAEMCHEN "_zwo". */ +#define NAEMCHEN_ZWO_IDNA "xn--nmchen_zwo-q5a" + +/* IDNA encoding of SHEM. */ +#define SHEM_IDNA "xn--iebx" + +/* IDNA encoding of BUSSE. */ +#define BUSSE_IDNA "xn--bue-6ka" + +/* IDNA encoding of "שם1". */ +#define SHEM1_IDNA "xn--1-qic9a" + +/* Another IDNA name. */ +#define ANDERES_NAEMCHEN "anderes-" NAEMCHEN +#define ANDERES_NAEMCHEN_IDNA "xn--anderes-nmchen-eib" + +/* Controls the kind of test data in a PTR lookup response. */ +enum gni_test + { + gni_non_idn_name, + gni_non_idn_cname_to_non_idn_name, + gni_non_idn_cname_to_idn_name, + gni_idn_name, + gni_idn_shem, + gni_idn_shem1, + gni_idn_cname_to_non_idn_name, + gni_idn_cname_to_idn_name, + gni_invalid_idn_1, + gni_invalid_idn_2, + }; + +/* Called from response below. The LSB (first byte) controls what + goes into the response, see enum gni_test. */ +static void +response_ptr (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, const char *qname) +{ + int comp[4] = { 0 }; + TEST_COMPARE (sscanf (qname, "%d.%d.%d.%d.in-addr.arpa", + &comp[0], &comp[1], &comp[2], &comp[3]), 4); + const char *next_name; + switch ((enum gni_test) comp[0]) + { + /* First name in response is non-IDN name. */ + case gni_non_idn_name: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, "non-idn.example"); + resolv_response_close_record (b); + return; + case gni_non_idn_cname_to_non_idn_name: + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + next_name = "non-idn-cname.example"; + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + resolv_response_open_record (b, next_name, C_IN, T_PTR, 0); + resolv_response_add_name (b, "non-idn-name.example"); + resolv_response_close_record (b); + return; + case gni_non_idn_cname_to_idn_name: + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + next_name = "non-idn-cname.example"; + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + resolv_response_open_record (b, next_name, C_IN, T_PTR, 0); + resolv_response_add_name (b, NAEMCHEN_IDNA ".example"); + resolv_response_close_record (b); + return; + + /* First name in response is IDN name. */ + case gni_idn_name: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, "xn--nmchen-bua.example"); + resolv_response_close_record (b); + return; + case gni_idn_shem: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, SHEM_IDNA ".example"); + resolv_response_close_record (b); + return; + case gni_idn_shem1: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, SHEM1_IDNA ".example"); + resolv_response_close_record (b); + return; + case gni_idn_cname_to_non_idn_name: + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + next_name = NAEMCHEN_IDNA ".example"; + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + resolv_response_open_record (b, next_name, C_IN, T_PTR, 0); + resolv_response_add_name (b, "non-idn-name.example"); + resolv_response_close_record (b); + return; + case gni_idn_cname_to_idn_name: + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + next_name = NAEMCHEN_IDNA ".example"; + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + resolv_response_open_record (b, next_name, C_IN, T_PTR, 0); + resolv_response_add_name (b, ANDERES_NAEMCHEN_IDNA ".example"); + resolv_response_close_record (b); + return; + + /* Invalid IDN encodings. */ + case gni_invalid_idn_1: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, "xn---.example"); + resolv_response_close_record (b); + return; + case gni_invalid_idn_2: + resolv_response_open_record (b, qname, C_IN, T_PTR, 0); + resolv_response_add_name (b, "xn--x.example"); + resolv_response_close_record (b); + return; + } + FAIL_EXIT1 ("invalid PTR query: %s", qname); +} + +/* For PTR responses, see above. A/AAAA queries can request + additional CNAMEs in the response by include ".cname." and + ".idn-cname." in the query. The LSB in the address contains the + first byte of the QNAME. */ +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_VERIFY_EXIT (qclass == C_IN); + + for (const char *p = qname; *p != '\0'; ++p) + if (!(('0' <= *p && *p <= '9') + || ('a' <= *p && *p <= 'z') + || ('A' <= *p && *p <= 'Z') + || *p == '.' || *p == '-' || *p == '_')) + { + /* Non-ASCII query. Reply with NXDOMAIN. */ + struct resolv_response_flags flags = { .rcode = 3 }; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + return; + } + + struct resolv_response_flags flags = { 0 }; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + + if (qtype == T_PTR) + { + response_ptr (ctx, b, qname); + return; + } + + bool with_cname = strstr (qname, ".cname.") != NULL; + bool with_idn_cname = strstr (qname, ".idn-cname.") != NULL; + + const char *next_name = qname; + if (with_cname) + { + next_name = "non-idn-cname.example"; + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + } + if (with_idn_cname) + { + next_name = ANDERES_NAEMCHEN_IDNA ".example"; + resolv_response_open_record (b, qname, C_IN, T_CNAME, 0); + resolv_response_add_name (b, next_name); + resolv_response_close_record (b); + } + + resolv_response_open_record (b, next_name, C_IN, qtype, 0); + switch (qtype) + { + case T_A: + { + char addr[4] = { 192, 0, 2, qname[0] }; + resolv_response_add_data (b, &addr, sizeof (addr)); + } + break; + case T_AAAA: + { + char addr[16] + = { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + qname[0] }; + resolv_response_add_data (b, &addr, sizeof (addr)); + } + default: + FAIL_EXIT1 ("invalid qtype: %d", qtype); + } + resolv_response_close_record (b); +} + +/* Check the result of a getaddrinfo call. */ +static void +check_ai (const char *name, int ai_flags, const char *expected) +{ + struct addrinfo hints = + { + .ai_flags = ai_flags, + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + struct addrinfo *ai; + char *query = xasprintf ("%s:80 AF_INET/0x%x", name, ai_flags); + int ret = getaddrinfo (name, "80", &hints, &ai); + check_addrinfo (query, ai, ret, expected); + if (ret == 0) + freeaddrinfo (ai); + free (query); +} + +/* Run one getnameinfo test. FLAGS is automatically augmented with + NI_NUMERICSERV. */ +static void +gni_test (enum gni_test code, unsigned int flags, const char *expected) +{ + struct sockaddr_in sin = + { + .sin_family = AF_INET, + .sin_port = htons (80), + .sin_addr = { htonl (0xc0000200 | code) }, /* 192.0.2.0/24 network. */ + }; + char host[1024]; + char service[1024]; + int ret = getnameinfo ((const struct sockaddr *) &sin, sizeof (sin), + host, sizeof (host), service, sizeof (service), + flags| NI_NUMERICSERV); + if (ret != 0) + { + if (expected == NULL) + TEST_COMPARE (ret, EAI_IDN_ENCODE); + else + { + support_record_failure (); + printf ("error: getnameinfo failed (code %d, flags 0x%x): %s (%d)\n", + (int) code, flags, gai_strerror (ret), ret); + } + } + else if (ret == 0 && expected == NULL) + { + support_record_failure (); + printf ("error: getnameinfo unexpected success (code %d, flags 0x%x)\n", + (int) code, flags); + } + else if (strcmp (host, expected) != 0 || strcmp (service, "80") != 0) + { + support_record_failure (); + printf ("error: getnameinfo test failure (code %d, flags 0x%x)\n" + " expected host: \"%s\"\n" + " expected service: \"80\"\n" + " actual host: \"%s\"\n" + " actual service: \"%s\"\n", + (int) code, flags, expected, host, service); + } +} + +/* Tests for getaddrinfo which assume a working libidn2 library. */ +__attribute__ ((unused)) +static void +gai_tests_with_libidn2 (void) +{ + /* No CNAME. */ + check_ai ("non-idn.example", 0, + "address: STREAM/TCP 192.0.2.110 80\n"); + check_ai ("non-idn.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.110 80\n"); + check_ai ("non-idn.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: non-idn.example\n" + "address: STREAM/TCP 192.0.2.110 80\n"); + + check_ai (NAEMCHEN ".example", 0, + "error: Name or service not known\n"); + check_ai (NAEMCHEN ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + +#if TEST_USE_UTF8 + check_ai (SHEM ".example", 0, + "error: Name or service not known\n"); + check_ai (SHEM ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " SHEM ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " SHEM_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM "1.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM "1.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " SHEM "1.example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM "1.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " SHEM1_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); +#endif + + /* Check that non-transitional mode is active. German sharp S + should not turn into SS. */ + check_ai (BUSSE ".example", 0, + "error: Name or service not known\n"); + check_ai (BUSSE ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (BUSSE ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " BUSSE_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (BUSSE ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " BUSSE ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + + /* Check that Unicode TR 46 mode is active. Underscores should be + permitted in IDNA components. */ + check_ai (NAEMCHEN "_zwo.example", 0, + "error: Name or service not known\n"); + check_ai (NAEMCHEN "_zwo.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN "_zwo.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " NAEMCHEN_ZWO_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN "_zwo.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " NAEMCHEN "_zwo.example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + + /* No CNAME, but already IDN-encoded. */ + check_ai (NAEMCHEN_IDNA ".example", 0, + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN_IDNA ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN_IDNA ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (NAEMCHEN_IDNA ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM_IDNA ".example", 0, + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM_IDNA ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + check_ai (SHEM_IDNA ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " SHEM_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); +#if TEST_USE_UTF8 + check_ai (SHEM_IDNA ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " SHEM ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); +#else + check_ai (SHEM_IDNA ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " SHEM_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); +#endif + + /* Invalid IDNA canonical name is returned as-is. */ + check_ai ("xn---.example", AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_CANONIDN\n" + "canonname: xn---.example\n" + "address: STREAM/TCP 192.0.2.120 80\n"); + + /* Non-IDN CNAME. */ + check_ai ("with.cname.example", 0, + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: non-idn-cname.example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + + check_ai ("with.cname." NAEMCHEN ".example", 0, + "error: Name or service not known\n"); + check_ai ("with.cname." NAEMCHEN ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname." NAEMCHEN ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: non-idn-cname.example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname." NAEMCHEN ".example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: non-idn-cname.example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + + /* IDN CNAME. */ + check_ai ("With.idn-cname.example", 0, + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + + check_ai ("With.idn-cname." NAEMCHEN ".example", 0, + "error: Name or service not known\n"); + check_ai ("With.idn-cname." NAEMCHEN ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("With.idn-cname." NAEMCHEN ".example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("With.idn-cname." NAEMCHEN ".example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + + /* Non-IDN to IDN CNAME chain. */ + check_ai ("both.cname.idn-cname.example", 0, + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + + check_ai ("both.cname.idn-cname." NAEMCHEN ".example", 0, + "error: Name or service not known\n"); + check_ai ("both.cname.idn-cname." NAEMCHEN ".example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname." NAEMCHEN ".example", + AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname." NAEMCHEN ".example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); +} + +/* Tests for getnameinfo which assume a working libidn2 library. */ +__attribute__ ((unused)) +static void +gni_tests_with_libidn2 (void) +{ + gni_test (gni_non_idn_name, 0, "non-idn.example"); + gni_test (gni_non_idn_name, NI_IDN, "non-idn.example"); + gni_test (gni_non_idn_name, NI_NUMERICHOST, "192.0.2.0"); + gni_test (gni_non_idn_name, NI_NUMERICHOST | NI_IDN, "192.0.2.0"); + + gni_test (gni_non_idn_cname_to_non_idn_name, 0, "non-idn-name.example"); + gni_test (gni_non_idn_cname_to_non_idn_name, NI_IDN, "non-idn-name.example"); + + gni_test (gni_non_idn_cname_to_idn_name, 0, NAEMCHEN_IDNA ".example"); + gni_test (gni_non_idn_cname_to_idn_name, NI_IDN, NAEMCHEN ".example"); + + gni_test (gni_idn_name, 0, NAEMCHEN_IDNA ".example"); + gni_test (gni_idn_name, NI_IDN, NAEMCHEN ".example"); + gni_test (gni_idn_shem, 0, SHEM_IDNA ".example"); + gni_test (gni_idn_shem1, 0, SHEM1_IDNA ".example"); +#if TEST_USE_UTF8 + gni_test (gni_idn_shem, NI_IDN, SHEM ".example"); + gni_test (gni_idn_shem1, NI_IDN, SHEM "1.example"); +#else + gni_test (gni_idn_shem, NI_IDN, SHEM_IDNA ".example"); + gni_test (gni_idn_shem1, NI_IDN, SHEM1_IDNA ".example"); +#endif + + gni_test (gni_idn_cname_to_non_idn_name, 0, "non-idn-name.example"); + gni_test (gni_idn_cname_to_non_idn_name, NI_IDN, "non-idn-name.example"); + + gni_test (gni_idn_cname_to_idn_name, 0, ANDERES_NAEMCHEN_IDNA ".example"); + gni_test (gni_idn_cname_to_idn_name, NI_IDN, ANDERES_NAEMCHEN ".example"); + + /* Test encoding errors. */ + gni_test (gni_invalid_idn_1, 0, "xn---.example"); + gni_test (gni_invalid_idn_1, NI_IDN, "xn---.example"); + gni_test (gni_invalid_idn_2, 0, "xn--x.example"); + gni_test (gni_invalid_idn_2, NI_IDN, "xn--x.example"); +} diff --git a/resolv/tst-resolv-ai_idn-latin1.c b/resolv/tst-resolv-ai_idn-latin1.c new file mode 100644 index 0000000000..fb60f3261e --- /dev/null +++ b/resolv/tst-resolv-ai_idn-latin1.c @@ -0,0 +1,50 @@ +/* Test getaddrinfo and getnameinfo with AI_IDN, NI_IDN (Latin-1). + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + + +#define TEST_USE_UTF8 0 +#include "tst-resolv-ai_idn-common.c" + +#include +#include + +static int +do_test (void) +{ + void *handle = dlopen (LIBIDN2_SONAME, RTLD_LAZY); + if (handle == NULL) + FAIL_UNSUPPORTED ("libidn2 not installed"); + + if (setlocale (LC_CTYPE, "en_US.ISO-8859-1") == NULL) + FAIL_EXIT1 ("setlocale: %m"); + + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + gai_tests_with_libidn2 (); + gni_tests_with_libidn2 (); + + resolv_test_end (aux); + xdlclose (handle); + return 0; +} + +#include diff --git a/resolv/tst-resolv-ai_idn-nolibidn2.c b/resolv/tst-resolv-ai_idn-nolibidn2.c new file mode 100644 index 0000000000..7203c23f3c --- /dev/null +++ b/resolv/tst-resolv-ai_idn-nolibidn2.c @@ -0,0 +1,151 @@ +/* Test getaddrinfo and getnameinfo without usable libidn2. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#define TEST_USE_UTF8 1 +#include "tst-resolv-ai_idn-common.c" + +#include +#include + +/* Tests for getaddrinfo. */ +static void +gai_tests (void) +{ + /* No CNAME. */ + check_ai ("non-idn.example", 0, + "address: STREAM/TCP 192.0.2.110 80\n"); + check_ai ("non-idn.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.110 80\n"); + check_ai ("non-idn.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: non-idn.example\n" + "address: STREAM/TCP 192.0.2.110 80\n"); + + /* This gets passed over the network to the server, so it will + result in an NXDOMAIN error. */ + check_ai (NAEMCHEN ".example", 0, + "error: Name or service not known\n"); + /* Due to missing libidn2, this fails inside getaddrinfo. */ + check_ai (NAEMCHEN ".example", AI_IDN, + "error: Parameter string not correctly encoded\n"); + check_ai (NAEMCHEN ".example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "error: Parameter string not correctly encoded\n"); + + /* Non-IDN CNAME. */ + check_ai ("with.cname.example", 0, + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + check_ai ("with.cname.example", AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: non-idn-cname.example\n" + "address: STREAM/TCP 192.0.2.119 80\n"); + + /* IDN CNAME. */ + check_ai ("With.idn-cname.example", 0, + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + check_ai ("With.idn-cname.example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.87 80\n"); + + /* Non-IDN to IDN CNAME chain. */ + check_ai ("both.cname.idn-cname.example", 0, + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", AI_IDN, + "flags: AI_IDN\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", AI_IDN | AI_CANONNAME, + "flags: AI_CANONNAME AI_IDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); + check_ai ("both.cname.idn-cname.example", + AI_IDN | AI_CANONNAME | AI_CANONIDN, + "flags: AI_CANONNAME AI_IDN AI_CANONIDN\n" + "canonname: " ANDERES_NAEMCHEN_IDNA ".example\n" + "address: STREAM/TCP 192.0.2.98 80\n"); +} + +/* Tests for getnameinfo. */ +static void +gni_tests (void) +{ + /* All non-IDN an IDN results are the same due to lack of libidn2 + support. */ + for (int do_ni_idn = 0; do_ni_idn < 2; ++do_ni_idn) + { + int flags = 0; + if (do_ni_idn) + flags |= NI_IDN; + + gni_test (gni_non_idn_name, flags, "non-idn.example"); + gni_test (gni_non_idn_name, flags | NI_NUMERICHOST, "192.0.2.0"); + gni_test (gni_non_idn_cname_to_non_idn_name, flags, + "non-idn-name.example"); + gni_test (gni_non_idn_cname_to_idn_name, flags, + NAEMCHEN_IDNA ".example"); + gni_test (gni_idn_name, flags, NAEMCHEN_IDNA ".example"); + gni_test (gni_idn_cname_to_non_idn_name, flags, "non-idn-name.example"); + gni_test (gni_idn_cname_to_idn_name, flags, + ANDERES_NAEMCHEN_IDNA ".example"); + + /* Test encoding errors. */ + gni_test (gni_invalid_idn_1, flags, "xn---.example"); + gni_test (gni_invalid_idn_2, flags, "xn--x.example"); +} +} + +static int +do_test (void) +{ + void *handle = xdlopen ("tst-no-libidn2.so", RTLD_LAZY); + { + /* Verify that this replaced libidn2. */ + void *handle2 = xdlopen (LIBIDN2_SONAME, RTLD_LAZY | RTLD_NOLOAD); + TEST_VERIFY (handle2 == handle); + xdlclose (handle2); + } + + if (setlocale (LC_CTYPE, "en_US.UTF-8") == NULL) + FAIL_EXIT1 ("setlocale: %m"); + + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + gai_tests (); + gni_tests (); + + resolv_test_end (aux); + xdlclose (handle); + return 0; +} + +#include diff --git a/resolv/tst-resolv-ai_idn.c b/resolv/tst-resolv-ai_idn.c new file mode 100644 index 0000000000..df8203b14a --- /dev/null +++ b/resolv/tst-resolv-ai_idn.c @@ -0,0 +1,49 @@ +/* Test getaddrinfo and getnameinfo with AI_IDN, NI_IDN (UTF-8). + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#define TEST_USE_UTF8 1 +#include "tst-resolv-ai_idn-common.c" + +#include +#include + +static int +do_test (void) +{ + void *handle = dlopen (LIBIDN2_SONAME, RTLD_LAZY); + if (handle == NULL) + FAIL_UNSUPPORTED ("libidn2 not installed"); + + if (setlocale (LC_CTYPE, "en_US.UTF-8") == NULL) + FAIL_EXIT1 ("setlocale: %m"); + + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + gai_tests_with_libidn2 (); + gni_tests_with_libidn2 (); + + resolv_test_end (aux); + xdlclose (handle); + return 0; +} + +#include -- cgit 1.4.1