From 964ae8cc391520440cf5aa13e2b9cc34850ea6c2 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Tue, 26 Feb 2019 19:01:41 +0100 Subject: New upstream version 1.14.6 --- libnm-core/nm-utils.c | 369 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 262 insertions(+), 107 deletions(-) (limited to 'libnm-core/nm-utils.c') diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 0f524e11..f75276c7 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2737,11 +2737,14 @@ nm_utils_sriov_vf_from_str (const char *str, GError **error) detail++; } - return _nm_utils_sriov_vf_from_strparts (str, detail, error); + return _nm_utils_sriov_vf_from_strparts (str, detail, FALSE, error); } NMSriovVF * -_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error) +_nm_utils_sriov_vf_from_strparts (const char *index, + const char *detail, + gboolean ignore_unknown, + GError **error) { NMSriovVF *vf; guint32 n_index; @@ -2761,7 +2764,12 @@ _nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError vf = nm_sriov_vf_new (n_index); if (detail) { - ht = nm_utils_parse_variant_attributes (detail, ' ', '=', TRUE, _nm_sriov_vf_attribute_spec, error); + ht = nm_utils_parse_variant_attributes (detail, + ' ', + '=', + ignore_unknown, + _nm_sriov_vf_attribute_spec, + error); if (!ht) { nm_sriov_vf_unref (vf); return NULL; @@ -2785,6 +2793,57 @@ _nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError /*****************************************************************************/ +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 < 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 @@ -2794,11 +2853,12 @@ _nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError char * nm_utils_uuid_generate_buf_ (char *buf) { - uuid_t uuid; + NMUuid uuid; + + nm_assert (buf); - uuid_generate_random (uuid); - uuid_unparse_lower (uuid, buf); - return buf; + _nm_utils_uuid_generate_random (&uuid); + return _nm_utils_uuid_unparse (&uuid, buf); } /** @@ -2814,7 +2874,9 @@ nm_utils_uuid_generate (void) } /** - * nm_utils_uuid_generate_from_string: + * 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 @@ -2824,60 +2886,92 @@ nm_utils_uuid_generate (void) * * 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 + * Returns: the input @uuid. This function cannot fail. **/ -char * -nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, gpointer type_args) +NMUuid * +nm_utils_uuid_generate_from_string_bin (NMUuid *uuid, const char *s, gssize slen, int uuid_type, gpointer type_args) { - uuid_t uuid; - char *buf; - + g_return_val_if_fail (uuid, FALSE); g_return_val_if_fail (slen == 0 || s, FALSE); - g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); - g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); - 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)); + sizeof (*uuid)); break; - case NM_UTILS_UUID_TYPE_VARIANT3: { - uuid_t ns_uuid = { 0 }; + case NM_UTILS_UUID_TYPE_VERSION3: + case NM_UTILS_UUID_TYPE_VERSION5: { + NMUuid ns_uuid = { 0 }; if (type_args) { /* type_args can be a name space UUID. Interpret it as (char *) */ - if (uuid_parse ((char *) type_args, ns_uuid) != 0) + if (!_nm_utils_uuid_parse (type_args, &ns_uuid)) g_return_val_if_reached (NULL); } - nm_crypto_md5_hash ((guint8 *) s, - slen, - (guint8 *) ns_uuid, - sizeof (ns_uuid), - (guint8 *) uuid, - sizeof (uuid)); + 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[6] = (uuid[6] & 0x0F) | 0x30; - uuid[8] = (uuid[8] & 0x3F) | 0x80; + 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); } - buf = g_malloc (37); - uuid_unparse_lower (uuid, &buf[0]); + return uuid; +} - return buf; +/** + * 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); } /** @@ -2902,7 +2996,7 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...) char *uuid; if (!string1) - return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VERSION3, NM_UTILS_UUID_NS); str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/ @@ -2916,7 +3010,7 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...) } va_end (args); - uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VERSION3, NM_UTILS_UUID_NS); g_string_free (str, TRUE); return uuid; @@ -3527,60 +3621,68 @@ nm_utils_hwaddr_len (int type) } guint8 * -_nm_utils_str2bin_full (const char *asc, - gboolean delimiter_required, - const char *delimiter_candidates, - guint8 *buffer, - gsize buffer_length, - gsize *out_len) -{ - const char *in = asc; +_nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len) +{ + const char *in = hexstr; guint8 *out = buffer; gboolean delimiter_has = TRUE; guint8 delimiter = '\0'; + gsize len; - nm_assert (asc); + nm_assert (hexstr); nm_assert (buffer); - nm_assert (buffer_length); - nm_assert (out_len); + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && in[0] == '0' + && in[1] == 'x') + in += 2; while (TRUE) { const guint8 d1 = in[0]; guint8 d2; + int i1, i2; - if (!g_ascii_isxdigit (d1)) - return NULL; - -#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - ('A' - 10)) + i1 = nm_utils_hexchar_to_int (d1); + if (i1 < 0) + goto fail; /* If there's no leading zero (ie "aa:b:cc") then fake it */ d2 = in[1]; - if (d2 && g_ascii_isxdigit (d2)) { - *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2); + if ( d2 + && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { + *out++ = (i1 << 4) + i2; d2 = in[2]; if (!d2) break; in += 2; } else { /* Fake leading zero */ - *out++ = HEXVAL (d1); + *out++ = i1; if (!d2) { if (!delimiter_has) { /* when using no delimiter, there must be pairs of hex chars */ - return NULL; + goto fail; } break; } in += 1; } - if (--buffer_length == 0) - return NULL; + if (--buffer_len == 0) + goto fail; if (delimiter_has) { if (d2 != delimiter) { if (delimiter) - return NULL; + goto fail; if (delimiter_candidates) { while (delimiter_candidates[0]) { if (delimiter_candidates++[0] == d2) @@ -3589,7 +3691,7 @@ _nm_utils_str2bin_full (const char *asc, } if (!delimiter) { if (delimiter_required) - return NULL; + goto fail; delimiter_has = FALSE; continue; } @@ -3598,11 +3700,66 @@ _nm_utils_str2bin_full (const char *asc, } } - *out_len = out - buffer; - return buffer; + len = out - buffer; + if ( required_len == 0 + || len == required_len) { + NM_SET_OUT (out_len, len); + return buffer; + } + +fail: + NM_SET_OUT (out_len, 0); + return NULL; } -#define hwaddr_aton(asc, buffer, buffer_length, out_len) _nm_utils_str2bin_full ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len)) +guint8 * +_nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len) +{ + guint8 *buffer; + gsize buffer_len, len; + + g_return_val_if_fail (hexstr, NULL); + + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && hexstr[0] == '0' + && hexstr[1] == 'x') + hexstr += 2; + + if (!hexstr[0]) + goto fail; + + if (required_len > 0) + buffer_len = required_len; + else + buffer_len = strlen (hexstr) / 2 + 3; + + buffer = g_malloc (buffer_len); + + if (_nm_utils_hexstr2bin_full (hexstr, + FALSE, + delimiter_required, + delimiter_candidates, + required_len, + buffer, + buffer_len, + &len)) { + NM_SET_OUT (out_len, len); + return buffer; + } + + g_free (buffer); + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} /** * nm_utils_hexstr2bin: @@ -3619,23 +3776,17 @@ GBytes * nm_utils_hexstr2bin (const char *hex) { guint8 *buffer; - gsize buffer_length, len; - - g_return_val_if_fail (hex != NULL, NULL); - - if (hex[0] == '0' && hex[1] == 'x') - hex += 2; + gsize len; - buffer_length = strlen (hex) / 2 + 3; - buffer = g_malloc (buffer_length); - if (!_nm_utils_str2bin_full (hex, FALSE, ":", buffer, buffer_length, &len)) { - g_free (buffer); + 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); } +#define hwaddr_aton(asc, buffer, buffer_len, out_len) _nm_utils_hexstr2bin_full ((asc), FALSE, TRUE, ":-", 0, (buffer), (buffer_len), (out_len)) + /** * nm_utils_hwaddr_atoba: * @asc: the ASCII representation of a hardware address @@ -3728,16 +3879,46 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length) return buffer; } -void -_nm_utils_bin2str_full (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out) +/** + * _nm_utils_bin2hexstr_full: + * @addr: pointer of @length bytes. + * @length: number of bytes in @addr + * @delimiter: either '\0', otherwise the output string will have the + * given delimiter character between each two hex numbers. + * @upper_case: if TRUE, use upper case ASCII characters for hex. + * @out: if %NULL, the function will allocate a new buffer of + * either (@length*2+1) or (@length*3) bytes, depending on whether + * a @delimiter is specified. In that case, the allocated buffer will + * be returned and must be freed by the caller. + * If not %NULL, the buffer must already be preallocated and contain + * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. + * + * Returns: the binary value converted to a hex string. If @out is given, + * this always returns @out. If @out is %NULL, a newly allocated string + * is returned. + */ +char * +_nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out) { const guint8 *in = addr; const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char *out0; nm_assert (addr); - nm_assert (out); nm_assert (length > 0); + if (out) + out0 = out; + else { + out0 = out = g_new (char, delimiter == '\0' + ? length * 2 + 1 + : length * 3); + } + /* @out must contain at least @length*3 bytes if @delimiter is set, * otherwise, @length*2+1. */ @@ -3754,6 +3935,7 @@ _nm_utils_bin2str_full (gconstpointer addr, gsize length, const char delimiter, } *out = 0; + return out0; } /** @@ -3779,7 +3961,8 @@ nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len) g_return_val_if_fail (final_len < 0 || (gsize) final_len < buflen, NULL); result = g_malloc (buflen); - _nm_utils_bin2str_full (src, len, '\0', FALSE, result); + + _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) @@ -3800,14 +3983,10 @@ nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len) char * nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) { - char *result; - g_return_val_if_fail (addr, g_strdup ("")); g_return_val_if_fail (length > 0, g_strdup ("")); - result = g_malloc (length * 3); - _nm_utils_bin2str_full (addr, length, ':', TRUE, result); - return result; + return _nm_utils_bin2hexstr_full (addr, length, ':', TRUE, NULL); } const char * @@ -3819,31 +3998,7 @@ nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_cas if (buf_len < addr_len * 3) g_return_val_if_reached (NULL); - _nm_utils_bin2str_full (addr, addr_len, ':', upper_case, buf); - return buf; -} - -/** - * _nm_utils_bin2str: - * @addr: (type guint8) (array length=length): a binary hardware address - * @length: the length of @addr - * @upper_case: the case for the hexadecimal digits. - * - * Converts @addr to textual form. - * - * Return value: (transfer full): the textual form of @addr - */ -char * -_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case) -{ - char *result; - - g_return_val_if_fail (addr, g_strdup ("")); - g_return_val_if_fail (length > 0, g_strdup ("")); - - result = g_malloc (length * 3); - _nm_utils_bin2str_full (addr, length, ':', upper_case, result); - return result; + return _nm_utils_bin2hexstr_full (addr, addr_len, ':', upper_case, buf); } /** @@ -4510,7 +4665,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin) return TRUE; } - if (_nm_utils_str2bin_full (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) { + if (_nm_utils_hexstr2bin_full (duid, 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)) { -- cgit 1.3.0-6-gf8a5 From 9a6dcbf895f9da01768e64b73cec88c16157d91e Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Tue, 26 Mar 2019 23:25:23 +0100 Subject: New upstream version 1.16.0 --- libnm-core/nm-utils.c | 785 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 519 insertions(+), 266 deletions(-) (limited to 'libnm-core/nm-utils.c') diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index f75276c7..d276cfe6 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -23,8 +23,6 @@ #include "nm-utils.h" -#include -#include #include #include #include @@ -40,6 +38,8 @@ #endif #include "nm-utils/nm-enum-utils.h" +#include "nm-utils/nm-secret-utils.h" +#include "systemd/nm-sd-utils-shared.h" #include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -61,13 +61,284 @@ * access points and devices, among other things. */ +/*****************************************************************************/ + +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 = (const char *[]) { __VA_ARGS__, NULL }} +#define LANG_ENCODINGS(l, ...) { .lang = l, .encodings = NM_MAKE_STRV (__VA_ARGS__), } /* 5-letter language codes */ static const struct IsoLangToEncodings isoLangEntries5[] = @@ -112,7 +383,7 @@ static const struct IsoLangToEncodings isoLangEntries2[] = 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"), /* Ukranian */ + LANG_ENCODINGS ("uk", "koi8-u", "koi8-r", "windows-1251"), /* Ukrainian */ /* Arabic */ LANG_ENCODINGS ("ar", "iso-8859-6","windows-1256"), @@ -836,7 +1107,7 @@ device_supports_ap_ciphers (guint32 dev_caps, /** * nm_utils_ap_mode_security_valid: - * @type: the security type to check device capabilties against, + * @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 @@ -872,16 +1143,16 @@ nm_utils_ap_mode_security_valid (NMUtilsSecurityType type, /** * nm_utils_security_valid: - * @type: the security type to check AP flags and device capabilties against, + * @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 capabilties derived from the AP's WPA beacon, + * @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 capabilties derived from the AP's RSN/WPA2 beacon, + * @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 @@ -891,7 +1162,7 @@ nm_utils_ap_mode_security_valid (NMUtilsSecurityType type, * 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 capabilties intersect and are + * Returns: %TRUE if the device capabilities and AP capabilities intersect and are * compatible with the desired @type, %FALSE if they are not **/ gboolean @@ -1173,7 +1444,7 @@ nm_utils_ip4_dns_from_variant (GVariant *value) dns = g_new (char *, length + 1); for (i = 0; i < length; i++) - dns[i] = g_strdup (nm_utils_inet4_ntop (array[i], NULL)); + dns[i] = nm_utils_inet4_ntop_dup (array[i]); dns[i] = NULL; return dns; @@ -1271,7 +1542,7 @@ nm_utils_ip4_addresses_from_variant (GVariant *value, char **out_gateway) g_ptr_array_add (addresses, addr); if (addr_array[2] && out_gateway && !*out_gateway) - *out_gateway = g_strdup (nm_utils_inet4_ntop (addr_array[2], NULL)); + *out_gateway = nm_utils_inet4_ntop_dup (addr_array[2]); } else { g_warning ("Ignoring invalid IP4 address: %s", error->message); g_clear_error (&error); @@ -1389,30 +1660,13 @@ nm_utils_ip4_routes_from_variant (GVariant *value) guint32 nm_utils_ip4_netmask_to_prefix (guint32 netmask) { - guint32 prefix; - guint8 v; - const guint8 *p = (guint8 *) &netmask; - - if (p[3]) { - prefix = 24; - v = p[3]; - } else if (p[2]) { - prefix = 16; - v = p[2]; - } else if (p[1]) { - prefix = 8; - v = p[1]; - } else { - prefix = 0; - v = p[0]; - } - - while (v) { - prefix++; - v <<= 1; - } + G_STATIC_ASSERT_EXPR (__SIZEOF_INT__ == 4); + G_STATIC_ASSERT_EXPR (sizeof (int) == 4); + G_STATIC_ASSERT_EXPR (sizeof (netmask) == 4); - return prefix; + return ( (netmask != 0) + ? (32 - __builtin_ctz (ntohl (netmask))) + : 0); } /** @@ -1510,7 +1764,7 @@ nm_utils_ip6_dns_from_variant (GVariant *value) continue; } - dns[i++] = g_strdup (nm_utils_inet6_ntop (ip, NULL)); + dns[i++] = nm_utils_inet6_ntop_dup (ip); g_variant_unref (ip_var); } dns[i] = NULL; @@ -1629,7 +1883,7 @@ nm_utils_ip6_addresses_from_variant (GVariant *value, char **out_gateway) goto next; } if (!IN6_IS_ADDR_UNSPECIFIED (gateway_bytes)) - *out_gateway = g_strdup (nm_utils_inet6_ntop (gateway_bytes, NULL)); + *out_gateway = nm_utils_inet6_ntop_dup (gateway_bytes); } } else { g_warning ("Ignoring invalid IP6 address: %s", error->message); @@ -2017,7 +2271,7 @@ _string_append_tc_handle (GString *string, guint32 handle) * or to pretty-format (use symbolic name for root) the key in keyfile. * The presence of prefix determnines which one is the case. * - * Private API due to general uglyness and overall uselessness for anything + * Private API due to general ugliness and overall uselessness for anything * sensible. */ void @@ -2909,7 +3163,7 @@ nm_utils_uuid_generate_from_string_bin (NMUuid *uuid, const char *s, gssize slen break; case NM_UTILS_UUID_TYPE_VERSION3: case NM_UTILS_UUID_TYPE_VERSION5: { - NMUuid ns_uuid = { 0 }; + NMUuid ns_uuid = { }; if (type_args) { /* type_args can be a name space UUID. Interpret it as (char *) */ @@ -3124,7 +3378,7 @@ _nm_utils_check_file (const char *filename, g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_FAILED, - _("failed stat file %s: %s"), filename, strerror (errsv)); + _("failed stat file %s: %s"), filename, nm_strerror_native (errsv)); return FALSE; } @@ -3243,7 +3497,7 @@ _nm_utils_check_module_file (const char *name, * @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. + * @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. @@ -3620,147 +3874,6 @@ nm_utils_hwaddr_len (int type) g_return_val_if_reached (0); } -guint8 * -_nm_utils_hexstr2bin_full (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - guint8 *buffer, - gsize buffer_len, - gsize *out_len) -{ - const char *in = hexstr; - guint8 *out = buffer; - gboolean delimiter_has = TRUE; - guint8 delimiter = '\0'; - gsize len; - - nm_assert (hexstr); - nm_assert (buffer); - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && in[0] == '0' - && in[1] == 'x') - in += 2; - - while (TRUE) { - const guint8 d1 = in[0]; - guint8 d2; - int i1, i2; - - i1 = nm_utils_hexchar_to_int (d1); - if (i1 < 0) - goto fail; - - /* If there's no leading zero (ie "aa:b:cc") then fake it */ - d2 = in[1]; - if ( d2 - && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { - *out++ = (i1 << 4) + i2; - d2 = in[2]; - if (!d2) - break; - in += 2; - } else { - /* Fake leading zero */ - *out++ = i1; - if (!d2) { - if (!delimiter_has) { - /* when using no delimiter, there must be pairs of hex chars */ - goto fail; - } - break; - } - in += 1; - } - - if (--buffer_len == 0) - goto fail; - - if (delimiter_has) { - if (d2 != delimiter) { - if (delimiter) - goto fail; - if (delimiter_candidates) { - while (delimiter_candidates[0]) { - if (delimiter_candidates++[0] == d2) - delimiter = d2; - } - } - if (!delimiter) { - if (delimiter_required) - goto fail; - delimiter_has = FALSE; - continue; - } - } - in++; - } - } - - len = out - buffer; - if ( required_len == 0 - || len == required_len) { - NM_SET_OUT (out_len, len); - return buffer; - } - -fail: - NM_SET_OUT (out_len, 0); - return NULL; -} - -guint8 * -_nm_utils_hexstr2bin_alloc (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - gsize *out_len) -{ - guint8 *buffer; - gsize buffer_len, len; - - g_return_val_if_fail (hexstr, NULL); - - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && hexstr[0] == '0' - && hexstr[1] == 'x') - hexstr += 2; - - if (!hexstr[0]) - goto fail; - - if (required_len > 0) - buffer_len = required_len; - else - buffer_len = strlen (hexstr) / 2 + 3; - - buffer = g_malloc (buffer_len); - - if (_nm_utils_hexstr2bin_full (hexstr, - FALSE, - delimiter_required, - delimiter_candidates, - required_len, - buffer, - buffer_len, - &len)) { - NM_SET_OUT (out_len, len); - return buffer; - } - - g_free (buffer); - -fail: - NM_SET_OUT (out_len, 0); - return NULL; -} - /** * nm_utils_hexstr2bin: * @hex: a string of hexadecimal characters with optional ':' separators @@ -3778,14 +3891,14 @@ nm_utils_hexstr2bin (const char *hex) guint8 *buffer; gsize len; - buffer = _nm_utils_hexstr2bin_alloc (hex, TRUE, FALSE, ":", 0, &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); } -#define hwaddr_aton(asc, buffer, buffer_len, out_len) _nm_utils_hexstr2bin_full ((asc), FALSE, TRUE, ":-", 0, (buffer), (buffer_len), (out_len)) +#define hwaddr_aton(asc, buffer, buffer_len, out_len) nm_utils_hexstr2bin_full ((asc), FALSE, TRUE, ":-", 0, (buffer), (buffer_len), (out_len)) /** * nm_utils_hwaddr_atoba: @@ -3879,64 +3992,7 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length) return buffer; } -/** - * _nm_utils_bin2hexstr_full: - * @addr: pointer of @length bytes. - * @length: number of bytes in @addr - * @delimiter: either '\0', otherwise the output string will have the - * given delimiter character between each two hex numbers. - * @upper_case: if TRUE, use upper case ASCII characters for hex. - * @out: if %NULL, the function will allocate a new buffer of - * either (@length*2+1) or (@length*3) bytes, depending on whether - * a @delimiter is specified. In that case, the allocated buffer will - * be returned and must be freed by the caller. - * If not %NULL, the buffer must already be preallocated and contain - * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. - * - * Returns: the binary value converted to a hex string. If @out is given, - * this always returns @out. If @out is %NULL, a newly allocated string - * is returned. - */ -char * -_nm_utils_bin2hexstr_full (gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char *out) -{ - const guint8 *in = addr; - const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; - char *out0; - - nm_assert (addr); - nm_assert (length > 0); - - if (out) - out0 = out; - else { - out0 = out = g_new (char, delimiter == '\0' - ? length * 2 + 1 - : length * 3); - } - - /* @out must contain at least @length*3 bytes if @delimiter is set, - * otherwise, @length*2+1. */ - - for (;;) { - const guint8 v = *in++; - *out++ = LOOKUP[v >> 4]; - *out++ = LOOKUP[v & 0x0F]; - length--; - if (!length) - break; - if (delimiter) - *out++ = delimiter; - } - - *out = 0; - return out0; -} /** * nm_utils_bin2hexstr: @@ -3962,7 +4018,7 @@ nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len) result = g_malloc (buflen); - _nm_utils_bin2hexstr_full (src, len, '\0', FALSE, result); + 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) @@ -3986,7 +4042,7 @@ 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); + return nm_utils_bin2hexstr_full (addr, length, ':', TRUE, NULL); } const char * @@ -3998,7 +4054,7 @@ nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_cas if (buf_len < addr_len * 3) g_return_val_if_reached (NULL); - return _nm_utils_bin2hexstr_full (addr, addr_len, ':', upper_case, buf); + return nm_utils_bin2hexstr_full (addr, addr_len, ':', upper_case, buf); } /** @@ -4251,13 +4307,18 @@ _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, } GVariant * -_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, +_nm_utils_hwaddr_cloned_data_synth (const NMSettInfoSetting *sett_info, + guint property_idx, NMConnection *connection, - const char *property) + NMSetting *setting, + NMConnectionSerializationFlags flags) { gs_free char *addr = NULL; - nm_assert (nm_streq0 (property, "assigned-mac-address")); + 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", @@ -4278,7 +4339,9 @@ _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, * To preserve that behavior, serialize "" as NULL. */ - return addr && addr[0] ? g_variant_new_string (addr) : NULL; + return addr && addr[0] + ? g_variant_new_take_string (g_steal_pointer (&addr)) + : NULL; } gboolean @@ -4321,6 +4384,102 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, /*****************************************************************************/ +/* 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) { @@ -4517,10 +4676,11 @@ nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst) nm_assert_addr_family (addr_family); nm_assert (addr); + nm_assert (dst); s = inet_ntop (addr_family, addr, - dst ?: _nm_utils_inet_ntop_buffer, + dst, addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN); nm_assert (s); return s; @@ -4546,6 +4706,11 @@ nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst) 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 inet_ntop (AF_INET, &inaddr, dst ?: _nm_utils_inet_ntop_buffer, INET_ADDRSTRLEN); } @@ -4571,6 +4736,11 @@ nm_utils_inet4_ntop (in_addr_t inaddr, char *dst) 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 inet_ntop (AF_INET6, in6addr, dst ?: _nm_utils_inet_ntop_buffer, INET6_ADDRSTRLEN); @@ -4665,7 +4835,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin) return TRUE; } - if (_nm_utils_hexstr2bin_full (duid, FALSE, FALSE, ":", 0, duid_arr, sizeof (duid_arr), &duid_len)) { + if (nm_utils_hexstr2bin_full (duid, 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)) { @@ -5315,7 +5485,7 @@ _json_find_object (json_t *json, return json_element; } -static inline void +static void _json_delete_object_on_int_match (json_t *json, const char *key1, const char *key2, @@ -5331,7 +5501,7 @@ _json_delete_object_on_int_match (json_t *json, _json_del_object (json, key1, key2, key3); } -static inline void +static void _json_delete_object_on_bool_match (json_t *json, const char *key1, const char *key2, @@ -5347,7 +5517,7 @@ _json_delete_object_on_bool_match (json_t *json, _json_del_object (json, key1, key2, key3); } -static inline void +static void _json_delete_object_on_string_match (json_t *json, const char *key1, const char *key2, @@ -5435,7 +5605,7 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element) const char *j_key; json_t *j_val; gs_free char *name = NULL, *target_host = NULL, *source_host = NULL; - int val1 = 0, val2 = 0, val3 = 3; + int val1 = 0, val2 = 0, val3 = 3, val4 = -1; NMTeamLinkWatcherArpPingFlags flags = 0; g_return_val_if_fail (json_element, NULL); @@ -5456,6 +5626,8 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element) val2 = json_integer_value (j_val); else if (nm_streq (j_key, "missed_max")) val3 = json_integer_value (j_val); + else if (nm_streq (j_key, "vlanid")) + val4 = json_integer_value (j_val); else if (nm_streq (j_key, "validate_active")) { if (json_is_true (j_val)) flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; @@ -5473,8 +5645,8 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element) else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, NULL); else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING)) { - return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, - source_host, flags, NULL); + return nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4, target_host, + source_host, flags, NULL); } else return NULL; } @@ -5524,6 +5696,9 @@ _nm_utils_team_link_watcher_to_json (NMTeamLinkWatcher *watcher) if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) return json_element; + int_val = nm_team_link_watcher_get_vlanid (watcher); + if (int_val != -1) + json_object_set_new (json_element, "vlanid", json_integer (int_val)); str_val = nm_team_link_watcher_get_source_host (watcher); if (!str_val) goto fail; @@ -5976,15 +6151,15 @@ _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers) name = nm_team_link_watcher_get_name (watcher); g_variant_builder_add (&watcher_builder, "{sv}", - "name", - g_variant_new_string (name)); + "name", + g_variant_new_string (name)); - if nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL) { + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { int_val = nm_team_link_watcher_get_delay_up (watcher); if (int_val) { g_variant_builder_add (&watcher_builder, "{sv}", - "delay-up", - g_variant_new_int32 (int_val)); + "delay-up", + g_variant_new_int32 (int_val)); } int_val = nm_team_link_watcher_get_delay_down (watcher); if (int_val) { @@ -6019,12 +6194,18 @@ _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers) "target-host", g_variant_new_string (nm_team_link_watcher_get_target_host (watcher))); - if nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING) { + if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) { g_variant_builder_add (&builder, "a{sv}", &watcher_builder); continue; } /* arp_ping watcher only */ + int_val = nm_team_link_watcher_get_vlanid (watcher); + if (int_val != -1) { + g_variant_builder_add (&watcher_builder, "{sv}", + "vlanid", + g_variant_new_int32 (int_val)); + } g_variant_builder_add (&watcher_builder, "{sv}", "source-host", g_variant_new_string (nm_team_link_watcher_get_source_host (watcher))); @@ -6075,7 +6256,7 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value) while (g_variant_iter_next (&iter, "@a{sv}", &watcher_var)) { NMTeamLinkWatcher *watcher; const char *name; - int val1, val2, val3 = 0; + int val1, val2, val3 = 0, val4 = -1; const char *target_host = NULL, *source_host = NULL; gboolean bval; NMTeamLinkWatcherArpPingFlags flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE; @@ -6105,7 +6286,9 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value) val2 = 0; if (!g_variant_lookup (watcher_var, "missed-max", "i", &val3)) val3 = 3; - if nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) { + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING)) { + if (!g_variant_lookup (watcher_var, "vlanid", "i", &val4)) + val4 = -1; if (!g_variant_lookup (watcher_var, "source-host", "&s", &source_host)) goto next; if (!g_variant_lookup (watcher_var, "validate-active", "b", &bval)) @@ -6120,9 +6303,9 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value) bval = FALSE; if (bval) flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; - watcher = nm_team_link_watcher_new_arp_ping (val1, val2, val3, - target_host, source_host, - flags, &error); + watcher = nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4, + target_host, source_host, + flags, &error); } else watcher = nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, &error); @@ -6487,3 +6670,73 @@ nm_utils_version (void) return NM_VERSION; } +/*****************************************************************************/ + +/** + * 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) +{ + gs_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, &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; +} -- cgit 1.3.0-6-gf8a5 From 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sun, 21 Apr 2019 21:09:51 +0200 Subject: New upstream version 1.18.0 --- libnm-core/nm-utils.c | 422 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 305 insertions(+), 117 deletions(-) (limited to 'libnm-core/nm-utils.c') diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d276cfe6..04d5b1b5 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -37,15 +37,16 @@ #include "nm-json.h" #endif -#include "nm-utils/nm-enum-utils.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "systemd/nm-sd-utils-shared.h" -#include "nm-common-macros.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" @@ -1034,30 +1035,6 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray) return strv; } -/** - * _nm_utils_strv_equal: - * @strv1: a string array - * @strv2: a string array - * - * Compare NULL-terminated string arrays for equality. - * - * Returns: %TRUE if the arrays are equal, %FALSE otherwise. - **/ -gboolean -_nm_utils_strv_equal (char **strv1, char **strv2) -{ - if (strv1 == strv2) - return TRUE; - - if (!strv1 || !strv2) - return FALSE; - - for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++) - ; - - return !*strv1 && !*strv2; -} - static gboolean device_supports_ap_ciphers (guint32 dev_caps, guint32 ap_flags, @@ -2332,6 +2309,15 @@ static const NMVariantAttributeSpec * const tc_object_attribute_spec[] = { NULL, }; +typedef struct { + const char *kind; + const NMVariantAttributeSpec * const *attrs; +} NMQdiscAttributeSpec; + +static const NMQdiscAttributeSpec *const tc_qdisc_attribute_spec[] = { + NULL, +}; + /*****************************************************************************/ /** @@ -2348,6 +2334,7 @@ _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 && strcmp (kind, "ingress") != 0) { g_string_append (string, "handle "); @@ -2356,6 +2343,13 @@ _nm_utils_string_append_tc_qdisc_rest (GString *string, NMTCQdisc *qdisc) } 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); + } } /** @@ -2463,7 +2457,7 @@ _tc_read_common_opts (const char *str, * @str: the string representation of a qdisc * @error: location of the error * - * Parces the tc style string qdisc representation of the queueing + * 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 @@ -2478,21 +2472,36 @@ nm_utils_tc_qdisc_from_str (const char *str, GError **error) gs_free char *kind = NULL; gs_free char *rest = NULL; NMTCQdisc *qdisc = NULL; - gs_unref_hashtable GHashTable *ht = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GHashTableIter iter; + gpointer key, value; + guint i; nm_assert (str); nm_assert (!error || !*error); - ht = nm_utils_parse_variant_attributes (str, - ' ', ' ', FALSE, - tc_object_attribute_spec, - error); - if (!ht) - return NULL; - if (!_tc_read_common_opts (str, &handle, &parent, &kind, &rest, error)) return NULL; + for (i = 0; rest && tc_qdisc_attribute_spec[i]; i++) { + if (strcmp (tc_qdisc_attribute_spec[i]->kind, kind) == 0) { + options = nm_utils_parse_variant_attributes (rest, + ' ', ' ', FALSE, + tc_qdisc_attribute_spec[i]->attrs, + error); + if (!options) + return NULL; + break; + } + } + nm_clear_pointer (&rest, g_free); + + 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; @@ -2504,8 +2513,15 @@ nm_utils_tc_qdisc_from_str (const char *str, GError **error) 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[] = { @@ -2522,24 +2538,14 @@ static const NMVariantAttributeSpec * const tc_action_attribute_spec[] = { static gboolean _string_append_tc_action (GString *string, NMTCAction *action, GError **error) { - gs_unref_hashtable GHashTable *ht = NULL; const char *kind = nm_tc_action_get_kind (action); - gs_strfreev char **attr_names = NULL; gs_free char *str = NULL; - int i; - - ht = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); g_string_append (string, kind); - attr_names = nm_tc_action_get_attribute_names (action); - for (i = 0; attr_names[i]; i++) { - g_hash_table_insert (ht, attr_names[i], - nm_tc_action_get_attribute (action, attr_names[i])); - } - - if (i) { - str = nm_utils_format_variant_attributes (ht, ' ', ' '); + str = nm_utils_format_variant_attributes (_nm_tc_action_get_attributes (action), + ' ', ' '); + if (str) { g_string_append_c (string, ' '); g_string_append (string, str); } @@ -2578,7 +2584,7 @@ nm_utils_tc_action_to_str (NMTCAction *action, GError **error) * @str: the string representation of a action * @error: location of the error * - * Parces the tc style string action representation of the queueing + * 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 @@ -2731,7 +2737,7 @@ static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = { * @str: the string representation of a tfilter * @error: location of the error * - * Parces the tc style string tfilter representation of the queueing + * 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 @@ -2872,7 +2878,7 @@ _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, ";", FALSE); + vlans = nm_utils_strsplit_set (str, ";"); if (!vlans) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2986,8 +2992,7 @@ nm_utils_sriov_vf_from_str (const char *str, GError **error) detail = strchr (str, ' '); if (detail) { - index_free = g_strndup (str, detail - str); - str = index_free; + str = nm_strndup_a (200, str, detail - str, &index_free); detail++; } @@ -4565,6 +4570,29 @@ _nm_utils_generate_mac_address_mask_parse (const char *value, /*****************************************************************************/ +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, &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_is_valid_iface_name (bin, NULL); +} + /** * nm_utils_is_valid_iface_name: * @name: Name of interface @@ -5430,11 +5458,11 @@ _json_team_add_defaults (json_t *json, if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { _json_add_object (json, "notify_peers", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); + json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); _json_add_object (json, "mcast_rejoin", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); + json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); } else if ( nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) - || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { + || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { json_element = json_array (); json_array_append_new (json_element, json_string ("eth")); json_array_append_new (json_element, json_string ("ipv4")); @@ -5538,6 +5566,7 @@ _json_team_normalize_defaults (json_t *json, gboolean reset) { json_t *json_element; const char *runner = NM_SETTING_TEAM_RUNNER_DEFAULT; + gs_free char *runner_free = NULL; int notify_peers_count = 0, notify_peers_interval = 0; int mcast_rejoin_count = 0, mcast_rejoin_interval = 0; int runner_tx_balancer_interval = -1; @@ -5546,7 +5575,8 @@ _json_team_normalize_defaults (json_t *json, gboolean reset) json_element = _json_find_object (json, "runner", "name", NULL); if (json_element) { - runner = json_string_value (json_element); + runner_free = g_strdup (json_string_value (json_element)); + runner = runner_free; _json_delete_object_on_string_match (json, "runner", "name", NULL, NM_SETTING_TEAM_RUNNER_DEFAULT); } @@ -5922,11 +5952,9 @@ _nm_utils_team_config_get (const char *conf, if (json_is_string (str_element)) g_ptr_array_add (data, g_strdup (json_string_value (str_element))); } - if (data->len) { - g_value_init (value, G_TYPE_STRV); - g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (data)); - } - g_ptr_array_free (data, TRUE); + g_ptr_array_add (data, NULL); + g_value_init (value, G_TYPE_STRV); + g_value_take_boxed (value, g_ptr_array_free (data, FALSE)); } else { g_assert_not_reached (); g_free (value); @@ -5948,14 +5976,13 @@ _nm_utils_team_config_set (char **conf, const char *key3, const GValue *value) { - json_t *json, *json_element, *json_link, *json_value = NULL; + nm_auto_decref_json json_t *json = NULL; + nm_auto_decref_json json_t *json_value = NULL; + json_t *json_element; + json_t *json_link; json_error_t jerror; - gboolean updated = FALSE; - char **strv; - GPtrArray *array; const char *iter_key = key; - int i; - NMTeamLinkWatcher *watcher; + gs_free char *conf_new = NULL; g_return_val_if_fail (key, FALSE); @@ -5966,14 +5993,12 @@ _nm_utils_team_config_set (char **conf, if (!json) return FALSE; - /* no new value? delete element */ if (!value) { - updated = _json_del_object (json, key, key2, key3); + if (!_json_del_object (json, key, key2, key3)) + return FALSE; goto done; } - /* insert new value */ - updated = TRUE; if (G_VALUE_HOLDS_STRING (value)) json_value = json_string (g_value_get_string (value)); else if (G_VALUE_HOLDS_INT (value)) @@ -5982,53 +6007,56 @@ _nm_utils_team_config_set (char **conf, json_value = json_boolean (g_value_get_boolean (value)); else if (G_VALUE_HOLDS_BOXED (value)) { if (nm_streq (key, "link_watch")) { + gboolean has_array = FALSE; + GPtrArray *array; + guint i; + array = g_value_get_boxed (value); - if (!array || !array->len) { - updated = FALSE; - goto done; - } + if (!array || !array->len) + return FALSE; - /* - * json_value: will hold the final link_watcher json (array) object - * json_element: is the next link_watcher to append to json_value - * json_link: used to transit the json_value from a single link_watcher - * object to an array of link watcher objects - */ - json_value = NULL; for (i = 0; i < array->len; i++) { - watcher = array->pdata[i]; - json_element = _nm_utils_team_link_watcher_to_json (watcher); - if (!json_element) + json_t *el; + + el = _nm_utils_team_link_watcher_to_json (array->pdata[i]); + if (!el) continue; + /* if there is only one watcher, it is added as-is. If there + * are multiple watchers, they are added in an array. */ if (!json_value) { - json_value = json_element; + json_value = el; continue; } - if (!json_is_array (json_value)) { - json_link = json_value; - json_value = json_array (); - json_array_append_new (json_value, json_link); + if (!has_array) { + json_t *el_arr; + + has_array = TRUE; + el_arr = json_array(); + json_array_append_new (el_arr, json_value); + json_value = el_arr; } - json_array_append_new (json_value, json_element); + json_array_append_new (json_value, el); } } else if ( nm_streq (key, "runner") && nm_streq0 (key2, "tx_hash")) { + const char *const*strv; + gsize i; + strv = g_value_get_boxed (value); - if (!strv) { - updated = FALSE; - goto done; - } + if (!strv) + return FALSE; + json_value = json_array (); for (i = 0; strv[i]; i++) json_array_append_new (json_value, json_string (strv[i])); } else { - updated = FALSE; - goto done; + nm_assert_not_reached (); + return FALSE; } + } else { /* G_VALUE_HOLDS_? */ - g_assert_not_reached (); - updated = FALSE; - goto done; + nm_assert_not_reached (); + return FALSE; } /* Simplest case: first level key only */ @@ -6054,22 +6082,19 @@ _nm_utils_team_config_set (char **conf, iter_key = key3; } - json_object_set_new (json_element, iter_key, json_value); + json_object_set_new (json_element, iter_key, g_steal_pointer (&json_value)); done: - if (updated) { - _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") - && nm_streq0 (key2, "name"))); - g_free (*conf); - *conf = json_dumps (json, JSON_PRESERVE_ORDER); - /* Don't save an empty config */ - if (nm_streq0 (*conf, "{}")) { - g_free (*conf); - *conf = NULL; - } - } - json_decref (json); - return updated; + _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") + && nm_streq0 (key2, "name"))); + conf_new = json_dumps (json, JSON_PRESERVE_ORDER); + if (nm_streq0 (conf_new, "{}")) + nm_clear_g_free (&conf_new); + if (nm_streq0 (conf_new, *conf)) + return FALSE; + g_free (*conf); + *conf = g_steal_pointer (&conf_new); + return TRUE; } #else /* !WITH_JSON_VALIDATION */ @@ -6701,7 +6726,7 @@ nm_utils_base64secret_decode (const char *base64_key, base64_key_len = strlen (base64_key); - r = nm_sd_utils_unbase64mem (base64_key, base64_key_len, &bin_arr, &bin_len); + 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) { @@ -6740,3 +6765,166 @@ nm_utils_base64secret_normalize (const char *base64_key, nm_explicit_bzero (buf, required_key_len); return TRUE; } + +GVariant * +_nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + GVariantBuilder builder; + guint i; + + g_object_get (setting, property, &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); +} + +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; + + g_variant_lookup (vlan_var, "pvid", "b", &pvid); + if (pvid && vid_start != vid_end) + continue; + g_variant_lookup (vlan_var, "untagged", "b", &untagged); + + 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; +} + +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; +} -- cgit 1.3.0-6-gf8a5