diff options
Diffstat (limited to 'src/libnm-core-aux-intern/nm-libnm-core-utils.c')
| -rw-r--r-- | src/libnm-core-aux-intern/nm-libnm-core-utils.c | 462 |
1 files changed, 395 insertions, 67 deletions
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 b38accb2..5e7b90a4 100644 --- a/src/libnm-core-aux-intern/nm-libnm-core-utils.c +++ b/src/libnm-core-aux-intern/nm-libnm-core-utils.c @@ -479,6 +479,151 @@ nm_utils_validate_dhcp_dscp(const char *dscp, GError **error) } gboolean +nm_utils_validate_shared_dhcp_range(const char *shared_dhcp_range, + GPtrArray *addresses, + GError **error) +{ + char *start_address_str; + char *end_address_str; + NMIPAddress *interface_address_with_prefix; + NMIPAddr interface_address; + NMIPAddr start_address; + NMIPAddr end_address; + guint32 i; + guint32 mask; + guint32 prefix_length; + guint32 start_network; + guint32 end_network; + guint32 interface_network; + guint32 start_ip_length; + bool range_is_in_interface_network; + gs_free char *to_free = NULL; + + g_return_val_if_fail(!error || !(*error), FALSE); + + if (!shared_dhcp_range) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("NULL DHCP range; it should be provided as <START_IP>,<END_IP>.")); + return FALSE; + } + + if (!*shared_dhcp_range) { + return TRUE; + } + + if (!addresses) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Non-NULL range and NULL addresses detected.")); + return FALSE; + } + + end_address_str = strchr(shared_dhcp_range, ','); + if (!end_address_str) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Invalid DHCP range; it should be provided as <START_IP>,<END_IP>.")); + return FALSE; + } + + start_ip_length = end_address_str - shared_dhcp_range; + if (start_ip_length > 15) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Start IP has invalid length.")); + return FALSE; + } + + start_address_str = nm_strndup_a(200, shared_dhcp_range, start_ip_length, &to_free); + ++end_address_str; /* end address is pointing to ',', shift it to the actual address */ + + if (!nm_inet_parse_bin(AF_INET, start_address_str, NULL, &start_address)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Start IP is invalid.")); + return FALSE; + } + + if (!nm_inet_parse_bin(AF_INET, end_address_str, NULL, &end_address)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("End IP is invalid.")); + return FALSE; + } + + if (ntohl(start_address.addr4) > ntohl(end_address.addr4)) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Start IP should be lower than the end IP.")); + return FALSE; + } + + range_is_in_interface_network = FALSE; + for (i = 0; i < (*addresses).len; ++i) { + interface_address_with_prefix = (NMIPAddress *) addresses->pdata[i]; + nm_inet_parse_bin(AF_INET, + nm_ip_address_get_address(interface_address_with_prefix), + NULL, + &interface_address); + prefix_length = nm_ip_address_get_prefix(interface_address_with_prefix); + mask = nm_utils_ip4_prefix_to_netmask(prefix_length); + + interface_network = interface_address.addr4 & mask; + start_network = start_address.addr4 & mask; + end_network = end_address.addr4 & mask; + + if (start_network == interface_network && end_network == interface_network) { + range_is_in_interface_network = TRUE; + break; + } + } + + if (!range_is_in_interface_network) { + g_set_error_literal( + error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Requested range is not in any network configured on the interface.")); + return FALSE; + } + + return TRUE; +} + +gboolean +nm_utils_validate_shared_dhcp_lease_time(int shared_dhcp_lease_time, GError **error) +{ + g_return_val_if_fail(!error || !(*error), FALSE); + + if (shared_dhcp_lease_time == 0 || shared_dhcp_lease_time == G_MAXINT32) { + return TRUE; + } + + if (shared_dhcp_lease_time < NM_MIN_FINITE_LEASE_TIME + || NM_MAX_FINITE_LEASE_TIME < shared_dhcp_lease_time) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Invalid DHCP lease time value; it should be either default or a positive " + "number between %u and %u or %s."), + NM_MIN_FINITE_LEASE_TIME, + NM_MAX_FINITE_LEASE_TIME, + NM_INFINITE_LEASE_TIME); + return FALSE; + } + + return TRUE; +} + +gboolean nm_settings_connection_validate_permission_user(const char *item, gssize len) { gsize l; @@ -555,112 +700,295 @@ nm_mptcp_flags_normalize(NMMptcpFlags flags) /*****************************************************************************/ +/* + * nm_dns_uri_parse: + * @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 + * + * Parses the given name server URI string. Each name server is represented + * by the following grammar: + * + * NAMESERVER := { PLAIN | TLS_URI | UDP_URI } + * PLAIN := { ipv4address | ipv6address } [ '#' SERVERNAME ] + * TLS_URI := 'dns+tls://' URI_ADDRESS [ ':' PORT ] [ '#' SERVERNAME ] + * UDP_URI := 'dns+udp://' URI_ADDRESS [ ':' PORT ] + * URI_ADDRESS := { ipv4address | '[' ipv6address [ '%' ifname ] ']' } + * + * Examples: + * + * 192.0.2.0 + * 192.0.2.0#example.com + * 2001:db8::1 + * dns+tls://192.0.2.0 + * dns+tls://[2001:db8::1] + * dns+tls://192.0.2.0:53#example.com + * dns+udp://[fe80::1%enp1s0] + * + * Note that on return, the lifetime of the members in the @dns struct is + * the same as the input string @str. + * + * Returns: %TRUE on success, %FALSE on failure + */ gboolean -nm_utils_dnsname_parse(int addr_family, - const char *dns, - int *out_addr_family, - gpointer /* (NMIPAddr **) */ out_addr, - const char **out_servername) +nm_dns_uri_parse(int addr_family, const char *str, NMDnsServer *dns) { - gs_free char *dns_heap = NULL; - const char *s; - NMIPAddr addr; + NMDnsServer dns_stack; + gs_free char *addr_port_heap = NULL; + gs_free char *addr_heap = NULL; + const char *addr_port; + const char *addr; + const char *name; + const char *port; nm_assert_addr_family_or_unspec(addr_family); - nm_assert(!out_addr || out_addr_family || NM_IN_SET(addr_family, AF_INET, AF_INET6)); if (!dns) + dns = &dns_stack; + + if (!str) return FALSE; - s = strchr(dns, '#'); + *dns = (NMDnsServer) { + .port = -1, + }; - if (s) { - dns = nm_strndup_a(200, dns, s - dns, &dns_heap); - s++; + if (NM_STR_HAS_PREFIX(str, "dns+tls://")) { + dns->scheme = NM_DNS_URI_SCHEME_TLS; + str += NM_STRLEN("dns+tls://"); + } else if (NM_STR_HAS_PREFIX(str, "dns+udp://")) { + dns->scheme = NM_DNS_URI_SCHEME_UDP; + str += NM_STRLEN("dns+udp://"); + } else { + name = strchr(str, '#'); + if (name) { + str = nm_strndup_a(200, str, name - str, &addr_heap); + name++; + } + + if (name && name[0] == '\0') { + /* empty DoT server name is not allowed */ + return FALSE; + } + + if (!nm_inet_parse_bin(addr_family, str, &dns->addr_family, &dns->addr)) + return FALSE; + + dns->servername = name; + dns->scheme = NM_DNS_URI_SCHEME_NONE; + + return TRUE; + } + + addr_port = str; + name = strrchr(addr_port, '#'); + if (name) { + addr_port = nm_strndup_a(100, addr_port, name - addr_port, &addr_port_heap); + name++; + if (*name == '\0') { + /* empty DoT server name not allowed */ + return FALSE; + } + dns->servername = name; } - if (s && s[0] == '\0') { - /* "ADDR#" empty DoT SNI name is not allowed. */ + if (addr_family != AF_INET && *addr_port == '[') { + const char *end; + char *perc; + + addr_family = AF_INET6; + addr_port++; + end = strchr(addr_port, ']'); + if (!end) + return FALSE; + addr = nm_strndup_a(100, addr_port, end - addr_port, &addr_heap); + + /* IPv6 link-local scope-id */ + perc = strchr(addr, '%'); + if (perc) { + *perc = '\0'; + if (g_strlcpy(dns->interface, perc + 1, sizeof(dns->interface)) + >= sizeof(dns->interface)) + return FALSE; + } + + /* port */ + end++; + if (*end == ':') { + end++; + dns->port = _nm_utils_ascii_str_to_int64(end, 10, 0, 65535, G_MAXINT32); + if (dns->port == G_MAXINT32) + return FALSE; + } + } else if (addr_family != AF_INET6) { + /* square brackets are mandatory for IPv6, so it must be IPv4 */ + + addr_family = AF_INET; + addr = addr_port; + + /* port */ + port = strchr(addr_port, ':'); + if (port) { + addr = nm_strndup_a(100, addr_port, port - addr_port, &addr_heap); + port++; + dns->port = _nm_utils_ascii_str_to_int64(port, 10, 0, 65535, G_MAXINT32); + if (dns->port == G_MAXINT32) + return FALSE; + } + } else { return FALSE; } - if (!nm_inet_parse_bin(addr_family, dns, &addr_family, out_addr ? &addr : NULL)) + if (!nm_inet_parse_bin(addr_family, addr, &dns->addr_family, &dns->addr)) + return FALSE; + + if (dns->scheme != NM_DNS_URI_SCHEME_TLS && dns->servername) + 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))) return FALSE; - NM_SET_OUT(out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set(addr_family, out_addr, &addr); - NM_SET_OUT(out_servername, s); return TRUE; } -const char * -nm_utils_dnsname_construct(int addr_family, - gconstpointer /* (const NMIPAddr *) */ addr, - const char *server_name, - char *result, - gsize result_len) +/* @nm_dns_uri_parse_plain: + * @addr_family: the address family, or AF_UNSPEC to autodetect it + * @str: the name server URI string + * @out_addrstr: the buffer to fill with the address string on return, + * or %NULL. Must be of size at least NM_INET_ADDRSTRLEN. + * @out_addr: the %NMIPAddr struct to fill on return, or %NULL + * + * Returns whether the string contains a "plain" (DNS over UDP on port 53) + * name server. In such case, it fills the arguments with the address + * of the name server. + * + * Returns: %TRUE on success, %FALSE if the string can't be parsed or + * if it's not a plain name server. + */ +gboolean +nm_dns_uri_parse_plain(int addr_family, const char *str, char *out_addrstr, NMIPAddr *out_addr) { - char sbuf[NM_INET_ADDRSTRLEN]; - gsize l; - int d; + NMDnsServer dns; - nm_assert_addr_family(addr_family); - nm_assert(addr); - nm_assert(!server_name || !nm_str_is_empty(server_name)); - - nm_inet_ntop(addr_family, addr, sbuf); + if (!nm_dns_uri_parse(addr_family, str, &dns)) + return FALSE; - if (!server_name) { - l = g_strlcpy(result, sbuf, result_len); - } else { - d = g_snprintf(result, result_len, "%s#%s", sbuf, server_name); - nm_assert(d >= 0); - l = (gsize) d; + switch (dns.scheme) { + case NM_DNS_URI_SCHEME_TLS: + return FALSE; + case NM_DNS_URI_SCHEME_NONE: + NM_SET_OUT(out_addr, dns.addr); + if (out_addrstr) { + nm_inet_ntop(dns.addr_family, &dns.addr, out_addrstr); + } + return TRUE; + case NM_DNS_URI_SCHEME_UDP: + if (dns.port != -1 && dns.port != 53) + return FALSE; + if (dns.interface[0]) + return FALSE; + NM_SET_OUT(out_addr, dns.addr); + if (out_addrstr) { + nm_inet_ntop(dns.addr_family, &dns.addr, out_addrstr); + } + return TRUE; + case NM_DNS_URI_SCHEME_UNKNOWN: + default: + return FALSE; } - - return l < result_len ? result : NULL; } +/* @nm_dns_uri_normalize: + * @addr_family: the address family, or AF_UNSPEC to autodetect it + * @str: the name server URI string + * @out_free: the newly-allocated string to set on return, or %NULL + * + * Returns the "normal" representation for the given name server URI. + * Note that a plain name server (DNS over UDP on port 53) is always + * represented in the "legacy" (non-URI) form. + * + * Returns: the normalized DNS URI + */ const char * -nm_utils_dnsname_normalize(int addr_family, const char *dns, char **out_free) +nm_dns_uri_normalize(int addr_family, const char *str, char **out_free) { - char sbuf[NM_INET_ADDRSTRLEN]; - const char *server_name; - char *s; - NMIPAddr a; - gsize l; + NMDnsServer dns; + char addrstr[NM_INET_ADDRSTRLEN]; + char portstr[32]; + char *ret; + gsize len; nm_assert_addr_family_or_unspec(addr_family); - nm_assert(dns); + nm_assert(str); nm_assert(out_free && !*out_free); - if (!nm_utils_dnsname_parse(addr_family, dns, &addr_family, &a, &server_name)) + if (!nm_dns_uri_parse(addr_family, str, &dns)) return NULL; - nm_inet_ntop(addr_family, &a, sbuf); + nm_inet_ntop(dns.addr_family, &dns.addr, addrstr); - l = strlen(sbuf); + if (dns.port != -1) { + nm_assert(dns.port >= 0 && dns.port <= 65535); + g_snprintf(portstr, sizeof(portstr), "%d", dns.port); + } - /* In the vast majority of cases, the name is in fact normalized. Check - * whether it is, and don't duplicate the string. */ - if (strncmp(dns, sbuf, l) == 0) { - if (server_name) { - if (dns[l] == '#' && nm_streq(&dns[l + 1], server_name)) - return dns; - } else { - if (dns[l] == '\0') - return dns; + switch (dns.scheme) { + case NM_DNS_URI_SCHEME_NONE: + len = strlen(addrstr); + /* In the vast majority of cases, the name is in fact normalized. Check + * whether it is, and don't duplicate the string. */ + if (strncmp(str, addrstr, len) == 0) { + if (dns.servername) { + if (str[len] == '#' && nm_streq(&str[len + 1], dns.servername)) + return str; + } else { + if (str[len] == '\0') + return str; + } + } + + if (!dns.servername) + ret = g_strdup(addrstr); + else + ret = g_strconcat(addrstr, "#", dns.servername, NULL); + break; + case NM_DNS_URI_SCHEME_UDP: + if (dns.interface[0] || dns.port != -1) { + ret = g_strdup_printf("dns+udp://%s%s%s%s%s%s%s", + dns.addr_family == AF_INET6 ? "[" : "", + addrstr, + dns.interface[0] ? "%" : "", + dns.interface[0] ? dns.interface : "", + dns.addr_family == AF_INET6 ? "]" : "", + dns.port != -1 ? ":" : "", + dns.port != -1 ? portstr : ""); + break; } + ret = g_strdup_printf("%s%s%s", addrstr, dns.servername ? "#" : "", dns.servername ?: ""); + break; + case NM_DNS_URI_SCHEME_TLS: + ret = g_strdup_printf("dns+tls://%s%s%s%s%s%s%s%s%s", + dns.addr_family == AF_INET6 ? "[" : "", + addrstr, + dns.interface[0] ? "%%" : "", + dns.interface[0] ? dns.interface : "", + dns.addr_family == AF_INET6 ? "]" : "", + dns.port != -1 ? ":" : "", + dns.port != -1 ? portstr : "", + dns.servername ? "#" : "", + dns.servername ?: ""); + break; + case NM_DNS_URI_SCHEME_UNKNOWN: + default: + nm_assert_not_reached(); + ret = NULL; } - if (!server_name) - s = g_strdup(sbuf); - else - s = g_strconcat(sbuf, "#", server_name, NULL); + *out_free = ret; - *out_free = s; - return s; + return ret; } /*****************************************************************************/ |