diff options
Diffstat (limited to 'libnm-core/nm-utils.c')
| -rw-r--r-- | libnm-core/nm-utils.c | 6054 |
1 files changed, 0 insertions, 6054 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c deleted file mode 100644 index eebd73cf..00000000 --- a/libnm-core/nm-utils.c +++ /dev/null @@ -1,6054 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2005 - 2017 Red Hat, Inc. - */ - -#include "libnm-core/nm-default-libnm-core.h" - -#include "nm-utils.h" - -#include <stdlib.h> -#include <net/ethernet.h> -#include <arpa/inet.h> -#include <net/if_arp.h> -#include <uuid/uuid.h> -#include <libintl.h> -#include <gmodule.h> -#include <sys/stat.h> -#include <linux/pkt_sched.h> -#include <linux/if_infiniband.h> - -#include "nm-glib-aux/nm-json-aux.h" -#include "nm-glib-aux/nm-str-buf.h" -#include "nm-glib-aux/nm-enum-utils.h" -#include "nm-glib-aux/nm-time-utils.h" -#include "nm-glib-aux/nm-secret-utils.h" -#include "systemd/nm-sd-utils-shared.h" -#include "nm-libnm-core-intern/nm-common-macros.h" -#include "nm-utils-private.h" -#include "nm-setting-private.h" -#include "nm-crypto.h" -#include "nm-setting-bond.h" -#include "nm-setting-bridge.h" -#include "nm-setting-bridge-port.h" -#include "nm-setting-infiniband.h" -#include "nm-setting-ip6-config.h" -#include "nm-setting-team.h" -#include "nm-setting-vlan.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireless.h" - -/** - * SECTION:nm-utils - * @short_description: Utility functions - * - * A collection of utility functions for working with SSIDs, IP addresses, Wi-Fi - * access points and devices, among other things. - */ - -/*****************************************************************************/ - -/** - * NMUtilsPredicateStr: - * @str: the name to check. - * - * This function takes a string argument and returns either %TRUE or %FALSE. - * It is a general purpose predicate, for example used by nm_setting_option_clear_by_name(). - * - * Returns: %TRUE if the predicate function matches. - * - * Since: 1.26 - */ - -/*****************************************************************************/ - -struct _NMSockAddrEndpoint { - const char *host; - guint16 port; - guint refcount; - char endpoint[]; -}; - -static gboolean -NM_IS_SOCK_ADDR_ENDPOINT(const NMSockAddrEndpoint *self) -{ - return self && self->refcount > 0; -} - -static const char * -_parse_endpoint(char *str, guint16 *out_port) -{ - char * s; - const char *s_port; - gint16 port; - - /* Like - * - https://git.zx2c4.com/WireGuard/tree/src/tools/config.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n192 - * - https://github.com/systemd/systemd/blob/911649fdd43f3a9158b847947724a772a5a45c34/src/network/netdev/wireguard.c#L614 - */ - - g_strstrip(str); - - if (!str[0]) - return NULL; - - if (str[0] == '[') { - str++; - s = strchr(str, ']'); - if (!s) - return NULL; - if (s == str) - return NULL; - if (s[1] != ':') - return NULL; - if (!s[2]) - return NULL; - *s = '\0'; - s_port = &s[2]; - } else { - s = strrchr(str, ':'); - if (!s) - return NULL; - if (s == str) - return NULL; - if (!s[1]) - return NULL; - *s = '\0'; - s_port = &s[1]; - } - - if (!NM_STRCHAR_ALL(s_port, ch, (ch >= '0' && ch <= '9'))) - return NULL; - - port = _nm_utils_ascii_str_to_int64(s_port, 10, 1, G_MAXUINT16, 0); - if (port == 0) - return NULL; - - *out_port = port; - return str; -} - -/** - * nm_sock_addr_endpoint_new: - * @endpoint: the endpoint string. - * - * This function cannot fail, even if the @endpoint is invalid. - * The reason is to allow NMSockAddrEndpoint also to be used - * for tracking invalid endpoints. Use nm_sock_addr_endpoint_get_host() - * to determine whether the endpoint is valid. - * - * Returns: (transfer full): the new #NMSockAddrEndpoint endpoint. - */ -NMSockAddrEndpoint * -nm_sock_addr_endpoint_new(const char *endpoint) -{ - NMSockAddrEndpoint *ep; - gsize l_endpoint; - gsize l_host = 0; - gsize i; - gs_free char * host_clone = NULL; - const char * host; - guint16 port; - - g_return_val_if_fail(endpoint, NULL); - - l_endpoint = strlen(endpoint) + 1; - - host = _parse_endpoint(nm_strndup_a(200, endpoint, l_endpoint - 1, &host_clone), &port); - - if (host) - l_host = strlen(host) + 1; - - ep = g_malloc(sizeof(NMSockAddrEndpoint) + l_endpoint + l_host); - ep->refcount = 1; - memcpy(ep->endpoint, endpoint, l_endpoint); - if (host) { - i = l_endpoint; - memcpy(&ep->endpoint[i], host, l_host); - ep->host = &ep->endpoint[i]; - ep->port = port; - } else { - ep->host = NULL; - ep->port = 0; - } - return ep; -} - -/** - * nm_sock_addr_endpoint_ref: - * @self: (allow-none): the #NMSockAddrEndpoint - */ -NMSockAddrEndpoint * -nm_sock_addr_endpoint_ref(NMSockAddrEndpoint *self) -{ - if (!self) - return NULL; - - g_return_val_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self), NULL); - - nm_assert(self->refcount < G_MAXUINT); - - self->refcount++; - return self; -} - -/** - * nm_sock_addr_endpoint_unref: - * @self: (allow-none): the #NMSockAddrEndpoint - */ -void -nm_sock_addr_endpoint_unref(NMSockAddrEndpoint *self) -{ - if (!self) - return; - - g_return_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self)); - - if (--self->refcount == 0) - g_free(self); -} - -/** - * nm_sock_addr_endpoint_get_endpoint: - * @self: the #NMSockAddrEndpoint - * - * Gives the endpoint string. Since #NMSockAddrEndpoint's only - * information is the endpoint string, this can be used for comparing - * to instances for equality and order them lexically. - * - * Returns: (transfer none): the endpoint. - */ -const char * -nm_sock_addr_endpoint_get_endpoint(NMSockAddrEndpoint *self) -{ - g_return_val_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self), NULL); - - return self->endpoint; -} - -/** - * nm_sock_addr_endpoint_get_host: - * @self: the #NMSockAddrEndpoint - * - * Returns: (transfer none): the parsed host part of the endpoint. - * If the endpoint is invalid, %NULL will be returned. - */ -const char * -nm_sock_addr_endpoint_get_host(NMSockAddrEndpoint *self) -{ - g_return_val_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self), NULL); - - return self->host; -} - -/** - * nm_sock_addr_endpoint_get_port: - * @self: the #NMSockAddrEndpoint - * - * Returns: the parsed port part of the endpoint (the service). - * If the endpoint is invalid, -1 will be returned. - */ -gint32 -nm_sock_addr_endpoint_get_port(NMSockAddrEndpoint *self) -{ - g_return_val_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self), -1); - - return self->host ? (int) self->port : -1; -} - -gboolean -nm_sock_addr_endpoint_get_fixed_sockaddr(NMSockAddrEndpoint *self, gpointer sockaddr) -{ - int addr_family; - NMIPAddr addrbin; - const char *s; - guint scope_id = 0; - - g_return_val_if_fail(NM_IS_SOCK_ADDR_ENDPOINT(self), FALSE); - g_return_val_if_fail(sockaddr, FALSE); - - if (!self->host) - return FALSE; - - if (nm_utils_parse_inaddr_bin(AF_UNSPEC, self->host, &addr_family, &addrbin)) - goto good; - - /* See if there is an IPv6 scope-id... - * - * Note that it does not make sense to persist connection profiles to disk, - * that refenrence a scope-id (because the interface's ifindex changes on - * reboot). However, we also support runtime only changes like `nmcli device modify` - * where nothing is persisted to disk. At least in that case, passing a scope-id - * might be reasonable. So, parse that too. */ - s = strchr(self->host, '%'); - if (!s) - return FALSE; - - if (s[1] == '\0' || !NM_STRCHAR_ALL(&s[1], ch, (ch >= '0' && ch <= '9'))) - return FALSE; - - scope_id = _nm_utils_ascii_str_to_int64(&s[1], 10, 0, G_MAXINT32, G_MAXUINT); - if (scope_id == G_MAXUINT && errno) - return FALSE; - - { - gs_free char *tmp_str = NULL; - const char * host_part; - - host_part = nm_strndup_a(200, self->host, s - self->host, &tmp_str); - if (nm_utils_parse_inaddr_bin(AF_INET6, host_part, &addr_family, &addrbin)) - goto good; - } - - return FALSE; - -good: - switch (addr_family) { - case AF_INET: - *((struct sockaddr_in *) sockaddr) = (struct sockaddr_in){ - .sin_family = AF_INET, - .sin_addr = addrbin.addr4_struct, - .sin_port = htons(self->port), - }; - return TRUE; - case AF_INET6: - *((struct sockaddr_in6 *) sockaddr) = (struct sockaddr_in6){ - .sin6_family = AF_INET6, - .sin6_addr = addrbin.addr6, - .sin6_port = htons(self->port), - .sin6_scope_id = scope_id, - .sin6_flowinfo = 0, - }; - return TRUE; - } - - return FALSE; -} - -/*****************************************************************************/ - -struct IsoLangToEncodings { - const char * lang; - const char *const *encodings; -}; - -#define LANG_ENCODINGS(l, ...) \ - { \ - .lang = l, .encodings = NM_MAKE_STRV(__VA_ARGS__), \ - } - -/* 5-letter language codes */ -static const struct IsoLangToEncodings isoLangEntries5[] = { - /* Simplified Chinese */ - LANG_ENCODINGS("zh_cn", "euc-cn", "gb2312", "gb18030"), /* PRC */ - LANG_ENCODINGS("zh_sg", "euc-cn", "gb2312", "gb18030"), /* Singapore */ - - /* Traditional Chinese */ - LANG_ENCODINGS("zh_tw", "big5", "euc-tw"), /* Taiwan */ - LANG_ENCODINGS("zh_hk", "big5", "euc-tw", "big5-hkcs"), /* Hong Kong */ - LANG_ENCODINGS("zh_mo", "big5", "euc-tw"), /* Macau */ - - LANG_ENCODINGS(NULL, NULL)}; - -/* 2-letter language codes; we don't care about the other 3 in this table */ -static const struct IsoLangToEncodings isoLangEntries2[] = { - /* Japanese */ - LANG_ENCODINGS("ja", "euc-jp", "shift_jis", "iso-2022-jp"), - - /* Korean */ - LANG_ENCODINGS("ko", "euc-kr", "iso-2022-kr", "johab"), - - /* Thai */ - LANG_ENCODINGS("th", "iso-8859-11", "windows-874"), - - /* Central European */ - LANG_ENCODINGS("hu", "iso-8859-2", "windows-1250"), /* Hungarian */ - LANG_ENCODINGS("cs", "iso-8859-2", "windows-1250"), /* Czech */ - LANG_ENCODINGS("hr", "iso-8859-2", "windows-1250"), /* Croatian */ - LANG_ENCODINGS("pl", "iso-8859-2", "windows-1250"), /* Polish */ - LANG_ENCODINGS("ro", "iso-8859-2", "windows-1250"), /* Romanian */ - LANG_ENCODINGS("sk", "iso-8859-2", "windows-1250"), /* Slovakian */ - LANG_ENCODINGS("sl", "iso-8859-2", "windows-1250"), /* Slovenian */ - LANG_ENCODINGS("sh", "iso-8859-2", "windows-1250"), /* Serbo-Croatian */ - - /* Cyrillic */ - LANG_ENCODINGS("ru", "koi8-r", "windows-1251", "iso-8859-5"), /* Russian */ - LANG_ENCODINGS("be", "koi8-r", "windows-1251", "iso-8859-5"), /* Belorussian */ - LANG_ENCODINGS("bg", "windows-1251", "koi8-r", "iso-8859-5"), /* Bulgarian */ - LANG_ENCODINGS("mk", "koi8-r", "windows-1251", "iso-8859-5"), /* Macedonian */ - LANG_ENCODINGS("sr", "koi8-r", "windows-1251", "iso-8859-5"), /* Serbian */ - LANG_ENCODINGS("uk", "koi8-u", "koi8-r", "windows-1251"), /* Ukrainian */ - - /* Arabic */ - LANG_ENCODINGS("ar", "iso-8859-6", "windows-1256"), - - /* Baltic */ - LANG_ENCODINGS("et", "iso-8859-4", "windows-1257"), /* Estonian */ - LANG_ENCODINGS("lt", "iso-8859-4", "windows-1257"), /* Lithuanian */ - LANG_ENCODINGS("lv", "iso-8859-4", "windows-1257"), /* Latvian */ - - /* Greek */ - LANG_ENCODINGS("el", "iso-8859-7", "windows-1253"), - - /* Hebrew */ - LANG_ENCODINGS("he", "iso-8859-8", "windows-1255"), - LANG_ENCODINGS("iw", "iso-8859-8", "windows-1255"), - - /* Turkish */ - LANG_ENCODINGS("tr", "iso-8859-9", "windows-1254"), - - /* Table end */ - LANG_ENCODINGS(NULL, NULL)}; - -static GHashTable *langToEncodings5 = NULL; -static GHashTable *langToEncodings2 = NULL; - -static void -init_lang_to_encodings_hash(void) -{ - struct IsoLangToEncodings *enc; - - if (G_UNLIKELY(langToEncodings5 == NULL)) { - /* Five-letter codes */ - enc = (struct IsoLangToEncodings *) &isoLangEntries5[0]; - langToEncodings5 = g_hash_table_new(nm_str_hash, g_str_equal); - while (enc->lang) { - g_hash_table_insert(langToEncodings5, (gpointer) enc->lang, (gpointer) enc->encodings); - enc++; - } - } - - if (G_UNLIKELY(langToEncodings2 == NULL)) { - /* Two-letter codes */ - enc = (struct IsoLangToEncodings *) &isoLangEntries2[0]; - langToEncodings2 = g_hash_table_new(nm_str_hash, g_str_equal); - while (enc->lang) { - g_hash_table_insert(langToEncodings2, (gpointer) enc->lang, (gpointer) enc->encodings); - enc++; - } - } -} - -static gboolean -get_encodings_for_lang(const char *lang, const char *const **encodings) -{ - gs_free char *tmp_lang = NULL; - - g_return_val_if_fail(lang, FALSE); - g_return_val_if_fail(encodings, FALSE); - - init_lang_to_encodings_hash(); - - if ((*encodings = g_hash_table_lookup(langToEncodings5, lang))) - return TRUE; - - /* Truncate tmp_lang to length of 2 */ - if (strlen(lang) > 2) { - tmp_lang = g_strdup(lang); - tmp_lang[2] = '\0'; - if ((*encodings = g_hash_table_lookup(langToEncodings2, tmp_lang))) - return TRUE; - } - - return FALSE; -} - -static const char *const * -get_system_encodings(void) -{ - static const char *const *cached_encodings; - static char * default_encodings[4]; - const char *const * encodings = NULL; - char * lang; - - if (cached_encodings) - return cached_encodings; - - /* Use environment variables as encoding hint */ - lang = getenv("LC_ALL"); - if (!lang) - lang = getenv("LC_CTYPE"); - if (!lang) - lang = getenv("LANG"); - if (lang) { - char *dot; - - lang = g_ascii_strdown(lang, -1); - if ((dot = strchr(lang, '.'))) - *dot = '\0'; - - get_encodings_for_lang(lang, &encodings); - g_free(lang); - } - if (!encodings) { - g_get_charset((const char **) &default_encodings[0]); - default_encodings[1] = "iso-8859-1"; - default_encodings[2] = "windows-1251"; - default_encodings[3] = NULL; - encodings = (const char *const *) default_encodings; - } - - cached_encodings = encodings; - return cached_encodings; -} - -/*****************************************************************************/ - -static void __attribute__((constructor)) _nm_utils_init(void) -{ - static int initialized = 0; - - if (g_atomic_int_get(&initialized) != 0) - return; - - /* we don't expect this code to run multiple times, nor on multiple threads. - * - * In practice, it would not be a problem if two threads concurrently try to - * run the initialization code below, all code below itself is thread-safe, - * Hence, a poor-man guard "initialized" above is more than sufficient, - * although it does not guarantee that the code is not run concurrently. */ - - bindtextdomain(GETTEXT_PACKAGE, NMLOCALEDIR); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - - _nm_dbus_errors_init(); - - g_atomic_int_set(&initialized, 1); -} - -/*****************************************************************************/ - -gboolean _nm_utils_is_manager_process; - -/* ssid helpers */ - -/** - * nm_utils_ssid_to_utf8: - * @ssid: (array length=len): pointer to a buffer containing the SSID data - * @len: length of the SSID data in @ssid - * - * Wi-Fi SSIDs are byte arrays, they are _not_ strings. Thus, an SSID may - * contain embedded NULLs and other unprintable characters. Often it is - * useful to print the SSID out for debugging purposes, but that should be the - * _only_ use of this function. Do not use this function for any persistent - * storage of the SSID, since the printable SSID returned from this function - * cannot be converted back into the real SSID of the access point. - * - * This function does almost everything humanly possible to convert the input - * into a printable UTF-8 string, using roughly the following procedure: - * - * 1) if the input data is already UTF-8 safe, no conversion is performed - * 2) attempts to get the current system language from the LANG environment - * variable, and depending on the language, uses a table of alternative - * encodings to try. For example, if LANG=hu_HU, the table may first try - * the ISO-8859-2 encoding, and if that fails, try the Windows-1250 encoding. - * If all fallback encodings fail, replaces non-UTF-8 characters with '?'. - * 3) If the system language was unable to be determined, falls back to the - * ISO-8859-1 encoding, then to the Windows-1251 encoding. - * 4) If step 3 fails, replaces non-UTF-8 characters with '?'. - * - * Again, this function should be used for debugging and display purposes - * _only_. - * - * Returns: (transfer full): an allocated string containing a UTF-8 - * representation of the SSID, which must be freed by the caller using g_free(). - * Returns %NULL on errors. - **/ -char * -nm_utils_ssid_to_utf8(const guint8 *ssid, gsize len) -{ - const char *const *encodings; - const char *const *e; - char * converted = NULL; - - g_return_val_if_fail(ssid != NULL, NULL); - - if (g_utf8_validate((const char *) ssid, len, NULL)) - return g_strndup((const char *) ssid, len); - - encodings = get_system_encodings(); - - for (e = encodings; *e; e++) { - converted = g_convert((const char *) ssid, len, "UTF-8", *e, NULL, NULL, NULL); - if (converted) - break; - } - - if (!converted) { - converted = g_convert_with_fallback((const char *) ssid, - len, - "UTF-8", - encodings[0], - "?", - NULL, - NULL, - NULL); - } - - if (!converted) { - /* If there is still no converted string, the SSID probably - * contains characters not valid in the current locale. Convert - * the string to ASCII instead. - */ - - /* Use the printable range of 0x20-0x7E */ - char *valid_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" - "abcdefghijklmnopqrstuvwxyz{|}~"; - - converted = g_strndup((const char *) ssid, len); - g_strcanon(converted, valid_chars, '?'); - } - - return converted; -} - -char * -_nm_utils_ssid_to_utf8(GBytes *ssid) -{ - const guint8 *p; - gsize l; - - g_return_val_if_fail(ssid, NULL); - - p = g_bytes_get_data(ssid, &l); - return nm_utils_ssid_to_utf8(p, l); -} - -/* Shamelessly ripped from the Linux kernel ieee80211 stack */ -/** - * nm_utils_is_empty_ssid: - * @ssid: (array length=len): pointer to a buffer containing the SSID data - * @len: length of the SSID data in @ssid - * - * Different manufacturers use different mechanisms for not broadcasting the - * AP's SSID. This function attempts to detect blank/empty SSIDs using a - * number of known SSID-cloaking methods. - * - * Returns: %TRUE if the SSID is "empty", %FALSE if it is not - **/ -gboolean -nm_utils_is_empty_ssid(const guint8 *ssid, gsize len) -{ - /* Single white space is for Linksys APs */ - if (len == 1 && ssid[0] == ' ') - return TRUE; - - /* Otherwise, if the entire ssid is 0, we assume it is hidden */ - while (len--) { - if (ssid[len] != '\0') - return FALSE; - } - return TRUE; -} - -gboolean -_nm_utils_is_empty_ssid(GBytes *ssid) -{ - const guint8 *p; - gsize l; - - g_return_val_if_fail(ssid, FALSE); - - p = g_bytes_get_data(ssid, &l); - return nm_utils_is_empty_ssid(p, l); -} - -#define ESSID_MAX_SIZE 32 - -/** - * nm_utils_escape_ssid: - * @ssid: (array length=len): pointer to a buffer containing the SSID data - * @len: length of the SSID data in @ssid - * - * This function does a quick printable character conversion of the SSID, simply - * replacing embedded NULLs and non-printable characters with the hexadecimal - * representation of that character. Intended for debugging only, should not - * be used for display of SSIDs. - * - * Returns: pointer to the escaped SSID, which uses an internal static buffer - * and will be overwritten by subsequent calls to this function - **/ -const char * -nm_utils_escape_ssid(const guint8 *ssid, gsize len) -{ - static char escaped[ESSID_MAX_SIZE * 2 + 1]; - const guint8 *s = ssid; - char * d = escaped; - - if (nm_utils_is_empty_ssid(ssid, len)) { - memcpy(escaped, "<hidden>", sizeof("<hidden>")); - return escaped; - } - - len = MIN(len, (guint32) ESSID_MAX_SIZE); - while (len--) { - if (*s == '\0') { - *d++ = '\\'; - *d++ = '0'; - s++; - } else { - *d++ = *s++; - } - } - *d = '\0'; - return escaped; -} - -char * -_nm_utils_ssid_to_string_arr(const guint8 *ssid, gsize len) -{ - gs_free char *s_copy = NULL; - const char * s_cnst; - - if (len == 0) - return g_strdup("(empty)"); - - s_cnst = - nm_utils_buf_utf8safe_escape(ssid, len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &s_copy); - nm_assert(s_cnst); - - if (nm_utils_is_empty_ssid(ssid, len)) - return g_strdup_printf("\"%s\" (hidden)", s_cnst); - - return g_strdup_printf("\"%s\"", s_cnst); -} - -char * -_nm_utils_ssid_to_string(GBytes *ssid) -{ - gconstpointer p; - gsize l; - - if (!ssid) - return g_strdup("(none)"); - - p = g_bytes_get_data(ssid, &l); - return _nm_utils_ssid_to_string_arr(p, l); -} - -/** - * nm_utils_same_ssid: - * @ssid1: (array length=len1): the first SSID to compare - * @len1: length of the SSID data in @ssid1 - * @ssid2: (array length=len2): the second SSID to compare - * @len2: length of the SSID data in @ssid2 - * @ignore_trailing_null: %TRUE to ignore one trailing NULL byte - * - * Earlier versions of the Linux kernel added a NULL byte to the end of the - * SSID to enable easy printing of the SSID on the console or in a terminal, - * but this behavior was problematic (SSIDs are simply byte arrays, not strings) - * and thus was changed. This function compensates for that behavior at the - * cost of some compatibility with odd SSIDs that may legitimately have trailing - * NULLs, even though that is functionally pointless. - * - * Returns: %TRUE if the SSIDs are the same, %FALSE if they are not - **/ -gboolean -nm_utils_same_ssid(const guint8 *ssid1, - gsize len1, - const guint8 *ssid2, - gsize len2, - gboolean ignore_trailing_null) -{ - g_return_val_if_fail(ssid1 != NULL || len1 == 0, FALSE); - g_return_val_if_fail(ssid2 != NULL || len2 == 0, FALSE); - - if (ssid1 == ssid2 && len1 == len2) - return TRUE; - if (!ssid1 || !ssid2) - return FALSE; - - if (ignore_trailing_null) { - if (len1 && ssid1[len1 - 1] == '\0') - len1--; - if (len2 && ssid2[len2 - 1] == '\0') - len2--; - } - - if (len1 != len2) - return FALSE; - - return memcmp(ssid1, ssid2, len1) == 0 ? TRUE : FALSE; -} - -gboolean -_nm_utils_string_slist_validate(GSList *list, const char **valid_values) -{ - GSList *iter; - - for (iter = list; iter; iter = iter->next) { - if (!g_strv_contains(valid_values, (char *) iter->data)) - return FALSE; - } - - return TRUE; -} - -/** - * _nm_utils_hash_values_to_slist: - * @hash: a #GHashTable - * - * Utility function to iterate over a hash table and return - * its values as a #GSList. - * - * Returns: (element-type gpointer) (transfer container): a newly allocated #GSList - * containing the values of the hash table. The caller must free the - * returned list with g_slist_free(). The hash values are not owned - * by the returned list. - **/ -GSList * -_nm_utils_hash_values_to_slist(GHashTable *hash) -{ - GSList * list = NULL; - GHashTableIter iter; - void * value; - - g_return_val_if_fail(hash, NULL); - - g_hash_table_iter_init(&iter, hash); - while (g_hash_table_iter_next(&iter, NULL, &value)) - list = g_slist_prepend(list, value); - - return list; -} - -static GVariant * -_nm_utils_strdict_to_dbus(const GValue *prop_value) -{ - return nm_utils_strdict_to_variant_ass(g_value_get_boxed(prop_value)); -} - -void -_nm_utils_strdict_from_dbus(GVariant *dbus_value, GValue *prop_value) -{ - GVariantIter iter; - const char * key, *value; - GHashTable * hash; - - hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - g_variant_iter_init(&iter, dbus_value); - while (g_variant_iter_next(&iter, "{&s&s}", &key, &value)) - g_hash_table_insert(hash, g_strdup(key), g_strdup(value)); - - g_value_take_boxed(prop_value, hash); -} - -const NMSettInfoPropertType nm_sett_info_propert_type_strdict = { - .dbus_type = NM_G_VARIANT_TYPE("a{ss}"), - .gprop_to_dbus_fcn = _nm_utils_strdict_to_dbus, - .gprop_from_dbus_fcn = _nm_utils_strdict_from_dbus, -}; - -GHashTable * -_nm_utils_copy_strdict(GHashTable *strdict) -{ - GHashTable * copy; - GHashTableIter iter; - gpointer key, value; - - copy = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - if (strdict) { - g_hash_table_iter_init(&iter, strdict); - while (g_hash_table_iter_next(&iter, &key, &value)) - g_hash_table_insert(copy, g_strdup(key), g_strdup(value)); - } - return copy; -} - -GPtrArray * -_nm_utils_copy_array(const GPtrArray *array, NMUtilsCopyFunc copy_func, GDestroyNotify free_func) -{ - GPtrArray *copy; - guint i; - - if (!array) - return g_ptr_array_new_with_free_func(free_func); - - copy = g_ptr_array_new_full(array->len, free_func); - for (i = 0; i < array->len; i++) - g_ptr_array_add(copy, copy_func(array->pdata[i])); - return copy; -} - -GPtrArray * -_nm_utils_copy_object_array(const GPtrArray *array) -{ - return _nm_utils_copy_array(array, g_object_ref, g_object_unref); -} - -gssize -_nm_utils_ptrarray_find_first(gconstpointer *list, gssize len, gconstpointer needle) -{ - gssize i; - - if (len == 0) - return -1; - - if (len > 0) { - g_return_val_if_fail(list, -1); - for (i = 0; i < len; i++) { - if (list[i] == needle) - return i; - } - } else { - g_return_val_if_fail(needle, -1); - for (i = 0; list && list[i]; i++) { - if (list[i] == needle) - return i; - } - } - return -1; -} - -void -_nm_utils_bytes_from_dbus(GVariant *dbus_value, GValue *prop_value) -{ - GBytes *bytes; - - if (g_variant_n_children(dbus_value)) { - gconstpointer data; - gsize length; - - data = g_variant_get_fixed_array(dbus_value, &length, 1); - bytes = g_bytes_new(data, length); - } else - bytes = NULL; - g_value_take_boxed(prop_value, bytes); -} - -/*****************************************************************************/ - -GSList * -_nm_utils_strv_to_slist(char **strv, gboolean deep_copy) -{ - GSList *list = NULL; - gsize i; - - if (!strv) - return NULL; - - if (deep_copy) { - for (i = 0; strv[i]; i++) - list = g_slist_prepend(list, g_strdup(strv[i])); - } else { - for (i = 0; strv[i]; i++) - list = g_slist_prepend(list, strv[i]); - } - return g_slist_reverse(list); -} - -char ** -_nm_utils_slist_to_strv(const GSList *slist, gboolean deep_copy) -{ - const GSList *iter; - char ** strv; - guint len, i; - - if (!slist) - return NULL; - - len = g_slist_length((GSList *) slist); - - strv = g_new(char *, len + 1); - - if (deep_copy) { - for (i = 0, iter = slist; iter; iter = iter->next, i++) { - nm_assert(iter->data); - strv[i] = g_strdup(iter->data); - } - } else { - for (i = 0, iter = slist; iter; iter = iter->next, i++) { - nm_assert(iter->data); - strv[i] = iter->data; - } - } - strv[i] = NULL; - - return strv; -} - -GPtrArray * -_nm_utils_strv_to_ptrarray(char **strv) -{ - GPtrArray *ptrarray; - gsize i, l; - - l = NM_PTRARRAY_LEN(strv); - - ptrarray = g_ptr_array_new_full(l, g_free); - - if (strv) { - for (i = 0; strv[i]; i++) - g_ptr_array_add(ptrarray, g_strdup(strv[i])); - } - - return ptrarray; -} - -char ** -_nm_utils_ptrarray_to_strv(const GPtrArray *ptrarray) -{ - char **strv; - guint i; - - if (!ptrarray) - return g_new0(char *, 1); - - strv = g_new(char *, ptrarray->len + 1); - - for (i = 0; i < ptrarray->len; i++) - strv[i] = g_strdup(ptrarray->pdata[i]); - strv[i] = NULL; - - return strv; -} - -/*****************************************************************************/ - -static gboolean -device_supports_ap_ciphers(guint32 dev_caps, guint32 ap_flags, gboolean static_wep) -{ - gboolean have_pair = FALSE; - gboolean have_group = FALSE; - /* Device needs to support at least one pairwise and one group cipher */ - - /* Pairwise */ - if (static_wep) { - /* Static WEP only uses group ciphers */ - have_pair = TRUE; - } else { - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40) - if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP40) - have_pair = TRUE; - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104) - if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP104) - have_pair = TRUE; - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) - if (ap_flags & NM_802_11_AP_SEC_PAIR_TKIP) - have_pair = TRUE; - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) - if (ap_flags & NM_802_11_AP_SEC_PAIR_CCMP) - have_pair = TRUE; - } - - /* Group */ - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40) - if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP40) - have_group = TRUE; - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104) - if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP104) - have_group = TRUE; - if (!static_wep) { - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP) - if (ap_flags & NM_802_11_AP_SEC_GROUP_TKIP) - have_group = TRUE; - if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP) - if (ap_flags & NM_802_11_AP_SEC_GROUP_CCMP) - have_group = TRUE; - } - - return (have_pair && have_group); -} - -/** - * nm_utils_ap_mode_security_valid: - * @type: the security type to check device capabilities against, - * e.g. #NMU_SEC_STATIC_WEP - * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g. - * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40 - * - * Given a set of device capabilities, and a desired security type to check - * against, determines whether the combination of device capabilities and - * desired security type are valid for AP/Hotspot connections. - * - * Returns: %TRUE if the device capabilities are compatible with the desired - * @type, %FALSE if they are not. - **/ -gboolean -nm_utils_ap_mode_security_valid(NMUtilsSecurityType type, NMDeviceWifiCapabilities wifi_caps) -{ - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_AP)) - return FALSE; - - /* Return TRUE for any security that wpa_supplicant's lightweight AP - * mode can handle: which is open, WEP, and WPA/WPA2 PSK. - */ - switch (type) { - case NMU_SEC_NONE: - case NMU_SEC_STATIC_WEP: - case NMU_SEC_WPA_PSK: - case NMU_SEC_WPA2_PSK: - case NMU_SEC_SAE: - case NMU_SEC_OWE: - return TRUE; - case NMU_SEC_LEAP: - case NMU_SEC_DYNAMIC_WEP: - case NMU_SEC_WPA_ENTERPRISE: - case NMU_SEC_WPA2_ENTERPRISE: - case NMU_SEC_WPA3_SUITE_B_192: - return FALSE; - case NMU_SEC_INVALID: - break; - } - return FALSE; -} - -/** - * nm_utils_security_valid: - * @type: the security type to check AP flags and device capabilities against, - * e.g. #NMU_SEC_STATIC_WEP - * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g. - * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40 - * @have_ap: whether the @ap_flags, @ap_wpa, and @ap_rsn arguments are valid - * @adhoc: whether the capabilities being tested are from an Ad-Hoc AP (IBSS) - * @ap_flags: bitfield of AP capabilities, e.g. #NM_802_11_AP_FLAGS_PRIVACY - * @ap_wpa: bitfield of AP capabilities derived from the AP's WPA beacon, - * e.g. (#NM_802_11_AP_SEC_PAIR_TKIP | #NM_802_11_AP_SEC_KEY_MGMT_PSK) - * @ap_rsn: bitfield of AP capabilities derived from the AP's RSN/WPA2 beacon, - * e.g. (#NM_802_11_AP_SEC_PAIR_CCMP | #NM_802_11_AP_SEC_PAIR_TKIP) - * - * Given a set of device capabilities, and a desired security type to check - * against, determines whether the combination of device, desired security - * type, and AP capabilities intersect. - * - * NOTE: this function cannot handle checking security for AP/Hotspot mode; - * use nm_utils_ap_mode_security_valid() instead. - * - * Returns: %TRUE if the device capabilities and AP capabilities intersect and are - * compatible with the desired @type, %FALSE if they are not - **/ -gboolean -nm_utils_security_valid(NMUtilsSecurityType type, - NMDeviceWifiCapabilities wifi_caps, - gboolean have_ap, - gboolean adhoc, - NM80211ApFlags ap_flags, - NM80211ApSecurityFlags ap_wpa, - NM80211ApSecurityFlags ap_rsn) -{ - switch (type) { - case NMU_SEC_NONE: - if (!have_ap) - return TRUE; - if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) - return FALSE; - if (ap_wpa || ap_rsn) - return FALSE; - return TRUE; - case NMU_SEC_LEAP: /* require PRIVACY bit for LEAP? */ - if (adhoc) - return FALSE; - /* fall-through */ - case NMU_SEC_STATIC_WEP: - if (!have_ap) { - if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) - return TRUE; - return FALSE; - } - if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) - return FALSE; - if (ap_wpa || ap_rsn) { - if (!device_supports_ap_ciphers(wifi_caps, ap_wpa, TRUE)) { - if (!device_supports_ap_ciphers(wifi_caps, ap_rsn, TRUE)) - return FALSE; - } - } - return TRUE; - case NMU_SEC_DYNAMIC_WEP: - if (adhoc) - return FALSE; - if (!have_ap) { - if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104)) - return TRUE; - return FALSE; - } - if (ap_rsn || !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)) - return FALSE; - /* Some APs broadcast minimal WPA-enabled beacons that must be handled */ - if (ap_wpa) { - if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) - return FALSE; - if (!device_supports_ap_ciphers(wifi_caps, ap_wpa, FALSE)) - return FALSE; - } - return TRUE; - case NMU_SEC_WPA_PSK: - if (adhoc) - return FALSE; - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA)) - return FALSE; - if (!have_ap) - return TRUE; - if (ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK) { - if ((ap_wpa & NM_802_11_AP_SEC_PAIR_TKIP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)) - return TRUE; - if ((ap_wpa & NM_802_11_AP_SEC_PAIR_CCMP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - return TRUE; - } - return FALSE; - case NMU_SEC_WPA2_PSK: - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) - return FALSE; - if (!have_ap) - return TRUE; - if (adhoc) { - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_IBSS_RSN)) - return FALSE; - if ((ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - return TRUE; - return FALSE; - } - if (ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK) { - if ((ap_rsn & NM_802_11_AP_SEC_PAIR_TKIP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)) - return TRUE; - if ((ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - return TRUE; - } - return FALSE; - case NMU_SEC_WPA_ENTERPRISE: - if (adhoc) - return FALSE; - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA)) - return FALSE; - if (!have_ap) - return TRUE; - if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) - return FALSE; - /* Ensure at least one WPA cipher is supported */ - if (!device_supports_ap_ciphers(wifi_caps, ap_wpa, FALSE)) - return FALSE; - return TRUE; - case NMU_SEC_WPA2_ENTERPRISE: - if (adhoc) - return FALSE; - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) - return FALSE; - if (!have_ap) - return TRUE; - if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) - return FALSE; - /* Ensure at least one WPA cipher is supported */ - if (!device_supports_ap_ciphers(wifi_caps, ap_rsn, FALSE)) - return FALSE; - return TRUE; - case NMU_SEC_SAE: - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) - return FALSE; - if (adhoc) - return FALSE; - if (!have_ap) - return TRUE; - if (ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_SAE) { - if ((ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP) - && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - return TRUE; - } - return FALSE; - case NMU_SEC_OWE: - if (adhoc) - return FALSE; - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) - return FALSE; - if (!have_ap) - return TRUE; - if (!NM_FLAGS_ANY(ap_rsn, NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) - return FALSE; - return TRUE; - case NMU_SEC_WPA3_SUITE_B_192: - if (adhoc) - return FALSE; - if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN)) - return FALSE; - if (!have_ap) - return TRUE; - if (ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192) - return TRUE; - return FALSE; - case NMU_SEC_INVALID: - break; - } - - return FALSE; -} - -/** - * nm_utils_wep_key_valid: - * @key: a string that might be a WEP key - * @wep_type: the #NMWepKeyType type of the WEP key - * - * Checks if @key is a valid WEP key - * - * Returns: %TRUE if @key is a WEP key, %FALSE if not - */ -gboolean -nm_utils_wep_key_valid(const char *key, NMWepKeyType wep_type) -{ - gsize keylen; - gsize i; - - if (!key) - return FALSE; - - if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) { - return nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_KEY) - || nm_utils_wep_key_valid(key, NM_WEP_KEY_TYPE_PASSPHRASE); - } - - keylen = strlen(key); - if (wep_type == NM_WEP_KEY_TYPE_KEY) { - if (keylen == 10 || keylen == 26) { - /* Hex key */ - for (i = 0; i < keylen; i++) { - if (!g_ascii_isxdigit(key[i])) - return FALSE; - } - } else if (keylen == 5 || keylen == 13) { - /* ASCII key */ - for (i = 0; i < keylen; i++) { - if (!g_ascii_isprint(key[i])) - return FALSE; - } - } else - return FALSE; - } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { - if (!keylen || keylen > 64) - return FALSE; - } - - return TRUE; -} - -/** - * nm_utils_wpa_psk_valid: - * @psk: a string that might be a WPA PSK - * - * Checks if @psk is a valid WPA PSK - * - * Returns: %TRUE if @psk is a WPA PSK, %FALSE if not - */ -gboolean -nm_utils_wpa_psk_valid(const char *psk) -{ - gsize psklen; - gsize i; - - if (!psk) - return FALSE; - - psklen = strlen(psk); - if (psklen < 8 || psklen > 64) - return FALSE; - - if (psklen == 64) { - /* Hex PSK */ - for (i = 0; i < psklen; i++) { - if (!g_ascii_isxdigit(psk[i])) - return FALSE; - } - } - - return TRUE; -} - -/** - * nm_utils_ip4_dns_to_variant: - * @dns: (type utf8): an array of IP address strings - * - * Utility function to convert an array of IP address strings int a #GVariant of - * type 'au' representing an array of IPv4 addresses. - * - * Returns: (transfer none): a new floating #GVariant representing @dns. - **/ -GVariant * -nm_utils_ip4_dns_to_variant(char **dns) -{ - GVariantBuilder builder; - gsize i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("au")); - - if (dns) { - for (i = 0; dns[i]; i++) { - guint32 ip = 0; - - inet_pton(AF_INET, dns[i], &ip); - g_variant_builder_add(&builder, "u", ip); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip4_dns_from_variant: - * @value: a #GVariant of type 'au' - * - * Utility function to convert a #GVariant of type 'au' representing a list of - * IPv4 addresses into an array of IP address strings. - * - * Returns: (transfer full) (type utf8): a %NULL-terminated array of IP address strings. - **/ -char ** -nm_utils_ip4_dns_from_variant(GVariant *value) -{ - const guint32 *array; - gsize length; - char ** dns; - gsize i; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("au")), NULL); - - array = g_variant_get_fixed_array(value, &length, sizeof(guint32)); - dns = g_new(char *, length + 1u); - for (i = 0; i < length; i++) - dns[i] = nm_utils_inet4_ntop_dup(array[i]); - dns[i] = NULL; - - return dns; -} - -/** - * nm_utils_ip4_addresses_to_variant: - * @addresses: (element-type NMIPAddress): an array of #NMIPAddress objects - * @gateway: (allow-none): the gateway IP address - * - * Utility function to convert a #GPtrArray of #NMIPAddress objects representing - * IPv4 addresses into a #GVariant of type 'aau' representing an array of - * NetworkManager IPv4 addresses (which are tuples of address, prefix, and - * gateway). The "gateway" field of the first address will get the value of - * @gateway (if non-%NULL). In all of the other addresses, that field will be 0. - * - * Returns: (transfer none): a new floating #GVariant representing @addresses. - **/ -GVariant * -nm_utils_ip4_addresses_to_variant(GPtrArray *addresses, const char *gateway) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aau")); - - if (addresses) { - for (i = 0; i < addresses->len; i++) { - NMIPAddress *addr = addresses->pdata[i]; - guint32 array[3]; - in_addr_t gw; - - if (nm_ip_address_get_family(addr) != AF_INET) - continue; - - gw = 0u; - if (gateway) { - in_addr_t a; - - if (inet_pton(AF_INET, gateway, &a) == 1) - gw = a; - gateway = NULL; - } - - nm_ip_address_get_address_binary(addr, &array[0]); - array[1] = nm_ip_address_get_prefix(addr); - array[2] = gw; - - g_variant_builder_add( - &builder, - "@au", - g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, array, 3, sizeof(guint32))); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip4_addresses_from_variant: - * @value: a #GVariant of type 'aau' - * @out_gateway: (out) (allow-none) (transfer full): on return, will contain the IP gateway - * - * Utility function to convert a #GVariant of type 'aau' representing a list of - * NetworkManager IPv4 addresses (which are tuples of address, prefix, and - * gateway) into a #GPtrArray of #NMIPAddress objects. The "gateway" field of - * the first address (if set) will be returned in @out_gateway; the "gateway" fields - * of the other addresses are ignored. - * - * Returns: (transfer full) (element-type NMIPAddress): a newly allocated - * #GPtrArray of #NMIPAddress objects - **/ -GPtrArray * -nm_utils_ip4_addresses_from_variant(GVariant *value, char **out_gateway) -{ - GPtrArray * addresses; - GVariantIter iter; - GVariant * addr_var; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aau")), NULL); - - if (out_gateway) - *out_gateway = NULL; - - g_variant_iter_init(&iter, value); - addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); - - while (g_variant_iter_next(&iter, "@au", &addr_var)) { - const guint32 *addr_array; - gsize length; - NMIPAddress * addr; - GError * error = NULL; - - addr_array = g_variant_get_fixed_array(addr_var, &length, sizeof(guint32)); - if (length < 3) { - g_warning("Ignoring invalid IP4 address"); - g_variant_unref(addr_var); - continue; - } - - addr = nm_ip_address_new_binary(AF_INET, &addr_array[0], addr_array[1], &error); - if (addr) { - g_ptr_array_add(addresses, addr); - - if (addr_array[2] && out_gateway && !*out_gateway) - *out_gateway = nm_utils_inet4_ntop_dup(addr_array[2]); - } else { - g_warning("Ignoring invalid IP4 address: %s", error->message); - g_clear_error(&error); - } - - g_variant_unref(addr_var); - } - - return addresses; -} - -/** - * nm_utils_ip4_routes_to_variant: - * @routes: (element-type NMIPRoute): an array of #NMIP4Route objects - * - * Utility function to convert a #GPtrArray of #NMIPRoute objects representing - * IPv4 routes into a #GVariant of type 'aau' representing an array of - * NetworkManager IPv4 routes (which are tuples of route, prefix, next hop, and - * metric). - * - * Returns: (transfer none): a new floating #GVariant representing @routes. - **/ -GVariant * -nm_utils_ip4_routes_to_variant(GPtrArray *routes) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aau")); - - if (routes) { - for (i = 0; i < routes->len; i++) { - NMIPRoute *route = routes->pdata[i]; - guint32 array[4]; - - if (nm_ip_route_get_family(route) != AF_INET) - continue; - - nm_ip_route_get_dest_binary(route, &array[0]); - array[1] = nm_ip_route_get_prefix(route); - nm_ip_route_get_next_hop_binary(route, &array[2]); - /* The old routes format uses "0" for default, not "-1" */ - array[3] = MAX(0, nm_ip_route_get_metric(route)); - - g_variant_builder_add( - &builder, - "@au", - g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, array, 4, sizeof(guint32))); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip4_routes_from_variant: - * @value: #GVariant of type 'aau' - * - * Utility function to convert a #GVariant of type 'aau' representing an array - * of NetworkManager IPv4 routes (which are tuples of route, prefix, next hop, - * and metric) into a #GPtrArray of #NMIPRoute objects. - * - * Returns: (transfer full) (element-type NMIPRoute): a newly allocated - * #GPtrArray of #NMIPRoute objects - **/ -GPtrArray * -nm_utils_ip4_routes_from_variant(GVariant *value) -{ - GVariantIter iter; - GVariant * route_var; - GPtrArray * routes; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aau")), NULL); - - g_variant_iter_init(&iter, value); - routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); - - while (g_variant_iter_next(&iter, "@au", &route_var)) { - const guint32 *route_array; - gsize length; - NMIPRoute * route; - GError * error = NULL; - - route_array = g_variant_get_fixed_array(route_var, &length, sizeof(guint32)); - if (length < 4) { - g_warning("Ignoring invalid IP4 route"); - g_variant_unref(route_var); - continue; - } - - route = nm_ip_route_new_binary(AF_INET, - &route_array[0], - route_array[1], - &route_array[2], - /* The old routes format uses "0" for default, not "-1" */ - route_array[3] ? (gint64) route_array[3] : -1, - &error); - if (route) - g_ptr_array_add(routes, route); - else { - g_warning("Ignoring invalid IP4 route: %s", error->message); - g_clear_error(&error); - } - g_variant_unref(route_var); - } - - return routes; -} - -/** - * nm_utils_ip4_netmask_to_prefix: - * @netmask: an IPv4 netmask in network byte order - * - * Returns: the CIDR prefix represented by the netmask - **/ -guint32 -nm_utils_ip4_netmask_to_prefix(guint32 netmask) -{ - G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4); - G_STATIC_ASSERT_EXPR(sizeof(int) == 4); - G_STATIC_ASSERT_EXPR(sizeof(netmask) == 4); - - return ((netmask != 0u) ? (guint32)(32 - __builtin_ctz(ntohl(netmask))) : 0u); -} - -/** - * nm_utils_ip4_prefix_to_netmask: - * @prefix: a CIDR prefix - * - * Returns: the netmask represented by the prefix, in network byte order - **/ -guint32 -nm_utils_ip4_prefix_to_netmask(guint32 prefix) -{ - return _nm_utils_ip4_prefix_to_netmask(prefix); -} - -/** - * nm_utils_ip4_get_default_prefix: - * @ip: an IPv4 address (in network byte order) - * - * When the Internet was originally set up, various ranges of IP addresses were - * segmented into three network classes: A, B, and C. This function will return - * a prefix that is associated with the IP address specified defining where it - * falls in the predefined classes. - * - * Returns: the default class prefix for the given IP - **/ -/* The function is originally from ipcalc.c of Red Hat's initscripts. */ -guint32 -nm_utils_ip4_get_default_prefix(guint32 ip) -{ - return _nm_utils_ip4_get_default_prefix(ip); -} - -/** - * nm_utils_ip6_dns_to_variant: - * @dns: (type utf8): an array of IP address strings - * - * Utility function to convert an array of IP address strings int a #GVariant of - * type 'aay' representing an array of IPv6 addresses. - * - * If a string cannot be parsed, it will be silently ignored. - * - * Returns: (transfer none): a new floating #GVariant representing @dns. - **/ -GVariant * -nm_utils_ip6_dns_to_variant(char **dns) -{ - GVariantBuilder builder; - gsize i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aay")); - if (dns) { - for (i = 0; dns[i]; i++) { - struct in6_addr ip; - - if (inet_pton(AF_INET6, dns[i], &ip) != 1) - continue; - g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip)); - } - } - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip6_dns_from_variant: - * @value: a #GVariant of type 'aay' - * - * Utility function to convert a #GVariant of type 'aay' representing a list of - * IPv6 addresses into an array of IP address strings. Each "ay" entry must be - * a IPv6 address in binary form (16 bytes long). Invalid entries are silently - * ignored. - * - * Returns: (transfer full) (type utf8): a %NULL-terminated array of IP address strings. - **/ -char ** -nm_utils_ip6_dns_from_variant(GVariant *value) -{ - GVariantIter iter; - GVariant * ip_var; - char ** dns; - gsize i; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aay")), NULL); - - dns = g_new(char *, g_variant_n_children(value) + 1); - - g_variant_iter_init(&iter, value); - i = 0; - while (g_variant_iter_next(&iter, "@ay", &ip_var)) { - gsize length; - const struct in6_addr *ip = g_variant_get_fixed_array(ip_var, &length, 1); - - if (length == sizeof(struct in6_addr)) - dns[i++] = nm_utils_inet6_ntop_dup(ip); - - g_variant_unref(ip_var); - } - dns[i] = NULL; - - return dns; -} - -/** - * nm_utils_ip6_addresses_to_variant: - * @addresses: (element-type NMIPAddress): an array of #NMIPAddress objects - * @gateway: (allow-none): the gateway IP address - * - * Utility function to convert a #GPtrArray of #NMIPAddress objects representing - * IPv6 addresses into a #GVariant of type 'a(ayuay)' representing an array of - * NetworkManager IPv6 addresses (which are tuples of address, prefix, and - * gateway). The "gateway" field of the first address will get the value of - * @gateway (if non-%NULL). In all of the other addresses, that field will be - * all 0s. - * - * Returns: (transfer none): a new floating #GVariant representing @addresses. - **/ -GVariant * -nm_utils_ip6_addresses_to_variant(GPtrArray *addresses, const char *gateway) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("a(ayuay)")); - - if (addresses) { - for (i = 0; i < addresses->len; i++) { - NMIPAddress * addr = addresses->pdata[i]; - struct in6_addr address_bin; - struct in6_addr gateway_bin_data; - const struct in6_addr *gateway_bin; - - if (nm_ip_address_get_family(addr) != AF_INET6) - continue; - - nm_ip_address_get_address_binary(addr, &address_bin); - - gateway_bin = &in6addr_any; - if (gateway) { - if (inet_pton(AF_INET6, gateway, &gateway_bin_data) == 1) - gateway_bin = &gateway_bin_data; - gateway = NULL; - } - - g_variant_builder_add(&builder, - "(@ayu@ay)", - nm_g_variant_new_ay_in6addr(&address_bin), - (guint32) nm_ip_address_get_prefix(addr), - nm_g_variant_new_ay_in6addr(gateway_bin)); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip6_addresses_from_variant: - * @value: a #GVariant of type 'a(ayuay)' - * @out_gateway: (out) (allow-none) (transfer full): on return, will contain the IP gateway - * - * Utility function to convert a #GVariant of type 'a(ayuay)' representing a - * list of NetworkManager IPv6 addresses (which are tuples of address, prefix, - * and gateway) into a #GPtrArray of #NMIPAddress objects. The "gateway" field - * of the first address (if set) will be returned in @out_gateway; the "gateway" - * fields of the other addresses are ignored. - * - * Returns: (transfer full) (element-type NMIPAddress): a newly allocated - * #GPtrArray of #NMIPAddress objects - **/ -GPtrArray * -nm_utils_ip6_addresses_from_variant(GVariant *value, char **out_gateway) -{ - GVariantIter iter; - GVariant * addr_var, *gateway_var; - guint32 prefix; - GPtrArray * addresses; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("a(ayuay)")), NULL); - - if (out_gateway) - *out_gateway = NULL; - - g_variant_iter_init(&iter, value); - addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); - - while (g_variant_iter_next(&iter, "(@ayu@ay)", &addr_var, &prefix, &gateway_var)) { - NMIPAddress * addr; - const struct in6_addr *addr_bytes, *gateway_bytes; - gsize addr_len, gateway_len; - GError * error = NULL; - - if (!g_variant_is_of_type(addr_var, G_VARIANT_TYPE_BYTESTRING) - || !g_variant_is_of_type(gateway_var, G_VARIANT_TYPE_BYTESTRING)) { - g_warning("%s: ignoring invalid IP6 address structure", __func__); - goto next; - } - - addr_bytes = g_variant_get_fixed_array(addr_var, &addr_len, 1); - if (addr_len != 16) { - g_warning("%s: ignoring invalid IP6 address of length %d", __func__, (int) addr_len); - goto next; - } - - addr = nm_ip_address_new_binary(AF_INET6, addr_bytes, prefix, &error); - if (addr) { - g_ptr_array_add(addresses, addr); - - if (out_gateway && !*out_gateway) { - gateway_bytes = g_variant_get_fixed_array(gateway_var, &gateway_len, 1); - if (gateway_len != 16) { - g_warning("%s: ignoring invalid IP6 address of length %d", - __func__, - (int) gateway_len); - goto next; - } - if (!IN6_IS_ADDR_UNSPECIFIED(gateway_bytes)) - *out_gateway = nm_utils_inet6_ntop_dup(gateway_bytes); - } - } else { - g_warning("Ignoring invalid IP6 address: %s", error->message); - g_clear_error(&error); - } - -next: - g_variant_unref(addr_var); - g_variant_unref(gateway_var); - } - - return addresses; -} - -/** - * nm_utils_ip6_routes_to_variant: - * @routes: (element-type NMIPRoute): an array of #NMIPRoute objects - * - * Utility function to convert a #GPtrArray of #NMIPRoute objects representing - * IPv6 routes into a #GVariant of type 'a(ayuayu)' representing an array of - * NetworkManager IPv6 routes (which are tuples of route, prefix, next hop, and - * metric). - * - * Returns: (transfer none): a new floating #GVariant representing @routes. - **/ -GVariant * -nm_utils_ip6_routes_to_variant(GPtrArray *routes) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("a(ayuayu)")); - - if (routes) { - for (i = 0; i < routes->len; i++) { - NMIPRoute * route = routes->pdata[i]; - struct in6_addr dest_bytes; - struct in6_addr next_hop_bytes; - guint32 metric; - - if (nm_ip_route_get_family(route) != AF_INET6) - continue; - - nm_ip_route_get_dest_binary(route, &dest_bytes); - nm_ip_route_get_next_hop_binary(route, &next_hop_bytes); - - /* The old routes format uses "0" for default, not "-1" */ - metric = NM_MAX(0, nm_ip_route_get_metric(route)); - - g_variant_builder_add(&builder, - "(@ayu@ayu)", - nm_g_variant_new_ay_in6addr(&dest_bytes), - (guint32) nm_ip_route_get_prefix(route), - nm_g_variant_new_ay_in6addr(&next_hop_bytes), - metric); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip6_routes_from_variant: - * @value: #GVariant of type 'a(ayuayu)' - * - * Utility function to convert a #GVariant of type 'a(ayuayu)' representing an - * array of NetworkManager IPv6 routes (which are tuples of route, prefix, next - * hop, and metric) into a #GPtrArray of #NMIPRoute objects. - * - * Returns: (transfer full) (element-type NMIPRoute): a newly allocated - * #GPtrArray of #NMIPRoute objects - **/ -GPtrArray * -nm_utils_ip6_routes_from_variant(GVariant *value) -{ - GPtrArray * routes; - GVariantIter iter; - GVariant * dest_var, *next_hop_var; - const struct in6_addr *dest, *next_hop; - gsize dest_len, next_hop_len; - guint32 prefix, metric; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("a(ayuayu)")), NULL); - - routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); - - g_variant_iter_init(&iter, value); - while (g_variant_iter_next(&iter, "(@ayu@ayu)", &dest_var, &prefix, &next_hop_var, &metric)) { - NMIPRoute *route; - GError * error = NULL; - - if (!g_variant_is_of_type(dest_var, G_VARIANT_TYPE_BYTESTRING) - || !g_variant_is_of_type(next_hop_var, G_VARIANT_TYPE_BYTESTRING)) { - g_warning("%s: ignoring invalid IP6 address structure", __func__); - goto next; - } - - dest = g_variant_get_fixed_array(dest_var, &dest_len, 1); - if (dest_len != 16) { - g_warning("%s: ignoring invalid IP6 address of length %d", __func__, (int) dest_len); - goto next; - } - - next_hop = g_variant_get_fixed_array(next_hop_var, &next_hop_len, 1); - if (next_hop_len != 16) { - g_warning("%s: ignoring invalid IP6 address of length %d", - __func__, - (int) next_hop_len); - goto next; - } - - route = nm_ip_route_new_binary(AF_INET6, - dest, - prefix, - next_hop, - metric ? (gint64) metric : -1, - &error); - if (route) - g_ptr_array_add(routes, route); - else { - g_warning("Ignoring invalid IP6 route: %s", error->message); - g_clear_error(&error); - } - -next: - g_variant_unref(dest_var); - g_variant_unref(next_hop_var); - } - - return routes; -} - -/** - * nm_utils_ip_addresses_to_variant: - * @addresses: (element-type NMIPAddress): an array of #NMIPAddress objects - * - * Utility function to convert a #GPtrArray of #NMIPAddress objects representing - * IPv4 or IPv6 addresses into a #GVariant of type 'aa{sv}' representing an - * array of new-style NetworkManager IP addresses. All addresses will include - * "address" (an IP address string), and "prefix" (a uint). Some addresses may - * include additional attributes. - * - * Returns: (transfer none): a new floating #GVariant representing @addresses. - **/ -GVariant * -nm_utils_ip_addresses_to_variant(GPtrArray *addresses) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - - if (addresses) { - for (i = 0; i < addresses->len; i++) { - NMIPAddress * addr = addresses->pdata[i]; - GVariantBuilder addr_builder; - gs_free const char **names = NULL; - guint j, len; - - g_variant_builder_init(&addr_builder, G_VARIANT_TYPE("a{sv}")); - g_variant_builder_add(&addr_builder, - "{sv}", - "address", - g_variant_new_string(nm_ip_address_get_address(addr))); - g_variant_builder_add(&addr_builder, - "{sv}", - "prefix", - g_variant_new_uint32(nm_ip_address_get_prefix(addr))); - - names = _nm_ip_address_get_attribute_names(addr, TRUE, &len); - for (j = 0; j < len; j++) { - g_variant_builder_add(&addr_builder, - "{sv}", - names[j], - nm_ip_address_get_attribute(addr, names[j])); - } - - g_variant_builder_add(&builder, "a{sv}", &addr_builder); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip_addresses_from_variant: - * @value: a #GVariant of type 'aa{sv}' - * @family: an IP address family - * - * Utility function to convert a #GVariant representing a list of new-style - * NetworkManager IPv4 or IPv6 addresses (as described in the documentation for - * nm_utils_ip_addresses_to_variant()) into a #GPtrArray of #NMIPAddress - * objects. - * - * Returns: (transfer full) (element-type NMIPAddress): a newly allocated - * #GPtrArray of #NMIPAddress objects - **/ -GPtrArray * -nm_utils_ip_addresses_from_variant(GVariant *value, int family) -{ - GPtrArray * addresses; - GVariantIter iter, attrs_iter; - GVariant * addr_var; - const char * ip; - guint32 prefix; - const char * attr_name; - GVariant * attr_val; - NMIPAddress *addr; - GError * error = NULL; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), NULL); - - g_variant_iter_init(&iter, value); - addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); - - while (g_variant_iter_next(&iter, "@a{sv}", &addr_var)) { - if (!g_variant_lookup(addr_var, "address", "&s", &ip) - || !g_variant_lookup(addr_var, "prefix", "u", &prefix)) { - g_warning("Ignoring invalid address"); - g_variant_unref(addr_var); - continue; - } - - addr = nm_ip_address_new(family, ip, prefix, &error); - if (!addr) { - g_warning("Ignoring invalid address: %s", error->message); - g_clear_error(&error); - g_variant_unref(addr_var); - continue; - } - - g_variant_iter_init(&attrs_iter, addr_var); - while (g_variant_iter_next(&attrs_iter, "{&sv}", &attr_name, &attr_val)) { - if (!NM_IN_STRSET(attr_name, "address", "prefix")) - nm_ip_address_set_attribute(addr, attr_name, attr_val); - g_variant_unref(attr_val); - } - - g_variant_unref(addr_var); - g_ptr_array_add(addresses, addr); - } - - return addresses; -} - -/** - * nm_utils_ip_routes_to_variant: - * @routes: (element-type NMIPRoute): an array of #NMIPRoute objects - * - * Utility function to convert a #GPtrArray of #NMIPRoute objects representing - * IPv4 or IPv6 routes into a #GVariant of type 'aa{sv}' representing an array - * of new-style NetworkManager IP routes (which are tuples of destination, - * prefix, next hop, metric, and additional attributes). - * - * Returns: (transfer none): a new floating #GVariant representing @routes. - **/ -GVariant * -nm_utils_ip_routes_to_variant(GPtrArray *routes) -{ - GVariantBuilder builder; - guint i; - - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - - if (routes) { - for (i = 0; i < routes->len; i++) { - NMIPRoute * route = routes->pdata[i]; - GVariantBuilder route_builder; - gs_free const char **names = NULL; - guint j, len; - - g_variant_builder_init(&route_builder, G_VARIANT_TYPE("a{sv}")); - g_variant_builder_add(&route_builder, - "{sv}", - "dest", - g_variant_new_string(nm_ip_route_get_dest(route))); - g_variant_builder_add(&route_builder, - "{sv}", - "prefix", - g_variant_new_uint32(nm_ip_route_get_prefix(route))); - if (nm_ip_route_get_next_hop(route)) { - g_variant_builder_add(&route_builder, - "{sv}", - "next-hop", - g_variant_new_string(nm_ip_route_get_next_hop(route))); - } - if (nm_ip_route_get_metric(route) != -1) { - g_variant_builder_add( - &route_builder, - "{sv}", - "metric", - g_variant_new_uint32((guint32) nm_ip_route_get_metric(route))); - } - - names = _nm_ip_route_get_attribute_names(route, TRUE, &len); - for (j = 0; j < len; j++) { - g_variant_builder_add(&route_builder, - "{sv}", - names[j], - nm_ip_route_get_attribute(route, names[j])); - } - - g_variant_builder_add(&builder, "a{sv}", &route_builder); - } - } - - return g_variant_builder_end(&builder); -} - -/** - * nm_utils_ip_routes_from_variant: - * @value: a #GVariant of type 'aa{sv}' - * @family: an IP address family - * - * Utility function to convert a #GVariant representing a list of new-style - * NetworkManager IPv4 or IPv6 addresses (which are tuples of destination, - * prefix, next hop, metric, and additional attributes) into a #GPtrArray of - * #NMIPRoute objects. - * - * Returns: (transfer full) (element-type NMIPRoute): a newly allocated - * #GPtrArray of #NMIPRoute objects - **/ -GPtrArray * -nm_utils_ip_routes_from_variant(GVariant *value, int family) -{ - GPtrArray * routes; - GVariantIter iter, attrs_iter; - GVariant * route_var; - const char * dest, *next_hop; - guint32 prefix, metric32; - gint64 metric; - const char * attr_name; - GVariant * attr_val; - NMIPRoute * route; - GError * error = NULL; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), NULL); - - g_variant_iter_init(&iter, value); - routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); - - while (g_variant_iter_next(&iter, "@a{sv}", &route_var)) { - if (!g_variant_lookup(route_var, "dest", "&s", &dest) - || !g_variant_lookup(route_var, "prefix", "u", &prefix)) { - g_warning("Ignoring invalid address"); - goto next; - } - if (!g_variant_lookup(route_var, "next-hop", "&s", &next_hop)) - next_hop = NULL; - if (g_variant_lookup(route_var, "metric", "u", &metric32)) - metric = metric32; - else - metric = -1; - - route = nm_ip_route_new(family, dest, prefix, next_hop, metric, &error); - if (!route) { - g_warning("Ignoring invalid route: %s", error->message); - g_clear_error(&error); - goto next; - } - - g_variant_iter_init(&attrs_iter, route_var); - while (g_variant_iter_next(&attrs_iter, "{&sv}", &attr_name, &attr_val)) { - if (!NM_IN_STRSET(attr_name, "dest", "prefix", "next-hop", "metric")) - nm_ip_route_set_attribute(route, attr_name, attr_val); - g_variant_unref(attr_val); - } - - g_ptr_array_add(routes, route); -next: - g_variant_unref(route_var); - } - - return routes; -} - -/*****************************************************************************/ - -static void -_string_append_tc_handle(GString *string, guint32 handle) -{ - g_string_append_printf(string, "%x:", TC_H_MAJ(handle) >> 16); - if (TC_H_MIN(handle) != TC_H_UNSPEC) - g_string_append_printf(string, "%x", TC_H_MIN(handle)); -} - -/** - * _nm_utils_string_append_tc_parent: - * @string: the string to write the parent handle to - * @prefix: optional prefix for the numeric handle - * @parent: the parent handle - * - * This is used to either write out the parent handle to the tc qdisc string - * or to pretty-format (use symbolic name for root) the key in keyfile. - * The presence of prefix determines which one is the case. - * - * Private API due to general ugliness and overall uselessness for anything - * sensible. - */ -void -_nm_utils_string_append_tc_parent(GString *string, const char *prefix, guint32 parent) -{ - if (parent == TC_H_ROOT) { - g_string_append(string, "root"); - } else { - if (prefix) { - if (parent == TC_H_INGRESS) - return; - g_string_append_printf(string, "%s ", prefix); - } - _string_append_tc_handle(string, parent); - } - - if (prefix) - g_string_append_c(string, ' '); -} - -/** - * _nm_utils_parse_tc_handle: - * @str: the string representation of a qdisc handle - * @error: location of the error - * - * Parses tc style handle number into a numeric representation. - * Don't use this, use nm_utils_tc_qdisc_from_str() instead. - */ -guint32 -_nm_utils_parse_tc_handle(const char *str, GError **error) -{ - gint64 maj; - gint64 min = 0; - const char *sep; - - nm_assert(str); - - maj = nm_g_ascii_strtoll(str, (char **) &sep, 0x10); - if (sep == str) - goto fail; - - sep = nm_str_skip_leading_spaces(sep); - - if (sep[0] == ':') { - const char *str2 = &sep[1]; - - min = nm_g_ascii_strtoll(str2, (char **) &sep, 0x10); - sep = nm_str_skip_leading_spaces(sep); - if (sep[0] != '\0') - goto fail; - } else if (sep[0] != '\0') - goto fail; - - if (maj <= 0 || maj > 0xffff || min < 0 || min > 0xffff - || !NM_STRCHAR_ALL(str, ch, (g_ascii_isxdigit(ch) || ch == ':' || g_ascii_isspace(ch)))) { - goto fail; - } - - return TC_H_MAKE(((guint32) maj) << 16, (guint32) min); -fail: - nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("'%s' is not a valid handle."), str); - return TC_H_UNSPEC; -} - -static const NMVariantAttributeSpec *const tc_object_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("root", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("parent", G_VARIANT_TYPE_STRING, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("handle", G_VARIANT_TYPE_STRING, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("", - G_VARIANT_TYPE_STRING, - .no_value = TRUE, - .consumes_rest = TRUE, ), - NULL, -}; - -static const NMVariantAttributeSpec *const tc_qdisc_sfq_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("quantum", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("perturb", G_VARIANT_TYPE_INT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("limit", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("divisor", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("flows", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("depth", G_VARIANT_TYPE_UINT32, ), - NULL, -}; - -static const NMVariantAttributeSpec *const tc_qdisc_tbf_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("rate", G_VARIANT_TYPE_UINT64, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("burst", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("limit", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("latency", G_VARIANT_TYPE_UINT32, ), - NULL, -}; - -static const NMVariantAttributeSpec *const tc_qdisc_fq_codel_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("limit", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("flows", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("target", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("interval", G_VARIANT_TYPE_UINT32, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("quantum", G_VARIANT_TYPE_UINT32, ), - - /* 0x83126E97u is not a valid value (it means "disabled"). We should reject that - * value. Or alternatively, reject all values >= MAX_INT(32). */ - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("ce_threshold", G_VARIANT_TYPE_UINT32, ), - - /* kernel clamps the value at 2^31. Possibly such values should be rejected from configuration - * as they cannot be configured. Leaving the attribute unspecified causes kernel to choose - * a default (currently 32MB). */ - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("memory_limit", G_VARIANT_TYPE_UINT32, ), - - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("ecn", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NULL, -}; - -typedef struct { - const char * kind; - const NMVariantAttributeSpec *const *attrs; -} NMQdiscAttributeSpec; - -static const NMQdiscAttributeSpec *const tc_qdisc_attribute_spec[] = { - &(const NMQdiscAttributeSpec){"fq_codel", tc_qdisc_fq_codel_spec}, - &(const NMQdiscAttributeSpec){"sfq", tc_qdisc_sfq_spec}, - &(const NMQdiscAttributeSpec){"tbf", tc_qdisc_tbf_spec}, - NULL, -}; - -/*****************************************************************************/ - -/** - * _nm_utils_string_append_tc_qdisc_rest: - * @string: the string to write the formatted qdisc to - * @qdisc: the %NMTCQdisc - * - * This formats the rest of the qdisc string but the parent. Useful to format - * the keyfile value and nowhere else. - * Use nm_utils_tc_qdisc_to_str() that also includes the parent instead. - */ -void -_nm_utils_string_append_tc_qdisc_rest(GString *string, NMTCQdisc *qdisc) -{ - guint32 handle = nm_tc_qdisc_get_handle(qdisc); - const char * kind = nm_tc_qdisc_get_kind(qdisc); - gs_free char *str = NULL; - - if (handle != TC_H_UNSPEC && !NM_IN_STRSET(kind, "ingress", "clsact")) { - g_string_append(string, "handle "); - _string_append_tc_handle(string, handle); - g_string_append_c(string, ' '); - } - - g_string_append(string, kind); - - str = nm_utils_format_variant_attributes(_nm_tc_qdisc_get_attributes(qdisc), ' ', ' '); - if (str) { - g_string_append_c(string, ' '); - g_string_append(string, str); - } -} - -/** - * nm_utils_tc_qdisc_to_str: - * @qdisc: the %NMTCQdisc - * @error: location of the error - * - * Turns the %NMTCQdisc into a tc style string representation of the queueing - * discipline. - * - * Returns: formatted string or %NULL - * - * Since: 1.12 - */ -char * -nm_utils_tc_qdisc_to_str(NMTCQdisc *qdisc, GError **error) -{ - GString *string; - - string = g_string_sized_new(60); - - _nm_utils_string_append_tc_parent(string, "parent", nm_tc_qdisc_get_parent(qdisc)); - _nm_utils_string_append_tc_qdisc_rest(string, qdisc); - - return g_string_free(string, FALSE); -} - -static gboolean -_tc_read_common_opts(const char *str, - guint32 * handle, - guint32 * parent, - char ** kind, - char ** rest, - GError ** error) -{ - gs_unref_hashtable GHashTable *ht = NULL; - GVariant * variant; - - ht = nm_utils_parse_variant_attributes(str, ' ', ' ', FALSE, tc_object_attribute_spec, error); - if (!ht) - return FALSE; - - if (g_hash_table_contains(ht, "root")) - *parent = TC_H_ROOT; - - variant = g_hash_table_lookup(ht, "parent"); - if (variant) { - if (*parent != TC_H_UNSPEC) { - g_set_error(error, - 1, - 0, - _("'%s' unexpected: parent already specified."), - g_variant_get_string(variant, NULL)); - return FALSE; - } - *parent = _nm_utils_parse_tc_handle(g_variant_get_string(variant, NULL), error); - if (*parent == TC_H_UNSPEC) - return FALSE; - } - - variant = g_hash_table_lookup(ht, "handle"); - if (variant) { - *handle = _nm_utils_parse_tc_handle(g_variant_get_string(variant, NULL), error); - if (*handle == TC_H_UNSPEC) - return FALSE; - if (TC_H_MIN(*handle)) { - g_set_error(error, - 1, - 0, - _("invalid handle: '%s'"), - g_variant_get_string(variant, NULL)); - return FALSE; - } - } - - variant = g_hash_table_lookup(ht, "kind"); - if (variant) { - *kind = g_variant_dup_string(variant, NULL); - if (NM_IN_STRSET(*kind, "ingress", "clsact")) { - if (*parent == TC_H_UNSPEC) - *parent = TC_H_INGRESS; - if (*handle == TC_H_UNSPEC) - *handle = TC_H_MAKE(TC_H_INGRESS, 0); - } - } - - if (*parent == TC_H_UNSPEC) { - if (*kind) { - g_free(*kind); - *kind = NULL; - } - g_set_error_literal(error, 1, 0, _("parent not specified.")); - return FALSE; - } - - variant = g_hash_table_lookup(ht, ""); - if (variant) - *rest = g_variant_dup_string(variant, NULL); - - return TRUE; -} - -/** - * nm_utils_tc_qdisc_from_str: - * @str: the string representation of a qdisc - * @error: location of the error - * - * Parses the tc style string qdisc representation of the queueing - * discipline to a %NMTCQdisc instance. Supports a subset of the tc language. - * - * Returns: the %NMTCQdisc or %NULL - * - * Since: 1.12 - */ -NMTCQdisc * -nm_utils_tc_qdisc_from_str(const char *str, GError **error) -{ - guint32 handle = TC_H_UNSPEC; - guint32 parent = TC_H_UNSPEC; - gs_free char * kind = NULL; - gs_free char * rest = NULL; - NMTCQdisc * qdisc = NULL; - gs_unref_hashtable GHashTable *options = NULL; - GHashTableIter iter; - gpointer key, value; - guint i; - - nm_assert(str); - nm_assert(!error || !*error); - - if (!_tc_read_common_opts(str, &handle, &parent, &kind, &rest, error)) - return NULL; - - for (i = 0; rest && tc_qdisc_attribute_spec[i]; i++) { - if (nm_streq(tc_qdisc_attribute_spec[i]->kind, kind)) { - options = nm_utils_parse_variant_attributes(rest, - ' ', - ' ', - FALSE, - tc_qdisc_attribute_spec[i]->attrs, - error); - if (!options) - return NULL; - break; - } - } - nm_clear_g_free(&rest); - - if (options) { - value = g_hash_table_lookup(options, ""); - if (value) - rest = g_variant_dup_string(value, NULL); - } - - if (rest) { - g_set_error(error, 1, 0, _("unsupported qdisc option: '%s'."), rest); - return NULL; - } - - qdisc = nm_tc_qdisc_new(kind, parent, error); - if (!qdisc) - return NULL; - - nm_tc_qdisc_set_handle(qdisc, handle); - - if (options) { - g_hash_table_iter_init(&iter, options); - while (g_hash_table_iter_next(&iter, &key, &value)) - nm_tc_qdisc_set_attribute(qdisc, key, g_variant_ref_sink(value)); - } - - return qdisc; -} - -/*****************************************************************************/ - -static const NMVariantAttributeSpec *const tc_action_simple_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("sdata", G_VARIANT_TYPE_BYTESTRING, ), - NULL, -}; - -static const NMVariantAttributeSpec *const tc_action_mirred_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("egress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("ingress", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("mirror", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("redirect", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("dev", G_VARIANT_TYPE_STRING, ), - NULL, -}; - -static const NMVariantAttributeSpec *const tc_action_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("kind", G_VARIANT_TYPE_STRING, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("", - G_VARIANT_TYPE_STRING, - .no_value = TRUE, - .consumes_rest = TRUE, ), - NULL, -}; - -static gboolean -_string_append_tc_action(GString *string, NMTCAction *action, GError **error) -{ - const char * kind = nm_tc_action_get_kind(action); - gs_free char * str = NULL; - const NMVariantAttributeSpec *const *attrs; - - if (nm_streq(kind, "simple")) - attrs = tc_action_simple_attribute_spec; - else if (nm_streq(kind, "mirred")) - attrs = tc_action_mirred_attribute_spec; - else - attrs = NULL; - - g_string_append(string, kind); - - str = - _nm_utils_format_variant_attributes(_nm_tc_action_get_attributes(action), attrs, ' ', ' '); - if (str) { - g_string_append_c(string, ' '); - g_string_append(string, str); - } - - return TRUE; -} - -/** - * nm_utils_tc_action_to_str: - * @action: the %NMTCAction - * @error: location of the error - * - * Turns the %NMTCAction into a tc style string representation of the queueing - * discipline. - * - * Returns: formatted string or %NULL - * - * Since: 1.12 - */ -char * -nm_utils_tc_action_to_str(NMTCAction *action, GError **error) -{ - GString *string; - - string = g_string_sized_new(60); - if (!_string_append_tc_action(string, action, error)) { - g_string_free(string, TRUE); - return NULL; - } - - return g_string_free(string, FALSE); -} - -/** - * nm_utils_tc_action_from_str: - * @str: the string representation of a action - * @error: location of the error - * - * Parses the tc style string action representation of the queueing - * discipline to a %NMTCAction instance. Supports a subset of the tc language. - * - * Returns: the %NMTCAction or %NULL - * - * Since: 1.12 - */ -NMTCAction * -nm_utils_tc_action_from_str(const char *str, GError **error) -{ - const char * kind = NULL; - const char * rest = NULL; - NMTCAction * action = NULL; - gs_unref_hashtable GHashTable *ht = NULL; - gs_unref_hashtable GHashTable * options = NULL; - GVariant * variant; - const NMVariantAttributeSpec *const *attrs; - - nm_assert(str); - nm_assert(!error || !*error); - - ht = nm_utils_parse_variant_attributes(str, ' ', ' ', FALSE, tc_action_attribute_spec, error); - if (!ht) - return FALSE; - - variant = g_hash_table_lookup(ht, "kind"); - if (variant) { - kind = g_variant_get_string(variant, NULL); - } else { - g_set_error_literal(error, 1, 0, _("action name missing.")); - return NULL; - } - - kind = g_variant_get_string(variant, NULL); - if (nm_streq(kind, "simple")) - attrs = tc_action_simple_attribute_spec; - else if (nm_streq(kind, "mirred")) - attrs = tc_action_mirred_attribute_spec; - else - attrs = NULL; - - variant = g_hash_table_lookup(ht, ""); - if (variant) - rest = g_variant_get_string(variant, NULL); - - action = nm_tc_action_new(kind, error); - if (!action) - return NULL; - - if (rest) { - GHashTableIter iter; - gpointer key, value; - - if (!attrs) { - nm_tc_action_unref(action); - g_set_error(error, 1, 0, _("unsupported action option: '%s'."), rest); - return NULL; - } - - options = nm_utils_parse_variant_attributes(rest, ' ', ' ', FALSE, attrs, error); - if (!options) { - nm_tc_action_unref(action); - return NULL; - } - - g_hash_table_iter_init(&iter, options); - while (g_hash_table_iter_next(&iter, &key, &value)) - nm_tc_action_set_attribute(action, key, g_variant_ref_sink(value)); - } - - return action; -} - -/*****************************************************************************/ - -/** - * _nm_utils_string_append_tc_tfilter_rest: - * @string: the string to write the formatted tfilter to - * @tfilter: the %NMTCTfilter - * - * This formats the rest of the tfilter string but the parent. Useful to format - * the keyfile value and nowhere else. - * Use nm_utils_tc_tfilter_to_str() that also includes the parent instead. - */ -gboolean -_nm_utils_string_append_tc_tfilter_rest(GString *string, NMTCTfilter *tfilter, GError **error) -{ - guint32 handle = nm_tc_tfilter_get_handle(tfilter); - const char *kind = nm_tc_tfilter_get_kind(tfilter); - NMTCAction *action; - - if (handle != TC_H_UNSPEC) { - g_string_append(string, "handle "); - _string_append_tc_handle(string, handle); - g_string_append_c(string, ' '); - } - - g_string_append(string, kind); - - action = nm_tc_tfilter_get_action(tfilter); - if (action) { - g_string_append(string, " action "); - if (!_string_append_tc_action(string, action, error)) - return FALSE; - } - - return TRUE; -} - -/** - * nm_utils_tc_tfilter_to_str: - * @tfilter: the %NMTCTfilter - * @error: location of the error - * - * Turns the %NMTCTfilter into a tc style string representation of the queueing - * discipline. - * - * Returns: formatted string or %NULL - * - * Since: 1.12 - */ -char * -nm_utils_tc_tfilter_to_str(NMTCTfilter *tfilter, GError **error) -{ - GString *string; - - string = g_string_sized_new(60); - - _nm_utils_string_append_tc_parent(string, "parent", nm_tc_tfilter_get_parent(tfilter)); - if (!_nm_utils_string_append_tc_tfilter_rest(string, tfilter, error)) { - g_string_free(string, TRUE); - return NULL; - } - - return g_string_free(string, FALSE); -} - -static const NMVariantAttributeSpec *const tc_tfilter_attribute_spec[] = { - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("action", G_VARIANT_TYPE_BOOLEAN, .no_value = TRUE, ), - NM_VARIANT_ATTRIBUTE_SPEC_DEFINE("", - G_VARIANT_TYPE_STRING, - .no_value = TRUE, - .consumes_rest = TRUE, ), - NULL, -}; - -/** - * nm_utils_tc_tfilter_from_str: - * @str: the string representation of a tfilter - * @error: location of the error - * - * Parses the tc style string tfilter representation of the queueing - * discipline to a %NMTCTfilter instance. Supports a subset of the tc language. - * - * Returns: the %NMTCTfilter or %NULL - * - * Since: 1.12 - */ -NMTCTfilter * -nm_utils_tc_tfilter_from_str(const char *str, GError **error) -{ - guint32 handle = TC_H_UNSPEC; - guint32 parent = TC_H_UNSPEC; - gs_free char * kind = NULL; - gs_free char * rest = NULL; - nm_auto_unref_tc_action NMTCAction *action = NULL; - const char * extra_opts = NULL; - NMTCTfilter * tfilter = NULL; - gs_unref_hashtable GHashTable *ht = NULL; - GVariant * variant; - - nm_assert(str); - nm_assert(!error || !*error); - - if (!_tc_read_common_opts(str, &handle, &parent, &kind, &rest, error)) - return NULL; - - if (rest) { - ht = nm_utils_parse_variant_attributes(rest, - ' ', - ' ', - FALSE, - tc_tfilter_attribute_spec, - error); - if (!ht) - return NULL; - - variant = g_hash_table_lookup(ht, ""); - if (variant) - extra_opts = g_variant_get_string(variant, NULL); - - if (g_hash_table_contains(ht, "action")) { - action = nm_utils_tc_action_from_str(extra_opts, error); - if (!action) { - g_prefix_error(error, _("invalid action: ")); - return NULL; - } - } else { - g_set_error(error, 1, 0, _("unsupported tfilter option: '%s'."), rest); - return NULL; - } - } - - tfilter = nm_tc_tfilter_new(kind, parent, error); - if (!tfilter) - return NULL; - - nm_tc_tfilter_set_handle(tfilter, handle); - if (action) - nm_tc_tfilter_set_action(tfilter, action); - - return tfilter; -} - -/*****************************************************************************/ - -extern const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[]; - -/** - * nm_utils_sriov_vf_to_str: - * @vf: the %NMSriovVF - * @omit_index: if %TRUE, the VF index will be omitted from output string - * @error: (out) (allow-none): location to store the error on failure - * - * Converts a SR-IOV virtual function object to its string representation. - * - * Returns: a newly allocated string or %NULL on error - * - * Since: 1.14 - */ -char * -nm_utils_sriov_vf_to_str(const NMSriovVF *vf, gboolean omit_index, GError **error) -{ - gs_free NMUtilsNamedValue *values = NULL; - gs_free const char ** names = NULL; - const guint * vlan_ids; - guint num_vlans, num_attrs; - guint i; - GString * str; - - str = g_string_new(""); - if (!omit_index) - g_string_append_printf(str, "%u", nm_sriov_vf_get_index(vf)); - - names = nm_sriov_vf_get_attribute_names(vf); - num_attrs = names ? g_strv_length((char **) names) : 0; - values = g_new0(NMUtilsNamedValue, num_attrs); - - for (i = 0; i < num_attrs; i++) { - values[i].name = names[i]; - values[i].value_ptr = nm_sriov_vf_get_attribute(vf, names[i]); - } - - if (num_attrs > 0) { - if (!omit_index) - g_string_append_c(str, ' '); - _nm_utils_format_variant_attributes_full(str, values, num_attrs, NULL, ' ', '='); - } - - vlan_ids = nm_sriov_vf_get_vlan_ids(vf, &num_vlans); - if (num_vlans != 0) { - g_string_append(str, " vlans"); - for (i = 0; i < num_vlans; i++) { - guint32 qos; - NMSriovVFVlanProtocol protocol; - - qos = nm_sriov_vf_get_vlan_qos(vf, vlan_ids[i]); - protocol = nm_sriov_vf_get_vlan_protocol(vf, vlan_ids[i]); - - g_string_append_c(str, i == 0 ? '=' : ';'); - - g_string_append_printf(str, "%u", vlan_ids[i]); - - if (qos != 0 || protocol != NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q) { - g_string_append_printf(str, - ".%u%s", - (unsigned) qos, - protocol == NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q ? "" : ".ad"); - } - } - } - - return g_string_free(str, FALSE); -} - -gboolean -_nm_sriov_vf_parse_vlans(NMSriovVF *vf, const char *str, GError **error) -{ - gs_free const char **vlans = NULL; - guint i; - - vlans = nm_utils_strsplit_set(str, ";"); - if (!vlans) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "empty VF VLAN"); - return FALSE; - } - - for (i = 0; vlans[i]; i++) { - gs_strfreev char **params = NULL; - guint id = G_MAXUINT; - gint64 qos = -1; - - /* we accept leading/trailing whitespace around vlans[1]. Hence - * the nm_str_skip_leading_spaces() and g_strchomp() below. - * - * However, we don't accept any whitespace inside the specifier. - * Hence the NM_STRCHAR_ALL() checks. */ - - params = g_strsplit(nm_str_skip_leading_spaces(vlans[i]), ".", 3); - if (!params || !params[0] || *params[0] == '\0') { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "empty VF VLAN"); - return FALSE; - } - - if (!params[1]) - g_strchomp(params[0]); - if (NM_STRCHAR_ALL(params[0], ch, ch == 'x' || g_ascii_isdigit(ch))) - id = _nm_utils_ascii_str_to_int64(params[0], 0, 0, 4095, G_MAXUINT); - if (id == G_MAXUINT) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "invalid VF VLAN id '%s'", - params[0]); - return FALSE; - } - if (!nm_sriov_vf_add_vlan(vf, id)) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "duplicate VLAN id %u", - id); - return FALSE; - } - - if (!params[1]) - continue; - - if (!params[2]) - g_strchomp(params[1]); - if (NM_STRCHAR_ALL(params[1], ch, ch == 'x' || g_ascii_isdigit(ch))) - qos = _nm_utils_ascii_str_to_int64(params[1], 0, 0, G_MAXUINT32, -1); - if (qos == -1) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "invalid VF VLAN QoS '%s'", - params[1]); - return FALSE; - } - nm_sriov_vf_set_vlan_qos(vf, id, qos); - - if (!params[2]) - continue; - - g_strchomp(params[2]); - - if (nm_streq(params[2], "ad")) - nm_sriov_vf_set_vlan_protocol(vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); - else if (nm_streq(params[2], "q")) - nm_sriov_vf_set_vlan_protocol(vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); - else { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "invalid VF VLAN protocol '%s'", - params[2]); - return FALSE; - } - } - - return TRUE; -} - -/** - * nm_utils_sriov_vf_from_str: - * @str: the input string - * @error: (out) (allow-none): location to store the error on failure - * - * Converts a string to a SR-IOV virtual function object. - * - * Returns: (transfer full): the virtual function object - * - * Since: 1.14 - */ -NMSriovVF * -nm_utils_sriov_vf_from_str(const char *str, GError **error) -{ - gs_free char *index_free = NULL; - const char * detail; - - g_return_val_if_fail(str, NULL); - g_return_val_if_fail(!error || !*error, NULL); - - while (*str == ' ') - str++; - - detail = strchr(str, ' '); - if (detail) { - str = nm_strndup_a(200, str, detail - str, &index_free); - detail++; - } - - return _nm_utils_sriov_vf_from_strparts(str, detail, FALSE, error); -} - -NMSriovVF * -_nm_utils_sriov_vf_from_strparts(const char *index, - const char *detail, - gboolean ignore_unknown, - GError ** error) -{ - NMSriovVF * vf; - guint32 n_index; - GHashTableIter iter; - char * key; - GVariant * variant; - gs_unref_hashtable GHashTable *ht = NULL; - - n_index = _nm_utils_ascii_str_to_int64(index, 10, 0, G_MAXUINT32, 0); - if (errno) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - "invalid index"); - return NULL; - } - - vf = nm_sriov_vf_new(n_index); - if (detail) { - ht = nm_utils_parse_variant_attributes(detail, - ' ', - '=', - ignore_unknown, - _nm_sriov_vf_attribute_spec, - error); - if (!ht) { - nm_sriov_vf_unref(vf); - return NULL; - } - - if ((variant = g_hash_table_lookup(ht, "vlans"))) { - if (!_nm_sriov_vf_parse_vlans(vf, g_variant_get_string(variant, NULL), error)) { - nm_sriov_vf_unref(vf); - return NULL; - } - g_hash_table_remove(ht, "vlans"); - } - - g_hash_table_iter_init(&iter, ht); - while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &variant)) - nm_sriov_vf_set_attribute(vf, key, g_variant_ref_sink(variant)); - } - - return vf; -} - -/*****************************************************************************/ - -NMUuid * -_nm_utils_uuid_parse(const char *str, NMUuid *out_uuid) -{ - nm_assert(str); - nm_assert(out_uuid); - - if (uuid_parse(str, out_uuid->uuid) != 0) - return NULL; - return out_uuid; -} - -char * -_nm_utils_uuid_unparse(const NMUuid *uuid, char *out_str /*[37]*/) -{ - nm_assert(uuid); - - if (!out_str) { - /* for convenience, allow %NULL to indicate that a new - * string should be allocated. */ - out_str = g_malloc(37); - } - uuid_unparse_lower(uuid->uuid, out_str); - return out_str; -} - -NMUuid * -_nm_utils_uuid_generate_random(NMUuid *out_uuid) -{ - nm_assert(out_uuid); - - uuid_generate_random(out_uuid->uuid); - return out_uuid; -} - -gboolean -nm_utils_uuid_is_null(const NMUuid *uuid) -{ - int i; - - if (!uuid) - return TRUE; - - for (i = 0; i < (int) G_N_ELEMENTS(uuid->uuid); i++) { - if (uuid->uuid[i]) - return FALSE; - } - return TRUE; -} - -/** - * nm_utils_uuid_generate_buf_: - * @buf: input buffer, must contain at least 37 bytes - * - * Returns: generates a new random UUID, writes it to @buf and returns @buf. - **/ -char * -nm_utils_uuid_generate_buf_(char *buf) -{ - NMUuid uuid; - - nm_assert(buf); - - _nm_utils_uuid_generate_random(&uuid); - return _nm_utils_uuid_unparse(&uuid, buf); -} - -/** - * nm_utils_uuid_generate: - * - * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection - * object's #NMSettingConnection:id: property. Should be freed with g_free() - **/ -char * -nm_utils_uuid_generate(void) -{ - return nm_utils_uuid_generate_buf_(g_malloc(37)); -} - -/** - * nm_utils_uuid_generate_from_string_bin: - * @uuid: the UUID to update inplace. This function cannot - * fail to succeed. - * @s: a string to use as the seed for the UUID - * @slen: if negative, treat @s as zero terminated C string. - * Otherwise, assume the length as given (and allow @s to be - * non-null terminated or contain '\0'). - * @uuid_type: a type identifier which UUID format to generate. - * @type_args: additional arguments, depending on the uuid_type - * - * For a given @s, this function will always return the same UUID. - * - * Returns: the input @uuid. This function cannot fail. - **/ -NMUuid * -nm_utils_uuid_generate_from_string_bin(NMUuid * uuid, - const char *s, - gssize slen, - int uuid_type, - gpointer type_args) -{ - g_return_val_if_fail(uuid, FALSE); - g_return_val_if_fail(slen == 0 || s, FALSE); - - if (slen < 0) - slen = s ? strlen(s) : 0; - - switch (uuid_type) { - case NM_UTILS_UUID_TYPE_LEGACY: - g_return_val_if_fail(!type_args, NULL); - nm_crypto_md5_hash(NULL, 0, (guint8 *) s, slen, (guint8 *) uuid, sizeof(*uuid)); - break; - case NM_UTILS_UUID_TYPE_VERSION3: - case NM_UTILS_UUID_TYPE_VERSION5: - { - NMUuid ns_uuid = {}; - - if (type_args) { - /* type_args can be a name space UUID. Interpret it as (char *) */ - if (!_nm_utils_uuid_parse(type_args, &ns_uuid)) - g_return_val_if_reached(NULL); - } - - if (uuid_type == NM_UTILS_UUID_TYPE_VERSION3) { - nm_crypto_md5_hash((guint8 *) s, - slen, - (guint8 *) &ns_uuid, - sizeof(ns_uuid), - (guint8 *) uuid, - sizeof(*uuid)); - } else { - nm_auto_free_checksum GChecksum *sum = NULL; - union { - guint8 sha1[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - NMUuid uuid; - } digest; - - sum = g_checksum_new(G_CHECKSUM_SHA1); - g_checksum_update(sum, (guchar *) &ns_uuid, sizeof(ns_uuid)); - g_checksum_update(sum, (guchar *) s, slen); - nm_utils_checksum_get_digest(sum, digest.sha1); - - G_STATIC_ASSERT_EXPR(sizeof(digest.sha1) > sizeof(digest.uuid)); - *uuid = digest.uuid; - } - - uuid->uuid[6] = (uuid->uuid[6] & 0x0F) | (uuid_type << 4); - uuid->uuid[8] = (uuid->uuid[8] & 0x3F) | 0x80; - break; - } - default: - g_return_val_if_reached(NULL); - } - - return uuid; -} - -/** - * nm_utils_uuid_generate_from_string: - * @s: a string to use as the seed for the UUID - * @slen: if negative, treat @s as zero terminated C string. - * Otherwise, assume the length as given (and allow @s to be - * non-null terminated or contain '\0'). - * @uuid_type: a type identifier which UUID format to generate. - * @type_args: additional arguments, depending on the uuid_type - * - * For a given @s, this function will always return the same UUID. - * - * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection - * object's #NMSettingConnection:id: property - **/ -char * -nm_utils_uuid_generate_from_string(const char *s, gssize slen, int uuid_type, gpointer type_args) -{ - NMUuid uuid; - - nm_utils_uuid_generate_from_string_bin(&uuid, s, slen, uuid_type, type_args); - return _nm_utils_uuid_unparse(&uuid, NULL); -} - -/** - * _nm_utils_uuid_generate_from_strings: - * @string1: a variadic list of strings. Must be NULL terminated. - * - * Returns a variant3 UUID based on the concatenated C strings. - * It does not simply concatenate them, but also includes the - * terminating '\0' character. For example "a", "b", gives - * "a\0b\0". - * - * This has the advantage, that the following invocations - * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""), - * ("aa"), ("aa", ""), ("", "aa"), ... - */ -char * -_nm_utils_uuid_generate_from_strings(const char *string1, ...) -{ - if (!string1) - return nm_utils_uuid_generate_from_string(NULL, - 0, - NM_UTILS_UUID_TYPE_VERSION3, - NM_UTILS_UUID_NS); - - { - nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); - va_list args; - const char * s; - - nm_str_buf_append_len(&str, string1, strlen(string1) + 1u); - - va_start(args, string1); - s = va_arg(args, const char *); - while (s) { - nm_str_buf_append_len(&str, s, strlen(s) + 1u); - s = va_arg(args, const char *); - } - va_end(args); - - return nm_utils_uuid_generate_from_string(nm_str_buf_get_str_unsafe(&str), - str.len, - NM_UTILS_UUID_TYPE_VERSION3, - NM_UTILS_UUID_NS); - } -} - -/*****************************************************************************/ - -static gboolean -file_has_extension(const char *filename, const char *extensions[]) -{ - const char *ext; - gsize i; - - ext = strrchr(filename, '.'); - if (!ext) - return FALSE; - - for (i = 0; extensions[i]; i++) { - if (!g_ascii_strcasecmp(ext, extensions[i])) - return TRUE; - } - - return FALSE; -} - -/** - * nm_utils_file_is_certificate: - * @filename: name of the file to test - * - * Tests if @filename has a valid extension for an X.509 certificate file - * (".cer", ".crt", ".der", or ".pem"), and contains a certificate in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a certificate, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_certificate(const char *filename) -{ - const char * extensions[] = {".der", ".pem", ".crt", ".cer", NULL}; - NMCryptoFileFormat file_format; - - g_return_val_if_fail(filename != NULL, FALSE); - - if (!file_has_extension(filename, extensions)) - return FALSE; - - if (!nm_crypto_load_and_verify_certificate(filename, &file_format, NULL, NULL)) - return FALSE; - return file_format = NM_CRYPTO_FILE_FORMAT_X509; -} - -/** - * nm_utils_file_is_private_key: - * @filename: name of the file to test - * @out_encrypted: (out): on return, whether the file is encrypted - * - * Tests if @filename has a valid extension for an X.509 private key file - * (".der", ".key", ".pem", or ".p12"), and contains a private key in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a private key, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted) -{ - const char *extensions[] = {".der", ".pem", ".p12", ".key", NULL}; - - g_return_val_if_fail(filename != NULL, FALSE); - - NM_SET_OUT(out_encrypted, FALSE); - if (!file_has_extension(filename, extensions)) - return FALSE; - - return nm_crypto_verify_private_key(filename, NULL, out_encrypted, NULL) - != NM_CRYPTO_FILE_FORMAT_UNKNOWN; -} - -/** - * nm_utils_file_is_pkcs12: - * @filename: name of the file to test - * - * Tests if @filename is a PKCS#<!-- -->12 file. - * - * Returns: %TRUE if the file is PKCS#<!-- -->12, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_pkcs12(const char *filename) -{ - g_return_val_if_fail(filename != NULL, FALSE); - - return nm_crypto_is_pkcs12_file(filename, NULL); -} - -/*****************************************************************************/ - -gboolean -_nm_utils_check_file(const char * filename, - gint64 check_owner, - NMUtilsCheckFilePredicate check_file, - gpointer user_data, - struct stat * out_st, - GError ** error) -{ - struct stat st_backup; - - if (!out_st) - out_st = &st_backup; - - if (stat(filename, out_st) != 0) { - int errsv = errno; - - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("failed stat file %s: %s"), - filename, - nm_strerror_native(errsv)); - return FALSE; - } - - /* ignore non-files. */ - if (!S_ISREG(out_st->st_mode)) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("not a file (%s)"), - filename); - return FALSE; - } - - /* with check_owner enabled, check that the file belongs to the - * owner or root. */ - if (check_owner >= 0 && (out_st->st_uid != 0 && (gint64) out_st->st_uid != check_owner)) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("invalid file owner %d for %s"), - out_st->st_uid, - filename); - return FALSE; - } - - /* with check_owner enabled, check that the file cannot be modified - * by other users (except root). */ - if (check_owner >= 0 && NM_FLAGS_ANY(out_st->st_mode, S_IWGRP | S_IWOTH | S_ISUID)) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("file permissions for %s"), - filename); - return FALSE; - } - - if (check_file && !check_file(filename, out_st, user_data, error)) { - if (error && !*error) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("reject %s"), - filename); - } - return FALSE; - } - - return TRUE; -} - -gboolean -_nm_utils_check_module_file(const char * name, - int check_owner, - NMUtilsCheckFilePredicate check_file, - gpointer user_data, - GError ** error) -{ - if (!g_path_is_absolute(name)) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("path is not absolute (%s)"), - name); - return FALSE; - } - - /* Set special error code if the file doesn't exist. - * The VPN package might be split into separate packages, - * so it could be correct that the plugin file is missing. - * - * Note that nm-applet checks for this error code to fail - * gracefully. */ - if (!g_file_test(name, G_FILE_TEST_EXISTS)) { - g_set_error(error, - G_FILE_ERROR, - G_FILE_ERROR_NOENT, - _("Plugin file does not exist (%s)"), - name); - return FALSE; - } - - if (!g_file_test(name, G_FILE_TEST_IS_REGULAR)) { - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("Plugin is not a valid file (%s)"), - name); - return FALSE; - } - - if (g_str_has_suffix(name, ".la")) { - /* g_module_open() treats files that end with .la special. - * We don't want to parse the libtool archive. Just error out. */ - g_set_error(error, - NM_VPN_PLUGIN_ERROR, - NM_VPN_PLUGIN_ERROR_FAILED, - _("libtool archives are not supported (%s)"), - name); - return FALSE; - } - - return _nm_utils_check_file(name, check_owner, check_file, user_data, NULL, error); -} - -/*****************************************************************************/ - -/** - * nm_utils_file_search_in_paths: - * @progname: the helper program name, like "iptables" - * Must be a non-empty string, without path separator (/). - * @try_first: (allow-none): a custom path to try first before searching. - * It is silently ignored if it is empty or not an absolute path. - * @paths: (allow-none): a %NULL terminated list of search paths. - * Can be empty or %NULL, in which case only @try_first is checked. - * @file_test_flags: the flags passed to g_file_test() when searching - * for @progname. Set it to 0 to skip the g_file_test(). - * @predicate: (scope call): if given, pass the file name to this function - * for additional checks. This check is performed after the check for - * @file_test_flags. You cannot omit both @file_test_flags and @predicate. - * @user_data: (closure) (allow-none): user data for @predicate function. - * @error: (allow-none): on failure, set a "not found" error %G_IO_ERROR %G_IO_ERROR_NOT_FOUND. - * - * Searches for a @progname file in a list of search @paths. - * - * Returns: (transfer none): the full path to the helper, if found, or %NULL if not found. - * The returned string is not owned by the caller, but later - * invocations of the function might overwrite it. - */ -const char * -nm_utils_file_search_in_paths(const char * progname, - const char * try_first, - const char *const * paths, - GFileTest file_test_flags, - NMUtilsFileSearchInPathsPredicate predicate, - gpointer user_data, - GError ** error) -{ - g_return_val_if_fail(!error || !*error, NULL); - g_return_val_if_fail(progname && progname[0] && !strchr(progname, '/'), NULL); - g_return_val_if_fail(file_test_flags || predicate, NULL); - - /* Only consider @try_first if it is a valid, absolute path. This makes - * it simpler to pass in a path from configure checks. */ - if (try_first && try_first[0] == '/' - && (file_test_flags == 0 || g_file_test(try_first, file_test_flags)) - && (!predicate || predicate(try_first, user_data))) - return g_intern_string(try_first); - - if (paths && paths[0]) { - nm_auto_str_buf NMStrBuf strbuf = - NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE); - - for (; *paths; paths++) { - const char *path = *paths; - const char *s; - - if (!path[0]) - continue; - - nm_str_buf_reset(&strbuf); - nm_str_buf_append(&strbuf, path); - nm_str_buf_ensure_trailing_c(&strbuf, '/'); - s = nm_str_buf_append0(&strbuf, progname); - - if ((file_test_flags == 0 || g_file_test(s, file_test_flags)) - && (!predicate || predicate(s, user_data))) - return g_intern_string(s); - } - } - - g_set_error(error, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - _("Could not find \"%s\" binary"), - progname); - return NULL; -} - -/*****************************************************************************/ - -/* Band, channel/frequency stuff for wireless */ -struct cf_pair { - guint32 chan; - guint32 freq; -}; - -static const struct cf_pair a_table[] = { - /* A band */ - {7, 5035}, {8, 5040}, {9, 5045}, {11, 5055}, {12, 5060}, {16, 5080}, {34, 5170}, - {36, 5180}, {38, 5190}, {40, 5200}, {42, 5210}, {44, 5220}, {46, 5230}, {48, 5240}, - {50, 5250}, {52, 5260}, {56, 5280}, {58, 5290}, {60, 5300}, {64, 5320}, {100, 5500}, - {104, 5520}, {108, 5540}, {112, 5560}, {116, 5580}, {120, 5600}, {124, 5620}, {128, 5640}, - {132, 5660}, {136, 5680}, {140, 5700}, {149, 5745}, {152, 5760}, {153, 5765}, {157, 5785}, - {160, 5800}, {161, 5805}, {165, 5825}, {183, 4915}, {184, 4920}, {185, 4925}, {187, 4935}, - {188, 4945}, {192, 4960}, {196, 4980}, {0, 0}}; - -static const guint a_table_freqs[G_N_ELEMENTS(a_table)] = { - /* A band */ - 5035, 5040, 5045, 5055, 5060, 5080, 5170, 5180, 5190, 5200, 5210, 5220, 5230, 5240, 5250, 5260, - 5280, 5290, 5300, 5320, 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5745, - 5760, 5765, 5785, 5800, 5805, 5825, 4915, 4920, 4925, 4935, 4945, 4960, 4980, 0, -}; - -static const struct cf_pair bg_table[] = { - /* B/G band */ - {1, 2412}, - {2, 2417}, - {3, 2422}, - {4, 2427}, - {5, 2432}, - {6, 2437}, - {7, 2442}, - {8, 2447}, - {9, 2452}, - {10, 2457}, - {11, 2462}, - {12, 2467}, - {13, 2472}, - {14, 2484}, - {0, 0}}; - -static const guint bg_table_freqs[G_N_ELEMENTS(bg_table)] = { - /* B/G band */ - 2412, - 2417, - 2422, - 2427, - 2432, - 2437, - 2442, - 2447, - 2452, - 2457, - 2462, - 2467, - 2472, - 2484, - 0, -}; - -/** - * nm_utils_wifi_freq_to_channel: - * @freq: frequency - * - * Utility function to translate a Wi-Fi frequency to its corresponding channel. - * - * Returns: the channel represented by the frequency or 0 - **/ -guint32 -nm_utils_wifi_freq_to_channel(guint32 freq) -{ - int i = 0; - - if (freq > 4900) { - while (a_table[i].freq && (a_table[i].freq != freq)) - i++; - return a_table[i].chan; - } - - while (bg_table[i].freq && (bg_table[i].freq != freq)) - i++; - return bg_table[i].chan; -} - -/** - * nm_utils_wifi_freq_to_band: - * @freq: frequency - * - * Utility function to translate a Wi-Fi frequency to its corresponding band. - * - * Returns: the band containing the frequency or NULL if freq is invalid - **/ -const char * -nm_utils_wifi_freq_to_band(guint32 freq) -{ - if (freq >= 4915 && freq <= 5825) - return "a"; - else if (freq >= 2412 && freq <= 2484) - return "bg"; - - return NULL; -} - -/** - * nm_utils_wifi_channel_to_freq: - * @channel: channel - * @band: frequency band for wireless ("a" or "bg") - * - * Utility function to translate a Wi-Fi channel to its corresponding frequency. - * - * Returns: the frequency represented by the channel of the band, - * or -1 when the freq is invalid, or 0 when the band - * is invalid - **/ -guint32 -nm_utils_wifi_channel_to_freq(guint32 channel, const char *band) -{ - int i; - - g_return_val_if_fail(band, 0); - - if (nm_streq(band, "a")) { - for (i = 0; a_table[i].chan; i++) { - if (a_table[i].chan == channel) - return a_table[i].freq; - } - return ((guint32) -1); - } - - if (nm_streq(band, "bg")) { - for (i = 0; bg_table[i].chan; i++) { - if (bg_table[i].chan == channel) - return bg_table[i].freq; - } - return ((guint32) -1); - } - - return 0; -} - -/** - * nm_utils_wifi_find_next_channel: - * @channel: current channel - * @direction: whether going downward (0 or less) or upward (1 or more) - * @band: frequency band for wireless ("a" or "bg") - * - * Utility function to find out next/previous Wi-Fi channel for a channel. - * - * Returns: the next channel in the specified direction or 0 - **/ -guint32 -nm_utils_wifi_find_next_channel(guint32 channel, int direction, char *band) -{ - size_t a_size = G_N_ELEMENTS(a_table); - size_t bg_size = G_N_ELEMENTS(bg_table); - const struct cf_pair *pair; - - if (nm_streq(band, "a")) { - if (channel < a_table[0].chan) - return a_table[0].chan; - if (channel > a_table[a_size - 2].chan) - return a_table[a_size - 2].chan; - pair = &a_table[0]; - } else if (nm_streq(band, "bg")) { - if (channel < bg_table[0].chan) - return bg_table[0].chan; - if (channel > bg_table[bg_size - 2].chan) - return bg_table[bg_size - 2].chan; - pair = &bg_table[0]; - } else - g_return_val_if_reached(0); - - while (pair->chan) { - if (channel == pair->chan) - return channel; - if ((channel < (pair + 1)->chan) && (channel > pair->chan)) { - if (direction > 0) - return (pair + 1)->chan; - else - return pair->chan; - } - pair++; - } - return 0; -} - -/** - * nm_utils_wifi_is_channel_valid: - * @channel: channel - * @band: frequency band for wireless ("a" or "bg") - * - * Utility function to verify Wi-Fi channel validity. - * - * Returns: %TRUE or %FALSE - **/ -gboolean -nm_utils_wifi_is_channel_valid(guint32 channel, const char *band) -{ - guint32 freq; - - freq = nm_utils_wifi_channel_to_freq(channel, band); - - return !NM_IN_SET(freq, 0u, (guint32) -1); -} - -#define _nm_assert_wifi_freqs(table, table_freqs) \ - G_STMT_START \ - { \ - if (NM_MORE_ASSERT_ONCE(5)) { \ - int i, j; \ - \ - G_STATIC_ASSERT(G_N_ELEMENTS(table) > 0); \ - G_STATIC_ASSERT(G_N_ELEMENTS(table) == G_N_ELEMENTS(table_freqs)); \ - \ - for (i = 0; i < (int) G_N_ELEMENTS(table); i++) { \ - nm_assert((i == G_N_ELEMENTS(table) - 1) == (table[i].chan == 0)); \ - nm_assert((i == G_N_ELEMENTS(table) - 1) == (table[i].freq == 0)); \ - nm_assert(table[i].freq == table_freqs[i]); \ - for (j = 0; j < i; j++) { \ - nm_assert(table[j].chan != table[i].chan); \ - nm_assert(table[j].freq != table[i].freq); \ - } \ - } \ - } \ - } \ - G_STMT_END - -/** - * nm_utils_wifi_2ghz_freqs: - * - * Utility function to return 2.4 GHz Wi-Fi frequencies (802.11bg band). - * - * Returns: zero-terminated array of frequencies numbers (in MHz) - * - * Since: 1.2 - **/ -const guint * -nm_utils_wifi_2ghz_freqs(void) -{ - _nm_assert_wifi_freqs(bg_table, bg_table_freqs); - return bg_table_freqs; -} - -/** - * nm_utils_wifi_5ghz_freqs: - * - * Utility function to return 5 GHz Wi-Fi frequencies (802.11a band). - * - * Returns: zero-terminated array of frequencies numbers (in MHz) - * - * Since: 1.2 - **/ -const guint * -nm_utils_wifi_5ghz_freqs(void) -{ - _nm_assert_wifi_freqs(a_table, a_table_freqs); - return a_table_freqs; -} - -/** - * nm_utils_wifi_strength_bars: - * @strength: the access point strength, from 0 to 100 - * - * Converts @strength into a 4-character-wide graphical representation of - * strength suitable for printing to stdout. - * - * Previous versions used to take a guess at the terminal type and possibly - * return a wide UTF-8 encoded string. Now it always returns a 7-bit - * clean strings of one to 0 to 4 asterisks. Users that actually need - * the functionality are encouraged to make their implementations instead. - * - * Returns: the graphical representation of the access point strength - */ -const char * -nm_utils_wifi_strength_bars(guint8 strength) -{ - if (strength > 80) - return "****"; - else if (strength > 55) - return "*** "; - else if (strength > 30) - return "** "; - else if (strength > 5) - return "* "; - else - return " "; -} - -/** - * nm_utils_hwaddr_len: - * @type: the type of address; either <literal>ARPHRD_ETHER</literal> or - * <literal>ARPHRD_INFINIBAND</literal> - * - * Returns the length in octets of a hardware address of type @type. - * - * Before 1.28, it was an error to call this function with any value other than - * <literal>ARPHRD_ETHER</literal> or <literal>ARPHRD_INFINIBAND</literal>. - * - * Return value: the length or zero if the type is unrecognized. - */ -gsize -nm_utils_hwaddr_len(int type) -{ - switch (type) { - case ARPHRD_ETHER: - return ETH_ALEN; - case ARPHRD_INFINIBAND: - return INFINIBAND_ALEN; - default: - return 0; - } -} - -/** - * nm_utils_hexstr2bin: - * @hex: a string of hexadecimal characters with optional ':' separators - * - * Converts a hexadecimal string @hex into an array of bytes. The optional - * separator ':' may be used between single or pairs of hexadecimal characters, - * eg "00:11" or "0:1". Any "0x" at the beginning of @hex is ignored. @hex - * may not start or end with ':'. - * - * Return value: (transfer full): the converted bytes, or %NULL on error - */ -GBytes * -nm_utils_hexstr2bin(const char *hex) -{ - guint8 *buffer; - gsize len; - - buffer = nm_utils_hexstr2bin_alloc(hex, TRUE, FALSE, ":", 0, &len); - if (!buffer) - return NULL; - buffer = g_realloc(buffer, len); - return g_bytes_new_take(buffer, len); -} - -/** - * nm_utils_hwaddr_atoba: - * @asc: the ASCII representation of a hardware address - * @length: the expected length in bytes of the result - * - * Parses @asc and converts it to binary form in a #GByteArray. See - * nm_utils_hwaddr_aton() if you don't want a #GByteArray. - * - * Return value: (transfer full): a new #GByteArray, or %NULL if @asc couldn't - * be parsed - */ -GByteArray * -nm_utils_hwaddr_atoba(const char *asc, gsize length) -{ - GByteArray *ba; - - g_return_val_if_fail(asc, NULL); - g_return_val_if_fail(length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, NULL); - - ba = g_byte_array_sized_new(length); - g_byte_array_set_size(ba, length); - if (!_nm_utils_hwaddr_aton_exact(asc, ba->data, length)) - goto fail; - - return ba; -fail: - g_byte_array_unref(ba); - return NULL; -} - -/** - * nm_utils_hwaddr_aton: - * @asc: the ASCII representation of a hardware address - * @buffer: (type guint8) (array length=length): buffer to store the result into - * @length: the expected length in bytes of the result and - * the size of the buffer in bytes. - * - * Parses @asc and converts it to binary form in @buffer. - * Bytes in @asc can be separated by colons (:), or hyphens (-), but not mixed. - * - * Return value: @buffer, or %NULL if @asc couldn't be parsed - * or would be shorter or longer than @length. - */ -guint8 * -nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize length) -{ - g_return_val_if_fail(asc, NULL); - g_return_val_if_fail(buffer, NULL); - g_return_val_if_fail(length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, NULL); - - return _nm_utils_hwaddr_aton_exact(asc, buffer, length); -} - -/** - * nm_utils_bin2hexstr: - * @src: (type guint8) (array length=len): an array of bytes - * @len: the length of the @src array - * @final_len: an index where to cut off the returned string, or -1 - * - * Converts the byte array @src into a hexadecimal string. If @final_len is - * greater than -1, the returned string is terminated at that index - * (returned_string[final_len] == '\0'), - * - * Return value: (transfer full): the textual form of @bytes - */ -char * -nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len) -{ - char *result; - gsize buflen = (len * 2) + 1; - - g_return_val_if_fail(src != NULL, NULL); - g_return_val_if_fail(len > 0 && (buflen - 1) / 2 == len, NULL); - g_return_val_if_fail(final_len < 0 || (gsize) final_len < buflen, NULL); - - result = g_malloc(buflen); - - nm_utils_bin2hexstr_full(src, len, '\0', FALSE, result); - - /* Cut converted key off at the correct length for this cipher type */ - if (final_len >= 0 && (gsize) final_len < buflen) - result[final_len] = '\0'; - - return result; -} - -/** - * nm_utils_hwaddr_ntoa: - * @addr: (type guint8) (array length=length): a binary hardware address - * @length: the length of @addr - * - * Converts @addr to textual form. - * - * Return value: (transfer full): the textual form of @addr - */ -char * -nm_utils_hwaddr_ntoa(gconstpointer addr, gsize length) -{ - g_return_val_if_fail(addr, g_strdup("")); - g_return_val_if_fail(length > 0, g_strdup("")); - - return nm_utils_bin2hexstr_full(addr, length, ':', TRUE, NULL); -} - -/** - * nm_utils_hwaddr_valid: - * @asc: the ASCII representation of a hardware address - * @length: the length of address that @asc is expected to convert to - * (or -1 to accept any length up to %NM_UTILS_HWADDR_LEN_MAX) - * - * Parses @asc to see if it is a valid hardware address of the given - * length. - * - * Return value: %TRUE if @asc appears to be a valid hardware address - * of the indicated length, %FALSE if not. - */ -gboolean -nm_utils_hwaddr_valid(const char *asc, gssize length) -{ - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - gsize l; - - g_return_val_if_fail(asc != NULL, FALSE); - g_return_val_if_fail(length >= -1 && length <= NM_UTILS_HWADDR_LEN_MAX, FALSE); - - if (length == 0) - return FALSE; - - if (!_nm_utils_hwaddr_aton(asc, buf, sizeof(buf), &l)) - return FALSE; - - return length == -1 || length == (gssize) l; -} - -/** - * nm_utils_hwaddr_canonical: - * @asc: the ASCII representation of a hardware address - * @length: the length of address that @asc is expected to convert to - * (or -1 to accept any length up to %NM_UTILS_HWADDR_LEN_MAX) - * - * Parses @asc to see if it is a valid hardware address of the given - * length, and if so, returns it in canonical form (uppercase, with - * leading 0s as needed, and with colons rather than hyphens). - * - * Return value: (transfer full): the canonicalized address if @asc appears to - * be a valid hardware address of the indicated length, %NULL if not. - */ -char * -nm_utils_hwaddr_canonical(const char *asc, gssize length) -{ - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - gsize l; - - g_return_val_if_fail(asc, NULL); - g_return_val_if_fail(length == -1 || (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX), NULL); - - if (!_nm_utils_hwaddr_aton(asc, buf, sizeof(buf), &l)) - return NULL; - if (length != -1 && length != (gssize) l) - return NULL; - return nm_utils_hwaddr_ntoa(buf, l); -} - -/* This is used to possibly canonicalize values passed to MAC address property - * setters. Unlike nm_utils_hwaddr_canonical(), it accepts %NULL, and if you - * pass it an invalid MAC address, it just returns that string rather than - * returning %NULL (so that we can return a proper error from verify() later). - */ -char * -_nm_utils_hwaddr_canonical_or_invalid(const char *mac, gssize length) -{ - char *canonical; - - if (!mac) - return NULL; - - canonical = nm_utils_hwaddr_canonical(mac, length); - if (canonical) - return canonical; - else - return g_strdup(mac); -} - -/* - * Determine if given Ethernet address is link-local - * - * Return value: %TRUE if @mac is link local - * reserved addr (01:80:c2:00:00:0X) per IEEE 802.1Q 8.6.3 Frame filtering, %FALSE if not. - */ -gboolean -_nm_utils_hwaddr_link_local_valid(const char *mac) -{ - guint8 mac_net[ETH_ALEN]; - static const guint8 eth_reserved_addr_base[] = {0x01, 0x80, 0xc2, 0x00, 0x00}; - - if (!mac) - return FALSE; - - if (!nm_utils_hwaddr_aton(mac, mac_net, ETH_ALEN)) - return FALSE; - - if (memcmp(mac_net, eth_reserved_addr_base, ETH_ALEN - 1) || (mac_net[5] & 0xF0)) - return FALSE; - - if (mac_net[5] == 1 /* 802.3x Pause address */ - || mac_net[5] == 2 /* 802.3ad Slow protocols */ - || mac_net[5] == 3) /* 802.1X PAE address */ - return FALSE; - - return TRUE; -} - -/** - * nm_utils_hwaddr_matches: - * @hwaddr1: (nullable): pointer to a binary or ASCII hardware address, or %NULL - * @hwaddr1_len: size of @hwaddr1, or -1 if @hwaddr1 is ASCII - * @hwaddr2: (nullable): pointer to a binary or ASCII hardware address, or %NULL - * @hwaddr2_len: size of @hwaddr2, or -1 if @hwaddr2 is ASCII - * - * Generalized hardware address comparison function. Tests if @hwaddr1 and - * @hwaddr2 "equal" (or more precisely, "equivalent"), with several advantages - * over a simple memcmp(): - * - * 1. If @hwaddr1_len or @hwaddr2_len is -1, then the corresponding address is - * assumed to be ASCII rather than binary, and will be converted to binary - * before being compared. - * - * 2. If @hwaddr1 or @hwaddr2 is %NULL, it is treated instead as though it was - * a zero-filled buffer @hwaddr1_len or @hwaddr2_len bytes long. - * - * 3. If @hwaddr1 and @hwaddr2 are InfiniBand hardware addresses (that is, if - * they are <literal>INFINIBAND_ALEN</literal> bytes long in binary form) - * then only the last 8 bytes are compared, since those are the only bytes - * that actually identify the hardware. (The other 12 bytes will change - * depending on the configuration of the InfiniBand fabric that the device - * is connected to.) - * - * If a passed-in ASCII hardware address cannot be parsed, or would parse to an - * address larger than %NM_UTILS_HWADDR_LEN_MAX, then it will silently fail to - * match. (This means that externally-provided address strings do not need to be - * sanity-checked before comparing them against known good addresses; they are - * guaranteed to not match if they are invalid.) - * - * Return value: %TRUE if @hwaddr1 and @hwaddr2 are equivalent, %FALSE if they are - * different (or either of them is invalid). - */ -gboolean -nm_utils_hwaddr_matches(gconstpointer hwaddr1, - gssize hwaddr1_len, - gconstpointer hwaddr2, - gssize hwaddr2_len) -{ - guint8 buf1[NM_UTILS_HWADDR_LEN_MAX], buf2[NM_UTILS_HWADDR_LEN_MAX]; - gsize l; - - if (hwaddr1_len == -1) { - if (hwaddr1 == NULL) { - hwaddr1_len = 0; - } else if (_nm_utils_hwaddr_aton(hwaddr1, buf1, sizeof(buf1), &l)) { - hwaddr1 = buf1; - hwaddr1_len = l; - } else { - g_return_val_if_fail(hwaddr2_len == -1 - || (hwaddr2_len > 0 && hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX), - FALSE); - return FALSE; - } - } else { - g_return_val_if_fail(hwaddr1_len > 0 && hwaddr1_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE); - - if (!hwaddr1) { - memset(buf1, 0, hwaddr1_len); - hwaddr1 = buf1; - } - } - - if (hwaddr2_len == -1) { - if (hwaddr2 == NULL) - l = 0; - else if (!_nm_utils_hwaddr_aton(hwaddr2, buf2, sizeof(buf2), &l)) - return FALSE; - if (l != hwaddr1_len) - return FALSE; - hwaddr2 = buf2; - } else { - g_return_val_if_fail(hwaddr2_len > 0 && hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE); - - if (hwaddr2_len != hwaddr1_len) - return FALSE; - - if (!hwaddr2) { - memset(buf2, 0, hwaddr2_len); - hwaddr2 = buf2; - } - } - - if (G_UNLIKELY(hwaddr1_len <= 0 || hwaddr1_len > NM_UTILS_HWADDR_LEN_MAX)) { - /* Only valid addresses can compare equal. In particular, - * addresses that are too long or of zero bytes, never - * compare equal. */ - return FALSE; - } - - if (hwaddr1_len == INFINIBAND_ALEN) { - hwaddr1 = &((guint8 *) hwaddr1)[INFINIBAND_ALEN - 8]; - hwaddr2 = &((guint8 *) hwaddr2)[INFINIBAND_ALEN - 8]; - hwaddr1_len = 8; - } - - return !memcmp(hwaddr1, hwaddr2, hwaddr1_len); -} - -/*****************************************************************************/ - -static GVariant * -_nm_utils_hwaddr_to_dbus_impl(const char *str) -{ - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - gsize len; - - if (!str) - return NULL; - if (!_nm_utils_hwaddr_aton(str, buf, sizeof(buf), &len)) - return NULL; - - return g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, buf, len, 1); -} - -static GVariant * -_nm_utils_hwaddr_cloned_get(const NMSettInfoSetting * sett_info, - guint property_idx, - NMConnection * connection, - NMSetting * setting, - NMConnectionSerializationFlags flags, - const NMConnectionSerializationOptions *options) -{ - gs_free char *addr = NULL; - - nm_assert(nm_streq(sett_info->property_infos[property_idx].name, "cloned-mac-address")); - - g_object_get(setting, "cloned-mac-address", &addr, NULL); - return _nm_utils_hwaddr_to_dbus_impl(addr); -} - -static gboolean -_nm_utils_hwaddr_cloned_set(NMSetting * setting, - GVariant * connection_dict, - const char * property, - GVariant * value, - NMSettingParseFlags parse_flags, - GError ** error) -{ - gsize length; - const guint8 *array; - char * str; - - nm_assert(nm_streq0(property, "cloned-mac-address")); - - if (!_nm_setting_use_legacy_property(setting, - connection_dict, - "cloned-mac-address", - "assigned-mac-address")) - return TRUE; - - length = 0; - array = g_variant_get_fixed_array(value, &length, 1); - - if (!length) - return TRUE; - - str = nm_utils_hwaddr_ntoa(array, length); - g_object_set(setting, "cloned-mac-address", str, NULL); - g_free(str); - return TRUE; -} - -static gboolean -_nm_utils_hwaddr_cloned_not_set(NMSetting * setting, - GVariant * connection_dict, - const char * property, - NMSettingParseFlags parse_flags, - GError ** error) -{ - nm_assert(nm_streq0(property, "cloned-mac-address")); - return TRUE; -} - -const NMSettInfoPropertType nm_sett_info_propert_type_cloned_mac_address = { - .dbus_type = G_VARIANT_TYPE_BYTESTRING, - .to_dbus_fcn = _nm_utils_hwaddr_cloned_get, - .from_dbus_fcn = _nm_utils_hwaddr_cloned_set, - .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, -}; - -static GVariant * -_nm_utils_hwaddr_cloned_data_synth(const NMSettInfoSetting * sett_info, - guint property_idx, - NMConnection * connection, - NMSetting * setting, - NMConnectionSerializationFlags flags, - const NMConnectionSerializationOptions *options) -{ - gs_free char *addr = NULL; - - if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS) - return NULL; - - nm_assert(nm_streq0(sett_info->property_infos[property_idx].name, "assigned-mac-address")); - - g_object_get(setting, "cloned-mac-address", &addr, NULL); - - /* Before introducing the extended "cloned-mac-address" (and its D-Bus - * field "assigned-mac-address"), libnm's _nm_utils_hwaddr_to_dbus() - * would drop invalid values as it was unable to serialize them. - * - * Now, we would like to send invalid values as "assigned-mac-address" - * over D-Bus and let the server reject them. - * - * However, clients used to set the cloned-mac-address property - * to "" and it just worked as the value was not serialized in - * an ill form. - * - * To preserve that behavior, serialize "" as NULL. - */ - - return addr && addr[0] ? g_variant_new_take_string(g_steal_pointer(&addr)) : NULL; -} - -static gboolean -_nm_utils_hwaddr_cloned_data_set(NMSetting * setting, - GVariant * connection_dict, - const char * property, - GVariant * value, - NMSettingParseFlags parse_flags, - GError ** error) -{ - nm_assert(nm_streq0(property, "assigned-mac-address")); - - if (_nm_setting_use_legacy_property(setting, - connection_dict, - "cloned-mac-address", - "assigned-mac-address")) - return TRUE; - - g_object_set(setting, - "cloned-mac-address", - nm_str_not_empty(g_variant_get_string(value, NULL)), - NULL); - return TRUE; -} - -const NMSettInfoPropertType nm_sett_info_propert_type_assigned_mac_address = { - .dbus_type = G_VARIANT_TYPE_STRING, - .to_dbus_fcn = _nm_utils_hwaddr_cloned_data_synth, - .from_dbus_fcn = _nm_utils_hwaddr_cloned_data_set, -}; - -static GVariant * -_nm_utils_hwaddr_to_dbus(const GValue *prop_value) -{ - return _nm_utils_hwaddr_to_dbus_impl(g_value_get_string(prop_value)); -} - -static void -_nm_utils_hwaddr_from_dbus(GVariant *dbus_value, GValue *prop_value) -{ - gsize length = 0; - const guint8 *array = g_variant_get_fixed_array(dbus_value, &length, 1); - char * str; - - str = length ? nm_utils_hwaddr_ntoa(array, length) : NULL; - g_value_take_string(prop_value, str); -} - -const NMSettInfoPropertType nm_sett_info_propert_type_mac_address = { - .dbus_type = G_VARIANT_TYPE_BYTESTRING, - .gprop_to_dbus_fcn = _nm_utils_hwaddr_to_dbus, - .gprop_from_dbus_fcn = _nm_utils_hwaddr_from_dbus, -}; - -/*****************************************************************************/ - -/* Validate secret-flags. Most settings don't validate them, which is a bug. - * But we possibly cannot enforce a strict validation now. - * - * For new settings, they shall validate the secret-flags strictly. */ -gboolean -_nm_utils_secret_flags_validate(NMSettingSecretFlags secret_flags, - const char * setting_name, - const char * property_name, - NMSettingSecretFlags disallowed_flags, - GError ** error) -{ - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - return TRUE; - - if (NM_FLAGS_ANY(secret_flags, ~NM_SETTING_SECRET_FLAG_ALL)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("unknown secret flags")); - if (setting_name) - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - - if (!nm_utils_is_power_of_two(secret_flags)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("conflicting secret flags")); - if (setting_name) - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - - if (NM_FLAGS_ANY(secret_flags, disallowed_flags)) { - if (NM_FLAGS_HAS(secret_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("secret flags must not be \"not-required\"")); - if (setting_name) - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("unsupported secret flags")); - if (setting_name) - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - - return TRUE; -} - -gboolean -_nm_utils_wps_method_validate(NMSettingWirelessSecurityWpsMethod wps_method, - const char * setting_name, - const char * property_name, - gboolean wps_required, - GError ** error) -{ - if (wps_method > NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is invalid")); - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - - if (NM_FLAGS_HAS(wps_method, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED)) { - if (wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("can't be simultaneously disabled and enabled")); - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - if (wps_required) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("WPS is required")); - g_prefix_error(error, "%s.%s: ", setting_name, property_name); - return FALSE; - } - } - - return TRUE; -} - -/*****************************************************************************/ - -static char * -_split_word(char *s) -{ - /* takes @s and truncates the string on the first white-space. - * then it returns the first word afterwards (again seeking - * over leading white-space). */ - for (; s[0]; s++) { - if (g_ascii_isspace(s[0])) { - s[0] = '\0'; - s++; - while (g_ascii_isspace(s[0])) - s++; - return s; - } - } - return s; -} - -gboolean -_nm_utils_generate_mac_address_mask_parse(const char * value, - struct ether_addr * out_mask, - struct ether_addr **out_ouis, - gsize * out_ouis_len, - GError ** error) -{ - gs_free char * s_free = NULL; - char * s, *s_next; - struct ether_addr mask; - gs_unref_array GArray *ouis = NULL; - - g_return_val_if_fail(!error || !*error, FALSE); - - if (!value || !*value) { - /* NULL and "" are valid values and both mean the default - * "q */ - if (out_mask) { - memset(out_mask, 0, sizeof(*out_mask)); - out_mask->ether_addr_octet[0] |= 0x02; - } - NM_SET_OUT(out_ouis, NULL); - NM_SET_OUT(out_ouis_len, 0); - return TRUE; - } - - s_free = g_strdup(value); - s = s_free; - - /* skip over leading whitespace */ - while (g_ascii_isspace(s[0])) - s++; - - /* parse the first mask */ - s_next = _split_word(s); - if (!nm_utils_hwaddr_aton(s, &mask, ETH_ALEN)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("not a valid ethernet MAC address for mask at position %lld"), - (long long) (s - s_free)); - return FALSE; - } - - if (s_next[0]) { - ouis = g_array_sized_new(FALSE, FALSE, sizeof(struct ether_addr), 4); - - do { - s = s_next; - s_next = _split_word(s); - - g_array_set_size(ouis, ouis->len + 1); - if (!nm_utils_hwaddr_aton(s, - &g_array_index(ouis, struct ether_addr, ouis->len - 1), - ETH_ALEN)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - _("not a valid ethernet MAC address #%u at position %lld"), - ouis->len, - (long long) (s - s_free)); - return FALSE; - } - } while (s_next[0]); - } - - NM_SET_OUT(out_mask, mask); - NM_SET_OUT(out_ouis_len, ouis ? ouis->len : 0); - NM_SET_OUT(out_ouis, - ouis ? ((struct ether_addr *) g_array_free(g_steal_pointer(&ouis), FALSE)) : NULL); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -nm_utils_is_valid_iface_name_utf8safe(const char *utf8safe_name) -{ - gs_free gpointer bin_to_free = NULL; - gconstpointer bin; - gsize len; - - g_return_val_if_fail(utf8safe_name, FALSE); - - bin = nm_utils_buf_utf8safe_unescape(utf8safe_name, - NM_UTILS_STR_UTF8_SAFE_FLAG_NONE, - &len, - &bin_to_free); - - if (bin_to_free) { - /* some unescaping happened... */ - - if (len != strlen(bin)) { - /* there are embedded NUL chars. Invalid. */ - return FALSE; - } - } - - return nm_utils_ifname_valid_kernel(bin, NULL); -} - -/** - * nm_utils_is_valid_iface_name: - * @name: (allow-none): Name of interface - * @error: location to store the error occurring, or %NULL to ignore - * - * Validate the network interface name. - * - * This function is a 1:1 copy of the kernel's interface validation - * function in net/core/dev.c. - * - * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned. - * - * Before 1.20, this function did not accept %NULL as @name argument. If you - * want to run against older versions of libnm, don't pass %NULL. - */ -gboolean -nm_utils_is_valid_iface_name(const char *name, GError **error) -{ - g_return_val_if_fail(!error || !*error, FALSE); - - return nm_utils_ifname_valid_kernel(name, error); -} - -/** - * nm_utils_iface_valid_name: - * @name: (allow-none): Name of interface - * - * Validate the network interface name. - * - * Deprecated: 1.6: Use nm_utils_is_valid_iface_name() instead, with better error reporting. - * - * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned. - * - * Before 1.20, this function did not accept %NULL as @name argument. If you - * want to run against older versions of libnm, don't pass %NULL. - */ -gboolean -nm_utils_iface_valid_name(const char *name) -{ - return nm_utils_is_valid_iface_name(name, NULL); -} - -/** - * nm_utils_is_uuid: - * @str: (allow-none): a string that might be a UUID - * - * Checks if @str is a UUID - * - * Returns: %TRUE if @str is a UUID, %FALSE if not - * - * In older versions, nm_utils_is_uuid() did not accept %NULL as @str - * argument. Don't pass %NULL if you run against older versions of libnm. - */ -gboolean -nm_utils_is_uuid(const char *str) -{ - const char *p = str; - int num_dashes = 0; - - if (!p) - return FALSE; - - while (*p) { - if (*p == '-') - num_dashes++; - else if (!g_ascii_isxdigit(*p)) - return FALSE; - p++; - } - - if ((num_dashes == 4) && (p - str == 36)) - return TRUE; - - /* Backwards compat for older configurations */ - if ((num_dashes == 0) && (p - str == 40)) - return TRUE; - - return FALSE; -} - -static _nm_thread_local char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN]; - -/** - * nm_utils_inet4_ntop: (skip) - * @inaddr: the address that should be converted to string. - * @dst: the destination buffer, it must contain at least - * <literal>INET_ADDRSTRLEN</literal> or %NM_UTILS_INET_ADDRSTRLEN - * characters. If set to %NULL, it will return a pointer to an internal, static - * buffer (shared with nm_utils_inet6_ntop()). Beware, that the internal - * buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or - * nm_utils_inet6_ntop() that does not provide its own @dst buffer. Since - * 1.28, the internal buffer is thread local and thus thread safe. Before - * it was not thread safe. When in doubt, pass your own - * @dst buffer to avoid these issues. - * - * Wrapper for inet_ntop. - * - * Returns: the input buffer @dst, or a pointer to an - * internal, static buffer. This function cannot fail. - **/ -const char * -nm_utils_inet4_ntop(in_addr_t inaddr, char *dst) -{ - /* relying on the static buffer (by leaving @dst as %NULL) is discouraged. - * Don't do that! - * - * However, still support it to be lenient against mistakes and because - * this is public API of libnm. */ - return _nm_utils_inet4_ntop(inaddr, dst ?: _nm_utils_inet_ntop_buffer); -} - -/** - * nm_utils_inet6_ntop: (skip) - * @in6addr: the address that should be converted to string. - * @dst: the destination buffer, it must contain at least - * <literal>INET6_ADDRSTRLEN</literal> or %NM_UTILS_INET_ADDRSTRLEN - * characters. If set to %NULL, it will return a pointer to an internal, static - * buffer (shared with nm_utils_inet4_ntop()). Beware, that the internal - * buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or - * nm_utils_inet6_ntop() that does not provide its own @dst buffer. Since - * 1.28, the internal buffer is thread local and thus thread safe. Before - * it was not thread safe. When in doubt, pass your own - * @dst buffer to avoid these issues. - * - * Wrapper for inet_ntop. - * - * Returns: the input buffer @dst, or a pointer to an - * internal, static buffer. %NULL is not allowed as @in6addr, - * otherwise, this function cannot fail. - **/ -const char * -nm_utils_inet6_ntop(const struct in6_addr *in6addr, char *dst) -{ - /* relying on the static buffer (by leaving @dst as %NULL) is discouraged. - * Don't do that! - * - * However, still support it to be lenient against mistakes and because - * this is public API of libnm. */ - g_return_val_if_fail(in6addr, NULL); - return _nm_utils_inet6_ntop(in6addr, dst ?: _nm_utils_inet_ntop_buffer); -} - -/** - * nm_utils_ipaddr_valid: - * @family: <literal>AF_INET</literal> or <literal>AF_INET6</literal>, or - * <literal>AF_UNSPEC</literal> to accept either - * @ip: an IP address - * - * Checks if @ip contains a valid IP address of the given family. - * - * Return value: %TRUE or %FALSE - */ -gboolean -nm_utils_ipaddr_valid(int family, const char *ip) -{ - g_return_val_if_fail(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC, FALSE); - - return nm_utils_ipaddr_is_valid(family, ip); -} - -/** - * nm_utils_iinet6_is_token: - * @in6addr: the AF_INET6 address structure - * - * Checks if only the bottom 64bits of the address are set. - * - * Return value: %TRUE or %FALSE - */ -gboolean -_nm_utils_inet6_is_token(const struct in6_addr *in6addr) -{ - if (in6addr->s6_addr[0] || in6addr->s6_addr[1] || in6addr->s6_addr[2] || in6addr->s6_addr[3] - || in6addr->s6_addr[4] || in6addr->s6_addr[5] || in6addr->s6_addr[6] || in6addr->s6_addr[7]) - return FALSE; - - if (in6addr->s6_addr[8] || in6addr->s6_addr[9] || in6addr->s6_addr[10] || in6addr->s6_addr[11] - || in6addr->s6_addr[12] || in6addr->s6_addr[13] || in6addr->s6_addr[14] - || in6addr->s6_addr[15]) - return TRUE; - - return FALSE; -} - -/** - * _nm_utils_dhcp_duid_valid: - * @duid: the candidate DUID - * - * Checks if @duid string contains either a special duid value ("ll", - * "llt", "lease" or the "stable" variants) or a valid hex DUID. - * - * Return value: %TRUE or %FALSE - */ -gboolean -_nm_utils_dhcp_duid_valid(const char *duid, GBytes **out_duid_bin) -{ - guint8 duid_arr[128 + 2]; - gsize duid_len; - - NM_SET_OUT(out_duid_bin, NULL); - - if (!duid) - return FALSE; - - if (NM_IN_STRSET(duid, "lease", "llt", "ll", "stable-llt", "stable-ll", "stable-uuid")) { - return TRUE; - } - - if (nm_utils_hexstr2bin_full(duid, - FALSE, - FALSE, - FALSE, - ":", - 0, - duid_arr, - sizeof(duid_arr), - &duid_len)) { - /* MAX DUID length is 128 octects + the type code (2 octects). */ - if (duid_len > 2 && duid_len <= (128 + 2)) { - NM_SET_OUT(out_duid_bin, g_bytes_new(duid_arr, duid_len)); - return TRUE; - } - } - - return FALSE; -} - -/** - * nm_utils_check_virtual_device_compatibility: - * @virtual_type: a virtual connection type - * @other_type: a connection type to test against @virtual_type - * - * Determines if a connection of type @virtual_type can (in the - * general case) work with connections of type @other_type. - * - * If @virtual_type is %NM_TYPE_SETTING_VLAN, then this checks if - * @other_type is a valid type for the parent of a VLAN. - * - * If @virtual_type is a "master" type (eg, %NM_TYPE_SETTING_BRIDGE), - * then this checks if @other_type is a valid type for a slave of that - * master. - * - * Note that even if this returns %TRUE it is not guaranteed that - * <emphasis>every</emphasis> connection of type @other_type is - * compatible with @virtual_type; it may depend on the exact - * configuration of the two connections, or on the capabilities of an - * underlying device driver. - * - * Returns: %TRUE or %FALSE - */ -gboolean -nm_utils_check_virtual_device_compatibility(GType virtual_type, GType other_type) -{ - g_return_val_if_fail(_nm_setting_type_get_base_type_priority(virtual_type) - != NM_SETTING_PRIORITY_INVALID, - FALSE); - g_return_val_if_fail(_nm_setting_type_get_base_type_priority(other_type) - != NM_SETTING_PRIORITY_INVALID, - FALSE); - - if (virtual_type == NM_TYPE_SETTING_BOND) { - return (other_type == NM_TYPE_SETTING_INFINIBAND || other_type == NM_TYPE_SETTING_WIRED - || other_type == NM_TYPE_SETTING_BRIDGE || other_type == NM_TYPE_SETTING_BOND - || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN); - } else if (virtual_type == NM_TYPE_SETTING_BRIDGE) { - return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_BOND - || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN); - } else if (virtual_type == NM_TYPE_SETTING_TEAM) { - return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_BRIDGE - || other_type == NM_TYPE_SETTING_BOND || other_type == NM_TYPE_SETTING_TEAM - || other_type == NM_TYPE_SETTING_VLAN); - } else if (virtual_type == NM_TYPE_SETTING_VLAN) { - return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_WIRELESS - || other_type == NM_TYPE_SETTING_BRIDGE || other_type == NM_TYPE_SETTING_BOND - || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN); - } else { - g_warn_if_reached(); - return FALSE; - } -} - -/*****************************************************************************/ - -/** - * nm_utils_bond_mode_int_to_string: - * @mode: bonding mode as a numeric value - * - * Convert bonding mode from integer value to descriptive name. - * See https://www.kernel.org/doc/Documentation/networking/bonding.txt for - * available modes. - * - * Returns: bonding mode string, or NULL on error - * - * Since: 1.2 - */ -const char * -nm_utils_bond_mode_int_to_string(int mode) -{ - return _nm_setting_bond_mode_to_string(mode); -} - -/** - * nm_utils_bond_mode_string_to_int: - * @mode: bonding mode as string - * - * Convert bonding mode from string representation to numeric value. - * See https://www.kernel.org/doc/Documentation/networking/bonding.txt for - * available modes. - * The @mode string can be either a descriptive name or a number (as string). - * - * Returns: numeric bond mode, or -1 on error - * - * Since: 1.2 - */ -int -nm_utils_bond_mode_string_to_int(const char *mode) -{ - return _nm_setting_bond_mode_from_string(mode); -} - -/*****************************************************************************/ - -#define STRSTRDICTKEY_V1_SET 0x01 -#define STRSTRDICTKEY_V2_SET 0x02 -#define STRSTRDICTKEY_ALL_SET 0x03 - -struct _NMUtilsStrStrDictKey { - char type; - char data[1]; -}; - -guint -_nm_utils_strstrdictkey_hash(gconstpointer a) -{ - const NMUtilsStrStrDictKey *k = a; - const char * p; - NMHashState h; - - nm_hash_init(&h, 76642997u); - if (k) { - if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET) - g_return_val_if_reached(0); - - nm_hash_update_val(&h, k->type); - if (k->type & STRSTRDICTKEY_ALL_SET) { - p = strchr(k->data, '\0'); - if (k->type == STRSTRDICTKEY_ALL_SET) { - /* the key contains two strings. Continue... */ - p = strchr(p + 1, '\0'); - } - if (p != k->data) - nm_hash_update(&h, k->data, p - k->data); - } - } - return nm_hash_complete(&h); -} - -gboolean -_nm_utils_strstrdictkey_equal(gconstpointer a, gconstpointer b) -{ - const NMUtilsStrStrDictKey *k1 = a; - const NMUtilsStrStrDictKey *k2 = b; - - if (k1 == k2) - return TRUE; - if (!k1 || !k2) - return FALSE; - - if (k1->type != k2->type) - return FALSE; - - if (k1->type & STRSTRDICTKEY_ALL_SET) { - if (!nm_streq(k1->data, k2->data)) - return FALSE; - - if (k1->type == STRSTRDICTKEY_ALL_SET) { - gsize l = strlen(k1->data) + 1; - - return nm_streq(&k1->data[l], &k2->data[l]); - } - } - - return TRUE; -} - -NMUtilsStrStrDictKey * -_nm_utils_strstrdictkey_create(const char *v1, const char *v2) -{ - char type = 0; - gsize l1 = 0, l2 = 0; - NMUtilsStrStrDictKey *k; - - if (!v1 && !v2) - return g_malloc0(1); - - /* we need to distinguish between ("",NULL) and (NULL,""). - * Thus, in @type we encode which strings we have present - * as not-NULL. */ - if (v1) { - type |= STRSTRDICTKEY_V1_SET; - l1 = strlen(v1) + 1; - } - if (v2) { - type |= STRSTRDICTKEY_V2_SET; - l2 = strlen(v2) + 1; - } - - k = g_malloc(G_STRUCT_OFFSET(NMUtilsStrStrDictKey, data) + l1 + l2); - k->type = type; - if (v1) - memcpy(&k->data[0], v1, l1); - if (v2) - memcpy(&k->data[l1], v2, l2); - - return k; -} - -static gboolean -validate_dns_option(const char * name, - gboolean numeric, - gboolean ipv6, - const NMUtilsDNSOptionDesc *option_descs) -{ - const NMUtilsDNSOptionDesc *desc; - - if (!option_descs) - return !!*name; - - for (desc = option_descs; desc->name; desc++) { - if (nm_streq(name, desc->name) && numeric == desc->numeric && (!desc->ipv6_only || ipv6)) - return TRUE; - } - - return FALSE; -} - -/** - * _nm_utils_dns_option_validate: - * @option: option string - * @out_name: (out) (allow-none): the option name - * @out_value: (out) (allow-none): the option value - * @ipv6: whether the option refers to a IPv6 configuration - * @option_descs: (allow-none): an array of NMUtilsDNSOptionDesc which describes the - * valid options - * - * Parses a DNS option in the form "name" or "name:number" and, if - * @option_descs is not NULL, checks that the option conforms to one - * of the provided descriptors. If @option_descs is NULL @ipv6 is - * not considered. - * - * Returns: %TRUE when the parsing was successful and the option is valid, - * %FALSE otherwise - */ -gboolean -_nm_utils_dns_option_validate(const char * option, - char ** out_name, - long * out_value, - gboolean ipv6, - const NMUtilsDNSOptionDesc *option_descs) -{ - gs_free char *option0_free = NULL; - const char * option0; - const char * option1; - const char * delim; - long option1_num; - - g_return_val_if_fail(option != NULL, FALSE); - - NM_SET_OUT(out_name, NULL); - NM_SET_OUT(out_value, -1); - - if (!option[0]) - return FALSE; - - delim = strchr(option, ':'); - if (!delim) { - if (!validate_dns_option(option, FALSE, ipv6, option_descs)) - return FALSE; - NM_SET_OUT(out_name, g_strdup(option)); - return TRUE; - } - - option1 = &delim[1]; - - if (!option1[0]) - return FALSE; - if (!NM_STRCHAR_ALL(option1, ch, g_ascii_isdigit(ch))) - return FALSE; - - option0 = nm_strndup_a(300, option, delim - option, &option0_free); - - if (!validate_dns_option(option0, TRUE, ipv6, option_descs)) - return FALSE; - - option1_num = _nm_utils_ascii_str_to_int64(option1, 10, 0, G_MAXINT32, -1); - if (option1_num == -1) - return FALSE; - - NM_SET_OUT(out_name, g_steal_pointer(&option0_free) ?: g_strdup(option0)); - NM_SET_OUT(out_value, option1_num); - return TRUE; -} - -/** - * _nm_utils_dns_option_find_idx: - * @array: an array of strings - * @option: a dns option string - * - * Searches for an option in an array of strings. The match is - * performed only the option name; the option value is ignored. - * - * Returns: the index of the option in the array or -1 if was not - * found. - */ -gssize -_nm_utils_dns_option_find_idx(GPtrArray *array, const char *option) -{ - gs_free char *option_name = NULL; - guint i; - - if (!_nm_utils_dns_option_validate(option, &option_name, NULL, FALSE, NULL)) - return -1; - - for (i = 0; i < array->len; i++) { - gs_free char *tmp_name = NULL; - - if (_nm_utils_dns_option_validate(array->pdata[i], &tmp_name, NULL, FALSE, NULL)) { - if (nm_streq(tmp_name, option_name)) - return i; - } - } - - return -1; -} - -/*****************************************************************************/ - -/** - * nm_utils_enum_to_str: - * @type: the %GType of the enum - * @value: the value to be translated - * - * Converts an enum value to its string representation. If the enum is a - * %G_TYPE_FLAGS the function returns a comma-separated list of matching values. - * If the value has no corresponding string representation, it is converted - * to a number. For enums it is converted to a decimal number, for flags - * to an (unsigned) hex number. - * - * Returns: a newly allocated string or %NULL - * - * Since: 1.2 - */ -char * -nm_utils_enum_to_str(GType type, int value) -{ - return _nm_utils_enum_to_str_full(type, value, ", ", NULL); -} - -/** - * nm_utils_enum_from_str: - * @type: the %GType of the enum - * @str: the input string - * @out_value: (out) (allow-none): the output value - * @err_token: (out) (allow-none) (transfer full): location to store the first unrecognized token - * - * Converts a string to the matching enum value. - * - * If the enum is a %G_TYPE_FLAGS the function returns the logical OR of values - * matching the comma-separated tokens in the string; if an unknown token is found - * the function returns %FALSE and stores a pointer to a newly allocated string - * containing the unrecognized token in @err_token. - * - * Returns: %TRUE if the conversion was successful, %FALSE otherwise - * - * Since: 1.2 - */ -gboolean -nm_utils_enum_from_str(GType type, const char *str, int *out_value, char **err_token) -{ - return _nm_utils_enum_from_str_full(type, str, out_value, err_token, NULL); -} - -/** - * nm_utils_enum_get_values: - * @type: the %GType of the enum - * @from: the first element to be returned - * @to: the last element to be returned - * - * Returns the list of possible values for a given enum. - * - * Returns: (transfer container): a NULL-terminated dynamically-allocated array of static strings - * or %NULL on error - * - * Since: 1.2 - */ -const char ** -nm_utils_enum_get_values(GType type, int from, int to) -{ - return _nm_utils_enum_get_values(type, from, to); -} - -/*****************************************************************************/ - -static gboolean -_nm_utils_is_json_object_no_validation(const char *str, GError **error) -{ - nm_assert(str); - - /* libjansson also requires only utf-8 encoding. */ - if (!g_utf8_validate(str, -1, NULL)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("not valid utf-8")); - return FALSE; - } - while (g_ascii_isspace(str[0])) - str++; - - /* do some very basic validation to see if this might be a JSON object. */ - if (str[0] == '{') { - gsize l; - - l = strlen(str) - 1; - while (l > 0 && g_ascii_isspace(str[l])) - l--; - - if (str[l] == '}') - return TRUE; - } - - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("is not a JSON object")); - return FALSE; -} - -/** - * nm_utils_is_json_object: - * @str: the JSON string to test - * @error: optional error reason - * - * Returns: whether the passed string is valid JSON. - * If libnm is not compiled with libjansson support, this check will - * also return %TRUE for possibly invalid inputs. If that is a problem - * for you, you must validate the JSON yourself. - * - * Since: 1.6 - */ -gboolean -nm_utils_is_json_object(const char *str, GError **error) -{ - nm_auto_decref_json nm_json_t *json = NULL; - const NMJsonVt * vt; - nm_json_error_t jerror; - - g_return_val_if_fail(!error || !*error, FALSE); - - if (!str || !str[0]) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - str ? _("value is NULL") : _("value is empty")); - return FALSE; - } - - if (!(vt = nm_json_vt())) - return _nm_utils_is_json_object_no_validation(str, error); - - json = vt->nm_json_loads(str, NM_JSON_REJECT_DUPLICATES, &jerror); - if (!json) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid JSON at position %d (%s)"), - jerror.position, - jerror.text); - return FALSE; - } - - /* valid JSON (depending on the definition) can also be a literal. - * Here we only allow objects. */ - if (!nm_json_is_object(json)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("is not a JSON object")); - return FALSE; - } - - return TRUE; -} - -static char * -attribute_unescape(const char *start, const char *end) -{ - char *ret, *dest; - - nm_assert(start <= end); - dest = ret = g_malloc(end - start + 1); - - for (; start < end && *start; start++) { - if (*start == '\\') { - start++; - if (!*start) - break; - } - *dest++ = *start; - } - *dest = '\0'; - - return ret; -} - -gboolean -_nmtst_variant_attribute_spec_assert_sorted(const NMVariantAttributeSpec *const *array, gsize len) -{ - gsize i; - - g_assert(array); - g_assert(len > 0); - g_assert_cmpint(len, ==, NM_PTRARRAY_LEN(array)); - - for (i = 0; i < len; i++) { - nm_assert(array[i]->name); - nm_assert(array[i]->name[0]); - if (i > 0) - nm_assert(strcmp(array[i - 1]->name, array[i]->name) < 0); - } - nm_assert(!array[i]); - - return TRUE; -} - -const NMVariantAttributeSpec * -_nm_variant_attribute_spec_find_binary_search(const NMVariantAttributeSpec *const *array, - gsize len, - const char * name) -{ - gssize idx; - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMVariantAttributeSpec, name) == 0); - - idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) array, - len, - &name, - nm_strcmp_p_with_data, - NULL, - NULL, - NULL); - if (idx < 0) - return NULL; - return array[idx]; -} - -/** - * nm_utils_parse_variant_attributes: - * @string: the input string - * @attr_separator: the attribute separator character - * @key_value_separator: character separating key and values - * @ignore_unknown: whether unknown attributes should be ignored - * @spec: the attribute format specifiers - * @error: (out) (allow-none): location to store the error on failure - * - * Parse attributes from a string. - * - * Returns: (transfer full) (element-type utf8 GVariant): a #GHashTable mapping - * attribute names to #GVariant values. Warning: the variant are still floating - * references, owned by the hash table. If you take a reference, ensure to sink - * the one of the hash table first. - * - * Since: 1.8 - */ -GHashTable * -nm_utils_parse_variant_attributes(const char * string, - char attr_separator, - char key_value_separator, - gboolean ignore_unknown, - const NMVariantAttributeSpec *const *spec, - GError ** error) -{ - gs_unref_hashtable GHashTable * ht = NULL; - const char * ptr = string, *start = NULL, *sep; - GVariant * variant; - const NMVariantAttributeSpec *const *s; - - g_return_val_if_fail(string, NULL); - g_return_val_if_fail(attr_separator, NULL); - g_return_val_if_fail(key_value_separator, NULL); - g_return_val_if_fail(!error || !*error, NULL); - - ht = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); - - while (TRUE) { - gs_free char *name = NULL, *value = NULL; - - if (!start) - start = ptr; - if (*ptr == '\\') { - ptr++; - if (!*ptr) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("unterminated escape sequence")); - return NULL; - } - goto next; - } - if (*ptr == attr_separator || *ptr == '\0') { - if (ptr == start) { - /* multiple separators */ - start = NULL; - goto next; - } - - /* Find the key-value separator */ - for (sep = start; sep != ptr; sep++) { - if (*sep == '\\') { - sep++; - if (!*sep) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("unterminated escape sequence")); - return NULL; - } - } - if (*sep == key_value_separator) - break; - } - - name = attribute_unescape(start, sep); - - for (s = spec; *s; s++) { - if (g_hash_table_contains(ht, (*s)->name)) - continue; - if (nm_streq(name, (*s)->name)) - break; - if ((*s)->no_value && g_variant_type_equal((*s)->type, G_VARIANT_TYPE_STRING)) - break; - } - - if (!*s) { - if (ignore_unknown) - goto next; - else { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("unknown attribute '%s'"), - name); - return NULL; - } - } - - if ((*s)->no_value) { - if ((*s)->consumes_rest) { - value = g_strdup(start); - ptr = strchr(start, '\0'); - } else { - value = g_steal_pointer(&name); - } - } else { - if (*sep != key_value_separator) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("missing key-value separator '%c' after '%s'"), - key_value_separator, - name); - return NULL; - } - - /* The attribute and key/value separators are the same. Look for the next one. */ - if (ptr == sep) - goto next; - - value = attribute_unescape(sep + 1, ptr); - } - - if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_UINT32)) { - gint64 num = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT32, -1); - - if (num == -1) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("invalid uint32 value '%s' for attribute '%s'"), - value, - (*s)->name); - return NULL; - } - variant = g_variant_new_uint32(num); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_INT32)) { - gint64 num = - _nm_utils_ascii_str_to_int64(value, 10, G_MININT32, G_MAXINT32, G_MAXINT64); - - if (num == G_MAXINT64) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("invalid int32 value '%s' for attribute '%s'"), - value, - (*s)->name); - return NULL; - } - variant = g_variant_new_int32(num); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_UINT64)) { - guint64 num = _nm_utils_ascii_str_to_uint64(value, 10, 0, G_MAXUINT64, G_MAXUINT64); - - if (num == G_MAXUINT64 && errno != 0) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("invalid uint64 value '%s' for attribute '%s'"), - value, - (*s)->name); - return NULL; - } - variant = g_variant_new_uint64(num); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_BYTE)) { - gint64 num = _nm_utils_ascii_str_to_int64(value, 10, 0, G_MAXUINT8, -1); - - if (num == -1) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("invalid uint8 value '%s' for attribute '%s'"), - value, - (*s)->name); - return NULL; - } - variant = g_variant_new_byte((guchar) num); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_BOOLEAN)) { - int b; - - b = (*s)->no_value ? TRUE : _nm_utils_ascii_str_to_bool(value, -1); - if (b == -1) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("invalid boolean value '%s' for attribute '%s'"), - value, - (*s)->name); - return NULL; - } - variant = g_variant_new_boolean(b); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_STRING)) { - variant = g_variant_new_take_string(g_steal_pointer(&value)); - } else if (g_variant_type_equal((*s)->type, G_VARIANT_TYPE_BYTESTRING)) { - variant = g_variant_new_bytestring(value); - } else { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("unsupported attribute '%s' of type '%s'"), - (*s)->name, - (char *) (*s)->type); - return NULL; - } - - g_hash_table_insert(ht, g_strdup((*s)->name), variant); - start = NULL; - } -next: - if (*ptr == '\0') - break; - ptr++; - } - - return g_steal_pointer(&ht); -} - -/* - * nm_utils_format_variant_attributes: - * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values - * @attr_separator: the attribute separator character - * @key_value_separator: character separating key and values - * - * Format attributes to a string. - * - * Returns: (transfer full): the string representing attributes, or %NULL - * in case there are no attributes - * - * Since: 1.8 - */ -char * -nm_utils_format_variant_attributes(GHashTable *attributes, - char attr_separator, - char key_value_separator) -{ - return _nm_utils_format_variant_attributes(attributes, - NULL, - attr_separator, - key_value_separator); -} - -/*****************************************************************************/ - -/* - * nm_utils_get_timestamp_msec(): - * - * Gets current time in milliseconds of CLOCK_BOOTTIME. - * - * Returns: time in milliseconds - * - * Since: 1.12 - */ -gint64 -nm_utils_get_timestamp_msec(void) -{ - gint64 ts; - - ts = nm_utils_clock_gettime_msec(CLOCK_BOOTTIME); - if (ts >= 0) - return ts; - - if (ts == -EINVAL) { - /* The fallback to CLOCK_MONOTONIC is taken only if we're running on a - * criminally old kernel, prior to 2.6.39 (released on 18 May, 2011). - * That happens during buildcheck on old builders, we don't expect to - * be actually runs on kernels that old. */ - ts = nm_utils_clock_gettime_msec(CLOCK_MONOTONIC); - if (ts >= 0) - return ts; - } - - g_return_val_if_reached(-1); -} - -/*****************************************************************************/ - -/** - * nm_utils_version: - * - * Returns: the version ID of the libnm version. That is, the %NM_VERSION - * at runtime. - * - * Since: 1.6.0 - */ -guint -nm_utils_version(void) -{ - return NM_VERSION; -} - -/*****************************************************************************/ - -NM_UTILS_FLAGS2STR_DEFINE(nm_bluetooth_capability_to_string, - NMBluetoothCapabilities, - NM_UTILS_FLAGS2STR(NM_BT_CAPABILITY_NONE, "NONE"), - NM_UTILS_FLAGS2STR(NM_BT_CAPABILITY_DUN, "DUN"), - NM_UTILS_FLAGS2STR(NM_BT_CAPABILITY_NAP, "NAP"), ); - -/*****************************************************************************/ - -/** - * nm_utils_base64secret_decode: - * @base64_key: the (possibly invalid) base64 encode key. - * @required_key_len: the expected (binary) length of the key after - * decoding. If the length does not match, the validation fails. - * @out_key: (allow-none): (out): an optional output buffer for the binary - * key. If given, it will be filled with exactly @required_key_len - * bytes. - * - * Returns: %TRUE if the input key is a valid base64 encoded key - * with @required_key_len bytes. - * - * Since: 1.16 - */ -gboolean -nm_utils_base64secret_decode(const char *base64_key, gsize required_key_len, guint8 *out_key) -{ - nm_auto_free guint8 *bin_arr = NULL; - gsize base64_key_len; - gsize bin_len; - int r; - - if (!base64_key) - return FALSE; - - base64_key_len = strlen(base64_key); - - r = nm_sd_utils_unbase64mem(base64_key, base64_key_len, TRUE, &bin_arr, &bin_len); - if (r < 0) - return FALSE; - if (bin_len != required_key_len) { - nm_explicit_bzero(bin_arr, bin_len); - return FALSE; - } - - if (out_key) - memcpy(out_key, bin_arr, required_key_len); - - nm_explicit_bzero(bin_arr, bin_len); - return TRUE; -} - -gboolean -nm_utils_base64secret_normalize(const char *base64_key, - gsize required_key_len, - char ** out_base64_key_norm) -{ - gs_free guint8 *buf_free = NULL; - guint8 buf_static[200]; - guint8 * buf; - - if (required_key_len > sizeof(buf_static)) { - buf_free = g_new(guint8, required_key_len); - buf = buf_free; - } else - buf = buf_static; - - if (!nm_utils_base64secret_decode(base64_key, required_key_len, buf)) { - NM_SET_OUT(out_base64_key_norm, NULL); - return FALSE; - } - - NM_SET_OUT(out_base64_key_norm, g_base64_encode(buf, required_key_len)); - nm_explicit_bzero(buf, required_key_len); - return TRUE; -} - -static GVariant * -_nm_utils_bridge_vlans_to_dbus(const NMSettInfoSetting * sett_info, - guint property_idx, - NMConnection * connection, - NMSetting * setting, - NMConnectionSerializationFlags flags, - const NMConnectionSerializationOptions *options) -{ - gs_unref_ptrarray GPtrArray *vlans = NULL; - GVariantBuilder builder; - guint i; - const char * property_name = sett_info->property_infos[property_idx].name; - - nm_assert(property_name); - - g_object_get(setting, property_name, &vlans, NULL); - g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}")); - - if (vlans) { - for (i = 0; i < vlans->len; i++) { - NMBridgeVlan * vlan = vlans->pdata[i]; - GVariantBuilder vlan_builder; - guint16 vid_start, vid_end; - - nm_bridge_vlan_get_vid_range(vlan, &vid_start, &vid_end); - - g_variant_builder_init(&vlan_builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add(&vlan_builder, - "{sv}", - "vid-start", - g_variant_new_uint16(vid_start)); - g_variant_builder_add(&vlan_builder, "{sv}", "vid-end", g_variant_new_uint16(vid_end)); - g_variant_builder_add(&vlan_builder, - "{sv}", - "pvid", - g_variant_new_boolean(nm_bridge_vlan_is_pvid(vlan))); - g_variant_builder_add(&vlan_builder, - "{sv}", - "untagged", - g_variant_new_boolean(nm_bridge_vlan_is_untagged(vlan))); - g_variant_builder_add(&builder, "a{sv}", &vlan_builder); - } - } - - return g_variant_builder_end(&builder); -} - -static gboolean -_nm_utils_bridge_vlans_from_dbus(NMSetting * setting, - GVariant * connection_dict, - const char * property, - GVariant * value, - NMSettingParseFlags parse_flags, - GError ** error) -{ - gs_unref_ptrarray GPtrArray *vlans = NULL; - GVariantIter vlan_iter; - GVariant * vlan_var; - - g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), FALSE); - - vlans = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref); - g_variant_iter_init(&vlan_iter, value); - while (g_variant_iter_next(&vlan_iter, "@a{sv}", &vlan_var)) { - _nm_unused gs_unref_variant GVariant *var_unref = vlan_var; - NMBridgeVlan * vlan; - guint16 vid_start, vid_end; - gboolean pvid = FALSE, untagged = FALSE; - - if (!g_variant_lookup(vlan_var, "vid-start", "q", &vid_start)) - continue; - if (vid_start < NM_BRIDGE_VLAN_VID_MIN || vid_start > NM_BRIDGE_VLAN_VID_MAX) - continue; - - if (!g_variant_lookup(vlan_var, "vid-end", "q", &vid_end)) - continue; - if (vid_end < NM_BRIDGE_VLAN_VID_MIN || vid_end > NM_BRIDGE_VLAN_VID_MAX) - continue; - if (vid_start > vid_end) - continue; - - if (!g_variant_lookup(vlan_var, "pvid", "b", &pvid)) - pvid = FALSE; - if (pvid && vid_start != vid_end) - continue; - if (!g_variant_lookup(vlan_var, "untagged", "b", &untagged)) - untagged = FALSE; - - vlan = nm_bridge_vlan_new(vid_start, vid_end); - nm_bridge_vlan_set_untagged(vlan, untagged); - nm_bridge_vlan_set_pvid(vlan, pvid); - g_ptr_array_add(vlans, vlan); - } - - g_object_set(setting, property, vlans, NULL); - - return TRUE; -} - -const NMSettInfoPropertType nm_sett_info_propert_type_bridge_vlans = { - .dbus_type = NM_G_VARIANT_TYPE("aa{sv}"), - .to_dbus_fcn = _nm_utils_bridge_vlans_to_dbus, - .from_dbus_fcn = _nm_utils_bridge_vlans_from_dbus, -}; - -gboolean -_nm_utils_bridge_vlan_verify_list(GPtrArray * vlans, - gboolean check_normalizable, - GError ** error, - const char *setting, - const char *property) -{ - guint i; - gs_unref_hashtable GHashTable *h = NULL; - gboolean pvid_found = FALSE; - - if (!vlans || vlans->len <= 1) - return TRUE; - - if (check_normalizable) { - guint16 vid_prev_end, vid_start, vid_end; - - nm_assert(_nm_utils_bridge_vlan_verify_list(vlans, FALSE, NULL, setting, property)); - - nm_bridge_vlan_get_vid_range(vlans->pdata[0], NULL, &vid_prev_end); - for (i = 1; i < vlans->len; i++) { - const NMBridgeVlan *vlan = vlans->pdata[i]; - - nm_bridge_vlan_get_vid_range(vlan, &vid_start, &vid_end); - - if (vid_prev_end > vid_start) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("Bridge VLANs %d and %d are not sorted by ascending vid"), - vid_prev_end, - vid_start); - g_prefix_error(error, "%s.%s: ", setting, property); - return FALSE; - } - - vid_prev_end = vid_end; - } - return TRUE; - } - - h = g_hash_table_new(nm_direct_hash, NULL); - for (i = 0; i < vlans->len; i++) { - NMBridgeVlan *vlan = vlans->pdata[i]; - guint16 v, vid_start, vid_end; - - nm_bridge_vlan_get_vid_range(vlan, &vid_start, &vid_end); - - for (v = vid_start; v <= vid_end; v++) { - if (!nm_g_hash_table_add(h, GUINT_TO_POINTER(v))) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("duplicate bridge VLAN vid %u"), - v); - g_prefix_error(error, "%s.%s: ", setting, property); - return FALSE; - } - } - - if (nm_bridge_vlan_is_pvid(vlan)) { - if (vid_start != vid_end || pvid_found) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("only one VLAN can be the PVID")); - g_prefix_error(error, "%s.%s: ", setting, property); - return FALSE; - } - pvid_found = TRUE; - } - } - - return TRUE; -} - -gboolean -_nm_utils_iaid_verify(const char *str, gint64 *out_value) -{ - gint64 iaid; - - NM_SET_OUT(out_value, -1); - - if (!str || !str[0]) - return FALSE; - - if (NM_IAID_IS_SPECIAL(str)) - return TRUE; - - if (NM_STRCHAR_ALL(str, ch, ch >= '0' && ch <= '9') && (str[0] != '0' || str[1] == '\0') - && (iaid = _nm_utils_ascii_str_to_int64(str, 10, 0, G_MAXUINT32, -1)) != -1) { - NM_SET_OUT(out_value, iaid); - return TRUE; - } - - return FALSE; -} - -gboolean -_nm_utils_validate_dhcp_hostname_flags(NMDhcpHostnameFlags flags, int addr_family, GError **error) -{ - NMDhcpHostnameFlags unknown; - - unknown = flags; - unknown &= ~(NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED | NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE - | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE | NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS); - if (unknown) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("unknown flags 0x%x"), - (guint) unknown); - return FALSE; - } - - if (NM_FLAGS_ALL(flags, - NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE - | NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)) { - g_set_error_literal( - error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'fqdn-no-update' and 'fqdn-serv-update' flags cannot be set at the same time")); - return FALSE; - } - - if (NM_FLAGS_HAS(flags, NM_DHCP_HOSTNAME_FLAG_FQDN_CLEAR_FLAGS) - && NM_FLAGS_ANY(flags, - NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE | NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED - | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'fqdn-clear-flags' flag is incompatible with other FQDN flags")); - return FALSE; - } - - if (addr_family == AF_INET6 && (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("DHCPv6 does not support the E (encoded) FQDN flag")); - return FALSE; - } - - return TRUE; -} |