diff options
Diffstat (limited to 'libnm-core/nm-utils.c')
| -rw-r--r-- | libnm-core/nm-utils.c | 452 |
1 files changed, 287 insertions, 165 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index fe8a3714..5b4566bf 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -667,10 +667,8 @@ _nm_utils_copy_object_array (const GPtrArray *array) return _nm_utils_copy_array (array, g_object_ref, g_object_unref); } -/* have @list of type 'gpointer *' instead of 'gconstpointer *' to - * reduce the necessity for annoying const-casts. */ gssize -_nm_utils_ptrarray_find_first (gpointer *list, gssize len, gconstpointer needle) +_nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer needle) { gssize i; @@ -694,7 +692,7 @@ _nm_utils_ptrarray_find_first (gpointer *list, gssize len, gconstpointer needle) } gssize -_nm_utils_ptrarray_find_binary_search (gpointer *list, gsize len, gpointer needle, GCompareDataFunc cmpfcn, gpointer user_data) +_nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data) { gssize imin, imax, imid; int cmp; @@ -726,6 +724,40 @@ _nm_utils_ptrarray_find_binary_search (gpointer *list, gsize len, gpointer needl return ~imin; } +gssize +_nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data) +{ + gssize imin, imax, imid; + int cmp; + + g_return_val_if_fail (list || !len, ~((gssize) 0)); + g_return_val_if_fail (cmpfcn, ~((gssize) 0)); + g_return_val_if_fail (elem_size > 0, ~((gssize) 0)); + + imin = 0; + if (len == 0) + return ~imin; + + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); + if (cmp == 0) + return imid; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + return ~imin; +} + GVariant * _nm_utils_bytes_to_dbus (const GValue *prop_value) { @@ -2112,27 +2144,37 @@ next: return routes; } -/**********************************************************************************************/ +/*****************************************************************************/ /** - * nm_utils_uuid_generate: + * nm_utils_uuid_generate_buf_: + * @buf: input buffer, must contain at least 37 bytes * - * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection - * object's #NMSettingConnection:id: property. Should be freed with g_free() + * Returns: generates a new random UUID, writes it to @buf and returns @buf. **/ char * -nm_utils_uuid_generate (void) +nm_utils_uuid_generate_buf_ (char *buf) { uuid_t uuid; - char *buf; - buf = g_malloc0 (37); uuid_generate_random (uuid); - uuid_unparse_lower (uuid, &buf[0]); + uuid_unparse_lower (uuid, buf); return 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: * @s: a string to use as the seed for the UUID * @slen: if negative, treat @s as zero terminated C string. @@ -2180,7 +2222,7 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g g_return_val_if_reached (NULL); } - buf = g_malloc0 (37); + buf = g_malloc (37); uuid_unparse_lower (uuid, &buf[0]); return buf; @@ -2228,7 +2270,7 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...) return uuid; } -/**********************************************************************************************/ +/*****************************************************************************/ /** * nm_utils_rsa_key_encrypt: @@ -2422,7 +2464,7 @@ nm_utils_file_is_pkcs12 (const char *filename) return crypto_is_pkcs12_file (filename, NULL); } -/**********************************************************************************************/ +/*****************************************************************************/ gboolean _nm_utils_check_file (const char *filename, @@ -2548,7 +2590,7 @@ _nm_utils_check_module_file (const char *name, error); } -/**********************************************************************************************/ +/*****************************************************************************/ /** * nm_utils_file_search_in_paths: @@ -2622,7 +2664,7 @@ NOT_FOUND: return NULL; } -/**********************************************************************************************/ +/*****************************************************************************/ /* Band, channel/frequency stuff for wireless */ struct cf_pair { @@ -2872,7 +2914,6 @@ nm_utils_wifi_2ghz_freqs (void) { return _wifi_freqs (TRUE); } -NM_BACKPORT_SYMBOL (libnm_1_0_6, const guint *, nm_utils_wifi_2ghz_freqs, (void), ()); /** * nm_utils_wifi_5ghz_freqs: @@ -2888,7 +2929,6 @@ nm_utils_wifi_5ghz_freqs (void) { return _wifi_freqs (FALSE); } -NM_BACKPORT_SYMBOL (libnm_1_0_6, const guint *, nm_utils_wifi_5ghz_freqs, (void), ()); /** * nm_utils_wifi_strength_bars: @@ -2974,10 +3014,16 @@ nm_utils_hwaddr_len (int type) } static guint8 * -hwaddr_aton (const char *asc, guint8 *buffer, gsize buffer_length, gsize *out_len) +_str2bin (const char *asc, + gboolean delimiter_required, + const char *delimiter_candidates, + guint8 *buffer, + gsize buffer_length, + gsize *out_len) { const char *in = asc; guint8 *out = buffer; + gboolean delimiter_has = TRUE; guint8 delimiter = '\0'; nm_assert (asc); @@ -2999,24 +3045,43 @@ hwaddr_aton (const char *asc, guint8 *buffer, gsize buffer_length, gsize *out_le if (d2 && g_ascii_isxdigit (d2)) { *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2); d2 = in[2]; - in += 3; + if (!d2) + break; + in += 2; } else { /* Fake leading zero */ *out++ = HEXVAL (d1); - in += 2; + if (!d2) { + if (!delimiter_has) { + /* when using no delimiter, there must be pairs of hex chars */ + return NULL; + } + break; + } + in += 1; } - if (!d2) - break; if (--buffer_length == 0) return NULL; - if (d2 != delimiter) { - if ( delimiter == '\0' - && (d2 == ':' || d2 == '-')) - delimiter = d2; - else - return NULL; + if (delimiter_has) { + if (d2 != delimiter) { + if (delimiter) + return NULL; + if (delimiter_candidates) { + while (delimiter_candidates[0]) { + if (delimiter_candidates++[0] == d2) + delimiter = d2; + } + } + if (!delimiter) { + if (delimiter_required) + return NULL; + delimiter_has = FALSE; + continue; + } + } + in++; } } @@ -3024,6 +3089,40 @@ hwaddr_aton (const char *asc, guint8 *buffer, gsize buffer_length, gsize *out_le return buffer; } +#define hwaddr_aton(asc, buffer, buffer_length, out_len) _str2bin ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len)) + +/** + * 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 buffer_length, len; + + g_return_val_if_fail (hex != NULL, NULL); + + if (hex[0] == '0' && hex[1] == 'x') + hex += 2; + + buffer_length = strlen (hex) / 2 + 3; + buffer = g_malloc (buffer_length); + if (!_str2bin (hex, FALSE, ":", buffer, buffer_length, &len)) { + g_free (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 @@ -3117,7 +3216,7 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length) } static void -_bin2str_buf (gconstpointer addr, gsize length, gboolean upper_case, char *out) +_bin2str (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out) { const guint8 *in = addr; const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; @@ -3126,7 +3225,8 @@ _bin2str_buf (gconstpointer addr, gsize length, gboolean upper_case, char *out) nm_assert (out); nm_assert (length > 0); - /* @out must contain at least @length*3 bytes */ + /* @out must contain at least @length*3 bytes if @delimiter is set, + * otherwise, @length*2+1. */ for (;;) { const guint8 v = *in++; @@ -3136,22 +3236,42 @@ _bin2str_buf (gconstpointer addr, gsize length, gboolean upper_case, char *out) length--; if (!length) break; - *out++ = ':'; + if (delimiter) + *out++ = delimiter; } *out = 0; } -static char * -_bin2str (gconstpointer addr, gsize length, gboolean upper_case) +/** + * 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; - nm_assert (addr); - nm_assert (length > 0); + 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); + _bin2str (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'; - result = g_malloc (length * 3); - _bin2str_buf (addr, length, upper_case, result); return result; } @@ -3167,10 +3287,14 @@ _bin2str (gconstpointer addr, gsize length, gboolean upper_case) 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 ("")); - return _bin2str (addr, length, TRUE); + result = g_malloc (length * 3); + _bin2str (addr, length, ':', TRUE, result); + return result; } const char * @@ -3182,7 +3306,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); - _bin2str_buf (addr, addr_len, TRUE, buf); + _bin2str (addr, addr_len, ':', TRUE, buf); return buf; } @@ -3199,10 +3323,14 @@ nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_cas 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 ("")); - return _bin2str (addr, length, upper_case); + result = g_malloc (length * 3); + _bin2str (addr, length, ':', upper_case, result); + return result; } /** @@ -3360,7 +3488,6 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, if (l != hwaddr1_len) return FALSE; hwaddr2 = buf2; - hwaddr2_len = hwaddr1_len; } else { g_return_val_if_fail (hwaddr2_len > 0 && hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE); @@ -3376,7 +3503,7 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, if (hwaddr1_len == INFINIBAND_ALEN) { hwaddr1 = (guint8 *)hwaddr1 + INFINIBAND_ALEN - 8; hwaddr2 = (guint8 *)hwaddr2 + INFINIBAND_ALEN - 8; - hwaddr1_len = hwaddr2_len = 8; + hwaddr1_len = 8; } return !memcmp (hwaddr1, hwaddr2, hwaddr1_len); @@ -3478,7 +3605,7 @@ _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, * to "" and it just worked as the value was not serialized in * an ill form. * - * To preserve that behavior, seralize "" as NULL. + * To preserve that behavior, serialize "" as NULL. */ return addr && addr[0] ? g_variant_new_string (addr) : NULL; @@ -3610,136 +3737,66 @@ _nm_utils_generate_mac_address_mask_parse (const char *value, /*****************************************************************************/ /** - * 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 + * nm_utils_is_valid_iface_name: + * @name: Name of interface + * @error: location to store the error occurring, or %NULL to ignore * - * 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'), + * Validate the network interface name. * - * Return value: (transfer full): the textual form of @bytes - */ -/* - * Code originally by Alex Larsson <alexl@redhat.com> and - * copyright Red Hat, Inc. under terms of the LGPL. - */ -char * -nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len) -{ - static char hex_digits[] = "0123456789abcdef"; - const guint8 *bytes = src; - char *result; - int i; - gsize buflen = (len * 2) + 1; - - g_return_val_if_fail (bytes != NULL, NULL); - g_return_val_if_fail (len > 0, NULL); - g_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */ - if (final_len > -1) - g_return_val_if_fail (final_len < buflen, NULL); - - result = g_malloc0 (buflen); - for (i = 0; i < len; i++) { - result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf]; - result[2*i+1] = hex_digits[bytes[i] & 0xf]; - } - /* Cut converted key off at the correct length for this cipher type */ - if (final_len > -1) - result[final_len] = '\0'; - else - result[buflen - 1] = '\0'; - - return result; -} - -/** - * 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 ':'. + * This function is a 1:1 copy of the kernel's interface validation + * function in net/core/dev.c. * - * Return value: (transfer full): the converted bytes, or %NULL on error + * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned. */ -GBytes * -nm_utils_hexstr2bin (const char *hex) +gboolean +nm_utils_is_valid_iface_name (const char *name, GError **error) { - guint i = 0, x = 0; - gs_free guint8 *c = NULL; - int a, b; - gboolean found_colon = FALSE; - - g_return_val_if_fail (hex != NULL, NULL); + g_return_val_if_fail (name != NULL, FALSE); - if (strncasecmp (hex, "0x", 2) == 0) - hex += 2; - found_colon = !!strchr (hex, ':'); + if (*name == '\0') { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("interface name is too short")); + return FALSE; + } - c = g_malloc (strlen (hex) / 2 + 1); - for (;;) { - a = g_ascii_xdigit_value (hex[i++]); - if (a < 0) - return NULL; + if (strlen (name) >= 16) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("interface name is longer than 15 characters")); + return FALSE; + } - if (hex[i] && hex[i] != ':') { - b = g_ascii_xdigit_value (hex[i++]); - if (b < 0) - return NULL; - c[x++] = ((guint) a << 4) | ((guint) b); - } else - c[x++] = (guint) a; + if (!strcmp (name, ".") || !strcmp (name, "..")) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("interface name is reserved")); + return FALSE; + } - if (!hex[i]) - break; - if (hex[i] == ':') { - if (!hex[i + 1]) { - /* trailing ':' is invalid */ - return NULL; - } - i++; - } else if (found_colon) { - /* If colons exist, they must delimit 1 or 2 hex chars */ - return NULL; + while (*name) { + if (*name == '/' || g_ascii_isspace (*name)) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("interface name contains an invalid character")); + return FALSE; } + name++; } - return g_bytes_new (c, x); + return TRUE; } /** * nm_utils_iface_valid_name: * @name: Name of interface * - * This function is a 1:1 copy of the kernel's interface validation - * function in net/core/dev.c. + * Validate the network interface name. + * + * Deprecated: 1.6: use nm_utils_is_valid_iface_name() insteead, with better error reporting. * * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned. */ gboolean nm_utils_iface_valid_name (const char *name) { - g_return_val_if_fail (name != NULL, FALSE); - - if (*name == '\0') - return FALSE; - - if (strlen (name) >= 16) - return FALSE; - - if (!strcmp (name, ".") || !strcmp (name, "..")) - return FALSE; - - while (*name) { - if (*name == '/' || g_ascii_isspace (*name)) - return FALSE; - name++; - } - - return TRUE; + return nm_utils_is_valid_iface_name (name, NULL); } /** @@ -4010,7 +4067,7 @@ nm_utils_bond_mode_string_to_int (const char *mode) return -1; } -/**********************************************************************************************/ +/*****************************************************************************/ #define STRSTRDICTKEY_V1_SET 0x01 #define STRSTRDICTKEY_V2_SET 0x02 @@ -4286,8 +4343,6 @@ char *nm_utils_enum_to_str (GType type, int value) g_type_class_unref (class); return ret; } -NM_BACKPORT_SYMBOL (libnm_1_0_6, char *, nm_utils_enum_to_str, - (GType type, int value), (type, value)); /** * nm_utils_enum_from_str: @@ -4359,9 +4414,6 @@ gboolean nm_utils_enum_from_str (GType type, const char *str, g_type_class_unref (class); return ret; } -NM_BACKPORT_SYMBOL (libnm_1_0_6, gboolean, nm_utils_enum_from_str, - (GType type, const char *str, int *out_value, char **err_token), - (type, str, out_value, err_token)); /** * nm_utils_enum_get_values: @@ -4416,8 +4468,20 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to) } #if WITH_JANSSON +/** + * 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_check_valid_json (const char *str, GError **error) +nm_utils_is_json_object (const char *str, GError **error) { json_t *json; json_error_t jerror; @@ -4428,7 +4492,7 @@ _nm_utils_check_valid_json (const char *str, GError **error) g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - "value is NULL or empty"); + str ? _("value is NULL") : _("value is empty")); return FALSE; } @@ -4437,9 +4501,19 @@ _nm_utils_check_valid_json (const char *str, GError **error) g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - "%s at position %d", - jerror.text, - jerror.position); + _("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 (!json_is_object (json)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("is not a JSON object")); return FALSE; } @@ -4531,17 +4605,48 @@ out: #else /* WITH_JANSSON */ gboolean -_nm_utils_check_valid_json (const char *str, GError **error) +nm_utils_is_json_object (const char *str, GError **error) { + g_return_val_if_fail (!error || !*error, FALSE); + + if (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++; + } + if (!str || !str[0]) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - "value is NULL or empty"); + str ? _("value is NULL") : _("value is empty")); return FALSE; } - return TRUE; + /* 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; } gboolean @@ -4552,3 +4657,20 @@ _nm_utils_team_config_equal (const char *conf1, return nm_streq0 (conf1, conf2); } #endif + +/*****************************************************************************/ + +/** + * 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; +} + |