diff --git a/src/core/dns/nm-dns-dnsconfd.c b/src/core/dns/nm-dns-dnsconfd.c index 3789caf885..5f32f3faec 100644 --- a/src/core/dns/nm-dns-dnsconfd.c +++ b/src/core/dns/nm-dns-dnsconfd.c @@ -374,7 +374,7 @@ server_builder_append_base(GVariantBuilder *argument_builder, NMDnsServer dns_server; gsize addr_size; - if (!nm_dns_uri_parse(address_family, address_string, &dns_server)) + if (!nm_dns_uri_parse(address_family, address_string, &dns_server, NULL)) return FALSE; addr_size = nm_utils_addr_family_to_size(dns_server.addr_family); diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c index 0701a1dcb5..1caed844eb 100644 --- a/src/core/dns/nm-dns-systemd-resolved.c +++ b/src/core/dns/nm-dns-systemd-resolved.c @@ -398,7 +398,7 @@ update_add_ip_config(NMDnsSystemdResolved *self, for (i = 0; i < n; i++) { NMDnsServer dns_server; - if (!nm_dns_uri_parse(ip_data->addr_family, strarr[i], &dns_server)) + if (!nm_dns_uri_parse(ip_data->addr_family, strarr[i], &dns_server, NULL)) continue; if (!NM_IN_SET(dns_server.scheme, diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index 35dc81cff9..b200b82087 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -3987,7 +3987,7 @@ _l3cfg_routed_dns_apply(NML3Cfg *self, const NML3ConfigData *l3cd) NMDnsServer dns; int r; - if (!nm_dns_uri_parse(addr_family, nameservers[i], &dns)) + if (!nm_dns_uri_parse(addr_family, nameservers[i], &dns, NULL)) continue; /* Find the gateway to the DNS over the current interface. When diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 1e2e2d92c0..b9e3f91920 100644 --- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -2056,8 +2056,9 @@ make_ip4_setting(shvarFile *ifcfg, * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting()) */ for (i = 1; i < 10000; i++) { - NMDnsServer dns; - char tag[256]; + NMDnsServer dns; + char tag[256]; + gs_free_error GError *local = NULL; numbered_tag(tag, "DNS", i); nm_clear_g_free(&value); @@ -2065,12 +2066,13 @@ make_ip4_setting(shvarFile *ifcfg, if (!v) break; - if (!nm_dns_uri_parse(AF_UNSPEC, v, &dns)) { + if (!nm_dns_uri_parse(AF_UNSPEC, v, &dns, &local)) { g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid DNS server address '%s'", - v); + "Invalid DNS server address '%s': %s", + v, + local->message); return NULL; } @@ -2607,8 +2609,9 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting()) */ for (i = 1; i < 10000; i++) { - NMDnsServer dns; - char tag[256]; + gs_free_error GError *err = NULL; + NMDnsServer dns; + char tag[256]; numbered_tag(tag, "DNS", i); nm_clear_g_free(&value); @@ -2616,14 +2619,15 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea if (!v) break; - if (!nm_dns_uri_parse(AF_UNSPEC, v, &dns)) { + if (!nm_dns_uri_parse(AF_UNSPEC, v, &dns, &err)) { if (is_disabled) continue; g_set_error(error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid DNS server address '%s'", - v); + "Invalid DNS server address '%s': %s", + v, + err->message); return NULL; } if (dns.addr_family == AF_INET6) { diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.c b/src/libnm-core-aux-intern/nm-libnm-core-utils.c index 94aa42bc87..2d709e20ef 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -705,6 +705,7 @@ nm_mptcp_flags_normalize(NMMptcpFlags flags) * @addr_family: the address family, or AF_UNSPEC to autodetect it * @str: the name server URI string to parse * @dns: the name server descriptor to fill, or %NULL + * @error: the error to set if the string cannot be parsed * * Parses the given name server URI string. Each name server is represented * by the following grammar: @@ -731,7 +732,7 @@ nm_mptcp_flags_normalize(NMMptcpFlags flags) * Returns: %TRUE on success, %FALSE on failure */ gboolean -nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) +nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns, GError **error) { NMDnsServer dns_stack; gs_free char *addr_port_heap = NULL; @@ -746,8 +747,13 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) if (!dns) dns = &dns_stack; - if (!str) + if (!str) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the string is empty")); return FALSE; + } *dns = (NMDnsServer) {0}; @@ -765,12 +771,21 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) } if (name && name[0] == '\0') { - /* empty DoT server name is not allowed */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the DNS-over-TLS server name is empty")); return FALSE; } - if (!nm_inet_parse_bin(addr_family, str, &dns->addr_family, &dns->addr)) + if (!nm_inet_parse_bin(addr_family, str, &dns->addr_family, &dns->addr)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("\"%s\" is not a valid IP address or a supported URI"), + str); return FALSE; + } dns->servername = name; dns->scheme = NM_DNS_URI_SCHEME_NONE; @@ -784,7 +799,10 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) addr_port = nm_strndup_a(100, addr_port, name - addr_port, &addr_port_heap); name++; if (*name == '\0') { - /* empty DoT server name not allowed */ + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the DNS-over-TLS server name is empty")); return FALSE; } dns->servername = name; @@ -797,8 +815,13 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) addr_family = AF_INET6; addr_port++; end = strchr(addr_port, ']'); - if (!end) + if (!end) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("unterminated square bracket")); return FALSE; + } addr = nm_strndup_a(100, addr_port, end - addr_port, &addr_heap); /* IPv6 link-local scope-id */ @@ -806,8 +829,13 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) if (perc) { *perc = '\0'; if (g_strlcpy(dns->interface, perc + 1, sizeof(dns->interface)) - >= sizeof(dns->interface)) + >= sizeof(dns->interface)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the interface name is too long")); return FALSE; + } } /* port */ @@ -815,8 +843,14 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) if (*end == ':') { end++; dns->port = _nm_utils_ascii_str_to_int64(end, 10, 1, 65535, 0); - if (dns->port == 0) + if (dns->port == 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("\"%s\" is not a valid port number"), + end); return FALSE; + } } } else if (addr_family != AF_INET6) { /* square brackets are mandatory for IPv6, so it must be IPv4 */ @@ -830,23 +864,49 @@ nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) addr = nm_strndup_a(100, addr_port, port - addr_port, &addr_heap); port++; dns->port = _nm_utils_ascii_str_to_int64(port, 10, 1, 65535, 0); - if (dns->port == 0) + if (dns->port == 0) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("\"%s\" is not a valid port number"), + port); return FALSE; + } } } else { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("IPv6 addresses must be enclosed in square brackets")); return FALSE; } - if (!nm_inet_parse_bin(addr_family, addr, &dns->addr_family, &dns->addr)) + if (!nm_inet_parse_bin(addr_family, addr, &dns->addr_family, &dns->addr)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("\"%s\" is not a valid IP address"), + addr); return FALSE; + } - if (dns->scheme != NM_DNS_URI_SCHEME_TLS && dns->servername) + if (dns->scheme != NM_DNS_URI_SCHEME_TLS && dns->servername) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the server name is only supported for DNS-over-TLS")); return FALSE; + } /* For now, allow the interface only for IPv6 link-local addresses */ if (dns->interface[0] - && (dns->addr_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&dns->addr.addr6))) + && (dns->addr_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&dns->addr.addr6))) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("the scope-id is only supported for IPv6 link-local addresses")); return FALSE; + } return TRUE; } @@ -870,7 +930,7 @@ nm_dns_uri_parse_plain(int addr_family, const char *str, char *out_addrstr, NMIP { NMDnsServer dns; - if (!nm_dns_uri_parse(addr_family, str, &dns)) + if (!nm_dns_uri_parse(addr_family, str, &dns, NULL)) return FALSE; switch (dns.scheme) { @@ -922,7 +982,7 @@ nm_dns_uri_normalize(int addr_family, const char *str, char **out_free) nm_assert(str); nm_assert(out_free && !*out_free); - if (!nm_dns_uri_parse(addr_family, str, &dns)) + if (!nm_dns_uri_parse(addr_family, str, &dns, NULL)) return NULL; nm_inet_ntop(dns.addr_family, &dns.addr, addrstr); diff --git a/src/libnm-core-aux-intern/nm-libnm-core-utils.h b/src/libnm-core-aux-intern/nm-libnm-core-utils.h index eeb720d9bf..8f30b95820 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.h +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.h @@ -330,7 +330,7 @@ typedef struct { guint16 port; } NMDnsServer; -gboolean nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *out_dns); +gboolean nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *out_dns, GError **error); gboolean nm_dns_uri_parse_plain(int addr_family, const char *str, char *out_addrstr, NMIPAddr *out_addr); const char *nm_dns_uri_normalize(int addr_family, const char *str, char **out_free); diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index f8f735e968..bc5272bcd9 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -1158,14 +1158,17 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) addr_family = NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting); for (i = 0, n = 0; i < length; i++) { - if (!nm_dns_uri_parse(addr_family, list[i], NULL)) { + gs_free_error GError *error = NULL; + + if (!nm_dns_uri_parse(addr_family, list[i], NULL, &error)) { if (!read_handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid DNS server IPv%c address '%s'"), + _("ignoring invalid DNS server IPv%c address '%s': %s"), nm_utils_addr_family_to_char(addr_family), - list[i])) { + list[i], + error->message)) { do { nm_clear_g_free(&list[i]); } while (++i < length); diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c index ad95eb1ac5..959f457bb9 100644 --- a/src/libnm-core-impl/nm-setting-ip-config.c +++ b/src/libnm-core-impl/nm-setting-ip-config.c @@ -5630,14 +5630,19 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) /* Validate DNS */ if (priv->dns) { for (i = 0; i < priv->dns->len; i++) { - const char *dns = priv->dns->pdata[i]; + const char *dns = priv->dns->pdata[i]; + gs_free_error GError *local = NULL; - if (!nm_dns_uri_parse(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), dns, NULL)) { + if (!nm_dns_uri_parse(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), + dns, + NULL, + &local)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("%u. DNS server address is invalid"), - (i + 1u)); + _("%u. DNS server address is invalid: %s"), + (i + 1u), + local->message); g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c index 43e73c3dab..8f9daacdcc 100644 --- a/src/libnm-core-impl/tests/test-general.c +++ b/src/libnm-core-impl/tests/test-general.c @@ -11400,12 +11400,16 @@ test_connection_path(void) static void t_dns_0(const char *str) { - NMDnsServer server = {}; - gboolean ret; + gs_free_error GError *error = NULL; + NMDnsServer server = {}; + gboolean ret; - ret = nm_dns_uri_parse(AF_UNSPEC, str, &server); + ret = nm_dns_uri_parse(AF_UNSPEC, str, &server, &error); g_assert(!ret); + g_assert(error); + g_assert(error->message); + g_assert(error->message[0] != '\0'); } static void @@ -11422,10 +11426,12 @@ dns_uri_parse_ok(const char *str, gboolean ret; for (int i = 0; i < 2; i++) { - gboolean af_unspec = i; + gs_free_error GError *error = NULL; + gboolean af_unspec = i; - ret = nm_dns_uri_parse(af_unspec ? AF_UNSPEC : addr_family, str, &dns); + ret = nm_dns_uri_parse(af_unspec ? AF_UNSPEC : addr_family, str, &dns, &error); g_assert(ret); + g_assert_no_error(error); g_assert_cmpint(addr_family, ==, dns.addr_family); g_assert_cmpint(port, ==, dns.port); @@ -11436,8 +11442,9 @@ dns_uri_parse_ok(const char *str, g_assert_cmpstr(addrstr, ==, addr); /* Parse with the wrong address family must fail */ - ret = nm_dns_uri_parse(addr_family == AF_INET ? AF_INET6 : AF_INET, str, &dns); + ret = nm_dns_uri_parse(addr_family == AF_INET ? AF_INET6 : AF_INET, str, &dns, &error); g_assert(!ret); + g_assert(error); } } diff --git a/src/nm-initrd-generator/nmi-cmdline-reader.c b/src/nm-initrd-generator/nmi-cmdline-reader.c index 89deddf0a3..0ae491d738 100644 --- a/src/nm-initrd-generator/nmi-cmdline-reader.c +++ b/src/nm-initrd-generator/nmi-cmdline-reader.c @@ -1273,8 +1273,10 @@ reader_parse_rd_znet(Reader *reader, char *argument, gboolean net_ifnames) static void reader_parse_global_dns(Reader *reader, char *argument) { - if (!nm_dns_uri_parse(AF_UNSPEC, argument, NULL)) { - _LOGW(LOGD_CORE, "rd.net.dns: invalid server '%s'", argument); + gs_free_error GError *error = NULL; + + if (!nm_dns_uri_parse(AF_UNSPEC, argument, NULL, &error)) { + _LOGW(LOGD_CORE, "rd.net.dns: invalid server '%s': %s", argument, error->message); return; } diff --git a/src/nm-initrd-generator/tests/test-cmdline-reader.c b/src/nm-initrd-generator/tests/test-cmdline-reader.c index 413f61c655..2241891ad2 100644 --- a/src/nm-initrd-generator/tests/test-cmdline-reader.c +++ b/src/nm-initrd-generator/tests/test-cmdline-reader.c @@ -2664,7 +2664,7 @@ test_global_dns(void) gs_free char *dns_resolve_mode = NULL; gint64 carrier_timeout_sec = 0; - NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dns: invalid server 'foobar'"); + NMTST_EXPECT_NM_WARN("cmdline-reader: rd.net.dns: invalid server 'foobar':*"); connections = _parse(ARGV, &hostname, &carrier_timeout_sec,