2680 lines
75 KiB
C
2680 lines
75 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* Ray Strode <rstrode@redhat.com>
|
|
* Dan Williams <dcbw@redhat.com>
|
|
* Tambet Ingo <tambet@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
* (C) Copyright 2005 - 2012 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/ether.h>
|
|
#include <linux/if_infiniband.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib-object.h>
|
|
#include <glib/gi18n.h>
|
|
#include <dbus/dbus-glib.h>
|
|
#include <uuid/uuid.h>
|
|
|
|
#include "nm-utils.h"
|
|
#include "nm-utils-private.h"
|
|
#include "NetworkManager.h"
|
|
#include "nm-dbus-glib-types.h"
|
|
#include "nm-setting-ip4-config.h"
|
|
#include "nm-setting-ip6-config.h"
|
|
#include "crypto.h"
|
|
|
|
/**
|
|
* SECTION:nm-utils
|
|
* @short_description: Utility functions
|
|
* @include: nm-utils.h
|
|
*
|
|
* A collection of utility functions for working SSIDs, IP addresses, WiFi
|
|
* access points and devices, among other things.
|
|
*/
|
|
|
|
struct EncodingTriplet
|
|
{
|
|
const char *encoding1;
|
|
const char *encoding2;
|
|
const char *encoding3;
|
|
};
|
|
|
|
struct IsoLangToEncodings
|
|
{
|
|
const char * lang;
|
|
struct EncodingTriplet encodings;
|
|
};
|
|
|
|
/* 5-letter language codes */
|
|
static const struct IsoLangToEncodings isoLangEntries5[] =
|
|
{
|
|
/* Simplified Chinese */
|
|
{ "zh_cn", {"euc-cn", "gb2312", "gb18030"} }, /* PRC */
|
|
{ "zh_sg", {"euc-cn", "gb2312", "gb18030"} }, /* Singapore */
|
|
|
|
/* Traditional Chinese */
|
|
{ "zh_tw", {"big5", "euc-tw", NULL} }, /* Taiwan */
|
|
{ "zh_hk", {"big5", "euc-tw", "big5-hkcs"} },/* Hong Kong */
|
|
{ "zh_mo", {"big5", "euc-tw", NULL} }, /* Macau */
|
|
|
|
/* Table end */
|
|
{ NULL, {NULL, NULL, NULL} }
|
|
};
|
|
|
|
/* 2-letter language codes; we don't care about the other 3 in this table */
|
|
static const struct IsoLangToEncodings isoLangEntries2[] =
|
|
{
|
|
/* Japanese */
|
|
{ "ja", {"euc-jp", "shift_jis", "iso-2022-jp"} },
|
|
|
|
/* Korean */
|
|
{ "ko", {"euc-kr", "iso-2022-kr", "johab"} },
|
|
|
|
/* Thai */
|
|
{ "th", {"iso-8859-11","windows-874", NULL} },
|
|
|
|
/* Central European */
|
|
{ "hu", {"iso-8859-2", "windows-1250", NULL} }, /* Hungarian */
|
|
{ "cs", {"iso-8859-2", "windows-1250", NULL} }, /* Czech */
|
|
{ "hr", {"iso-8859-2", "windows-1250", NULL} }, /* Croatian */
|
|
{ "pl", {"iso-8859-2", "windows-1250", NULL} }, /* Polish */
|
|
{ "ro", {"iso-8859-2", "windows-1250", NULL} }, /* Romanian */
|
|
{ "sk", {"iso-8859-2", "windows-1250", NULL} }, /* Slovakian */
|
|
{ "sl", {"iso-8859-2", "windows-1250", NULL} }, /* Slovenian */
|
|
{ "sh", {"iso-8859-2", "windows-1250", NULL} }, /* Serbo-Croatian */
|
|
|
|
/* Cyrillic */
|
|
{ "ru", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Russian */
|
|
{ "be", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Belorussian */
|
|
{ "bg", {"windows-1251","koi8-r", "iso-8859-5"} }, /* Bulgarian */
|
|
{ "mk", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Macedonian */
|
|
{ "sr", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Serbian */
|
|
{ "uk", {"koi8-u", "koi8-r", "windows-1251"} }, /* Ukranian */
|
|
|
|
/* Arabic */
|
|
{ "ar", {"iso-8859-6", "windows-1256", NULL} },
|
|
|
|
/* Baltic */
|
|
{ "et", {"iso-8859-4", "windows-1257", NULL} }, /* Estonian */
|
|
{ "lt", {"iso-8859-4", "windows-1257", NULL} }, /* Lithuanian */
|
|
{ "lv", {"iso-8859-4", "windows-1257", NULL} }, /* Latvian */
|
|
|
|
/* Greek */
|
|
{ "el", {"iso-8859-7", "windows-1253", NULL} },
|
|
|
|
/* Hebrew */
|
|
{ "he", {"iso-8859-8", "windows-1255", NULL} },
|
|
{ "iw", {"iso-8859-8", "windows-1255", NULL} },
|
|
|
|
/* Turkish */
|
|
{ "tr", {"iso-8859-9", "windows-1254", NULL} },
|
|
|
|
/* Table end */
|
|
{ NULL, {NULL, 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 (g_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 (g_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,
|
|
char **encoding1,
|
|
char **encoding2,
|
|
char **encoding3)
|
|
{
|
|
struct EncodingTriplet * encodings;
|
|
gboolean success = FALSE;
|
|
char * tmp_lang;
|
|
|
|
g_return_val_if_fail (lang != NULL, FALSE);
|
|
g_return_val_if_fail (encoding1 != NULL, FALSE);
|
|
g_return_val_if_fail (encoding2 != NULL, FALSE);
|
|
g_return_val_if_fail (encoding3 != NULL, FALSE);
|
|
|
|
*encoding1 = "iso-8859-1";
|
|
*encoding2 = "windows-1251";
|
|
*encoding3 = NULL;
|
|
|
|
init_lang_to_encodings_hash ();
|
|
|
|
tmp_lang = g_strdup (lang);
|
|
if ((encodings = g_hash_table_lookup (langToEncodings5, tmp_lang)))
|
|
{
|
|
*encoding1 = (char *) encodings->encoding1;
|
|
*encoding2 = (char *) encodings->encoding2;
|
|
*encoding3 = (char *) encodings->encoding3;
|
|
success = TRUE;
|
|
}
|
|
|
|
/* Truncate tmp_lang to length of 2 */
|
|
if (strlen (tmp_lang) > 2)
|
|
tmp_lang[2] = '\0';
|
|
if (!success && (encodings = g_hash_table_lookup (langToEncodings2, tmp_lang)))
|
|
{
|
|
*encoding1 = (char *) encodings->encoding1;
|
|
*encoding2 = (char *) encodings->encoding2;
|
|
*encoding3 = (char *) encodings->encoding3;
|
|
success = TRUE;
|
|
}
|
|
|
|
g_free (tmp_lang);
|
|
return success;
|
|
}
|
|
|
|
/* init, deinit for libnm_util */
|
|
|
|
static gboolean initialized = FALSE;
|
|
|
|
/**
|
|
* nm_utils_init:
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Initializes libnm-util; should be called when starting and program that
|
|
* uses libnm-util. Sets up an atexit() handler to ensure de-initialization
|
|
* is performed, but calling nm_utils_deinit() to explicitly deinitialize
|
|
* libnm-util can also be done. This function can be called more than once.
|
|
*
|
|
* Returns: TRUE if the initialization was successful, FALSE on failure.
|
|
**/
|
|
gboolean
|
|
nm_utils_init (GError **error)
|
|
{
|
|
if (!initialized) {
|
|
initialized = TRUE;
|
|
|
|
if (!crypto_init (error))
|
|
return FALSE;
|
|
|
|
_nm_utils_register_value_transformations ();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_deinit:
|
|
*
|
|
* Frees all resources used internally by libnm-util. This function is called
|
|
* from an atexit() handler, set up by nm_utils_init(), but is safe to be called
|
|
* more than once. Subsequent calls have no effect until nm_utils_init() is
|
|
* called again.
|
|
**/
|
|
void
|
|
nm_utils_deinit (void)
|
|
{
|
|
if (initialized) {
|
|
crypto_deinit ();
|
|
initialized = FALSE;
|
|
}
|
|
}
|
|
|
|
/* ssid helpers */
|
|
|
|
/**
|
|
* nm_utils_ssid_to_utf8:
|
|
* @ssid: a byte array containing the SSID data
|
|
*
|
|
* WiFi 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 GByteArray *ssid)
|
|
{
|
|
char *converted = NULL;
|
|
char *lang, *e1 = NULL, *e2 = NULL, *e3 = NULL;
|
|
|
|
g_return_val_if_fail (ssid != NULL, NULL);
|
|
|
|
if (g_utf8_validate ((const gchar *) ssid->data, ssid->len, NULL))
|
|
return g_strndup ((const gchar *) ssid->data, ssid->len);
|
|
|
|
/* LANG may be a good encoding hint */
|
|
g_get_charset ((const char **)(&e1));
|
|
if ((lang = getenv ("LANG"))) {
|
|
char * dot;
|
|
|
|
lang = g_ascii_strdown (lang, -1);
|
|
if ((dot = strchr (lang, '.')))
|
|
*dot = '\0';
|
|
|
|
get_encodings_for_lang (lang, &e1, &e2, &e3);
|
|
g_free (lang);
|
|
}
|
|
|
|
converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e1, NULL, NULL, NULL);
|
|
if (!converted && e2)
|
|
converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e2, NULL, NULL, NULL);
|
|
|
|
if (!converted && e3)
|
|
converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e3, NULL, NULL, NULL);
|
|
|
|
if (!converted) {
|
|
converted = g_convert_with_fallback ((const gchar *) ssid->data, ssid->len,
|
|
"UTF-8", e1, "?", NULL, NULL, NULL);
|
|
}
|
|
|
|
return converted;
|
|
}
|
|
|
|
/* Shamelessly ripped from the Linux kernel ieee80211 stack */
|
|
/**
|
|
* nm_utils_is_empty_ssid:
|
|
* @ssid: 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, int 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;
|
|
}
|
|
|
|
#define ESSID_MAX_SIZE 32
|
|
|
|
/**
|
|
* nm_utils_escape_ssid:
|
|
* @ssid: 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, guint32 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;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_same_ssid:
|
|
* @ssid1: first SSID data to compare
|
|
* @ssid2: second SSID data to compare
|
|
* @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 GByteArray * ssid1,
|
|
const GByteArray * ssid2,
|
|
gboolean ignore_trailing_null)
|
|
{
|
|
guint32 ssid1_len, ssid2_len;
|
|
|
|
if (ssid1 == ssid2)
|
|
return TRUE;
|
|
if ((ssid1 && !ssid2) || (!ssid1 && ssid2))
|
|
return FALSE;
|
|
|
|
ssid1_len = ssid1->len;
|
|
ssid2_len = ssid2->len;
|
|
if (ssid1_len && ssid2_len && ignore_trailing_null) {
|
|
if (ssid1->data[ssid1_len - 1] == '\0')
|
|
ssid1_len--;
|
|
if (ssid2->data[ssid2_len - 1] == '\0')
|
|
ssid2_len--;
|
|
}
|
|
|
|
if (ssid1_len != ssid2_len)
|
|
return FALSE;
|
|
|
|
return memcmp (ssid1->data, ssid2->data, ssid1_len) == 0 ? TRUE : FALSE;
|
|
}
|
|
|
|
static void
|
|
value_destroy (gpointer data)
|
|
{
|
|
GValue *value = (GValue *) data;
|
|
|
|
g_value_unset (value);
|
|
g_slice_free (GValue, value);
|
|
}
|
|
|
|
static void
|
|
value_dup (gpointer key, gpointer val, gpointer user_data)
|
|
{
|
|
GHashTable *table = (GHashTable *) user_data;
|
|
GValue *value = (GValue *) val;
|
|
GValue *dup_value;
|
|
|
|
dup_value = g_slice_new0 (GValue);
|
|
g_value_init (dup_value, G_VALUE_TYPE (val));
|
|
g_value_copy (value, dup_value);
|
|
|
|
g_hash_table_insert (table, g_strdup ((char *) key), dup_value);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_gvalue_hash_dup:
|
|
* @hash: a #GHashTable mapping string:GValue
|
|
*
|
|
* Utility function to duplicate a hash table of GValues.
|
|
*
|
|
* Returns: (transfer container) (element-type utf8 GObject.Value): a newly allocated duplicated #GHashTable, caller must free the
|
|
* returned hash with g_hash_table_unref() or g_hash_table_destroy()
|
|
**/
|
|
GHashTable *
|
|
nm_utils_gvalue_hash_dup (GHashTable *hash)
|
|
{
|
|
GHashTable *table;
|
|
|
|
g_return_val_if_fail (hash != NULL, NULL);
|
|
|
|
table = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
(GDestroyNotify) g_free,
|
|
value_destroy);
|
|
|
|
g_hash_table_foreach (hash, value_dup, table);
|
|
|
|
return table;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_slist_free: (skip)
|
|
* @list: a #GSList
|
|
* @elem_destroy_fn: user function called for each element in @list
|
|
*
|
|
* Utility function to free a #GSList.
|
|
**/
|
|
void
|
|
nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn)
|
|
{
|
|
if (!list)
|
|
return;
|
|
|
|
if (elem_destroy_fn)
|
|
g_slist_foreach (list, (GFunc) elem_destroy_fn, NULL);
|
|
|
|
g_slist_free (list);
|
|
}
|
|
|
|
gboolean
|
|
_nm_utils_string_in_list (const char *str, const char **valid_strings)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; valid_strings[i]; i++)
|
|
if (strcmp (str, valid_strings[i]) == 0)
|
|
break;
|
|
|
|
return valid_strings[i] != NULL;
|
|
}
|
|
|
|
gboolean
|
|
_nm_utils_string_slist_validate (GSList *list, const char **valid_values)
|
|
{
|
|
GSList *iter;
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
if (!_nm_utils_string_in_list ((char *) iter->data, valid_values))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_op_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_OBJECT_PATH));
|
|
|
|
g_value_set_string (dest_value, (const char *) g_value_get_boxed (src_value));
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_strv_to_slist (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
char **str;
|
|
GSList *list = NULL;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV));
|
|
|
|
str = (char **) g_value_get_boxed (src_value);
|
|
|
|
while (str && str[i])
|
|
list = g_slist_prepend (list, g_strdup (str[i++]));
|
|
|
|
g_value_take_boxed (dest_value, g_slist_reverse (list));
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_strv_to_ptrarray (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
char **str;
|
|
GPtrArray *array = NULL;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV));
|
|
|
|
str = (char **) g_value_get_boxed (src_value);
|
|
|
|
array = g_ptr_array_sized_new (3);
|
|
while (str && str[i])
|
|
g_ptr_array_add (array, g_strdup (str[i++]));
|
|
|
|
g_value_take_boxed (dest_value, array);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_strv_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GSList *strings;
|
|
GString *printable;
|
|
GSList *iter;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_LIST_OF_STRING));
|
|
|
|
strings = (GSList *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
for (iter = strings; iter; iter = g_slist_next (iter)) {
|
|
if (iter != strings)
|
|
g_string_append (printable, ", '");
|
|
else
|
|
g_string_append_c (printable, '\'');
|
|
g_string_append (printable, iter->data);
|
|
g_string_append_c (printable, '\'');
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_string_array_to_string (const GPtrArray *strings, GValue *dest_value)
|
|
{
|
|
GString *printable;
|
|
int i;
|
|
|
|
printable = g_string_new ("[");
|
|
for (i = 0; strings && i < strings->len; i++) {
|
|
if (i > 0)
|
|
g_string_append (printable, ", '");
|
|
else
|
|
g_string_append_c (printable, '\'');
|
|
g_string_append (printable, g_ptr_array_index (strings, i));
|
|
g_string_append_c (printable, '\'');
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_string_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
const GPtrArray *strings;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_STRING));
|
|
|
|
strings = (const GPtrArray *) g_value_get_boxed (src_value);
|
|
_string_array_to_string (strings, dest_value);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_op_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
const GPtrArray *strings;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH));
|
|
|
|
strings = (const GPtrArray *) g_value_get_boxed (src_value);
|
|
_string_array_to_string (strings, dest_value);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_uint_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GArray *array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UINT_ARRAY));
|
|
|
|
array = (GArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
while (array && (i < array->len)) {
|
|
char buf[INET_ADDRSTRLEN + 1];
|
|
struct in_addr addr;
|
|
|
|
if (i > 0)
|
|
g_string_append (printable, ", ");
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
addr.s_addr = g_array_index (array, guint32, i++);
|
|
if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
|
|
g_warning ("%s: error converting IP4 address 0x%X",
|
|
__func__, ntohl (addr.s_addr));
|
|
g_string_append_printf (printable, "%u (%s)", addr.s_addr, buf);
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_ip4_addr_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GPtrArray *ptr_array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT));
|
|
|
|
ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
while (ptr_array && (i < ptr_array->len)) {
|
|
GArray *array;
|
|
char buf[INET_ADDRSTRLEN + 1];
|
|
struct in_addr addr;
|
|
gboolean is_addr; /* array contains address x route */
|
|
|
|
if (i > 0)
|
|
g_string_append (printable, ", ");
|
|
|
|
g_string_append (printable, "{ ");
|
|
array = (GArray *) g_ptr_array_index (ptr_array, i++);
|
|
if (array->len < 2) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
is_addr = (array->len < 4);
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
addr.s_addr = g_array_index (array, guint32, 0);
|
|
if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
|
|
g_warning ("%s: error converting IP4 address 0x%X",
|
|
__func__, ntohl (addr.s_addr));
|
|
if (is_addr)
|
|
g_string_append_printf (printable, "ip = %s", buf);
|
|
else
|
|
g_string_append_printf (printable, "dst = %s", buf);
|
|
g_string_append (printable, ", ");
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
g_string_append_printf (printable, "px = %u",
|
|
g_array_index (array, guint32, 1));
|
|
|
|
if (array->len > 2) {
|
|
g_string_append (printable, ", ");
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
addr.s_addr = g_array_index (array, guint32, 2);
|
|
if (!inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN))
|
|
g_warning ("%s: error converting IP4 address 0x%X",
|
|
__func__, ntohl (addr.s_addr));
|
|
if (is_addr)
|
|
g_string_append_printf (printable, "gw = %s", buf);
|
|
else
|
|
g_string_append_printf (printable, "nh = %s", buf);
|
|
}
|
|
|
|
if (array->len > 3) {
|
|
g_string_append (printable, ", ");
|
|
|
|
memset (buf, 0, sizeof (buf));
|
|
g_string_append_printf (printable, "mt = %u",
|
|
g_array_index (array, guint32, 3));
|
|
}
|
|
|
|
g_string_append (printable, " }");
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
convert_one_gvalue_hash_entry (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
GString *printable = (GString *) user_data;
|
|
char *value_as_string;
|
|
|
|
value_as_string = g_strdup_value_contents ((GValue *) value);
|
|
g_string_append_printf (printable, " { '%s': %s },", (const char *) key, value_as_string);
|
|
g_free (value_as_string);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_gvalue_hash_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GHashTable *hash;
|
|
GString *printable;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_VARIANT));
|
|
|
|
hash = (GHashTable *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
g_hash_table_foreach (hash, convert_one_gvalue_hash_entry, printable);
|
|
g_string_append (printable, " ]");
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
convert_one_string_hash_entry (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
GString *printable = (GString *) user_data;
|
|
|
|
g_string_append_printf (printable, " { '%s': %s },", (const char *) key, (const char *) value);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GHashTable *hash;
|
|
GString *printable;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_STRING));
|
|
|
|
hash = (GHashTable *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
if (hash)
|
|
g_hash_table_foreach (hash, convert_one_string_hash_entry, printable);
|
|
g_string_append (printable, " ]");
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_byte_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GArray *array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UCHAR_ARRAY));
|
|
|
|
array = (GArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
if (array) {
|
|
while (i < MIN (array->len, 35)) {
|
|
if (i > 0)
|
|
g_string_append_c (printable, ' ');
|
|
g_string_append_printf (printable, "0x%02X",
|
|
g_array_index (array, unsigned char, i++));
|
|
}
|
|
if (i < array->len)
|
|
g_string_append (printable, " ... ");
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static gboolean
|
|
_nm_utils_inet6_ntop (struct in6_addr *addr, char *buf)
|
|
{
|
|
if (!inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN)) {
|
|
int i;
|
|
GString *ip6_str = g_string_new (NULL);
|
|
g_string_append_printf (ip6_str, "%02X", addr->s6_addr[0]);
|
|
for (i = 1; i < 16; i++)
|
|
g_string_append_printf (ip6_str, " %02X", addr->s6_addr[i]);
|
|
g_warning ("%s: error converting IP6 address %s",
|
|
__func__, ip6_str->str);
|
|
g_string_free (ip6_str, TRUE);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_ip6_dns_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GPtrArray *ptr_array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR));
|
|
|
|
ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
while (ptr_array && (i < ptr_array->len)) {
|
|
GByteArray *bytearray;
|
|
char buf[INET6_ADDRSTRLEN];
|
|
struct in6_addr *addr;
|
|
|
|
if (i > 0)
|
|
g_string_append (printable, ", ");
|
|
|
|
bytearray = (GByteArray *) g_ptr_array_index (ptr_array, i++);
|
|
if (bytearray->len != 16) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
addr = (struct in6_addr *) bytearray->data;
|
|
memset (buf, 0, sizeof (buf));
|
|
_nm_utils_inet6_ntop (addr, buf);
|
|
g_string_append_printf (printable, "%s", buf);
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_ip6_addr_struct_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GPtrArray *ptr_array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
|
|
|
|
ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
while (ptr_array && (i < ptr_array->len)) {
|
|
GValueArray *elements;
|
|
GValue *tmp;
|
|
GByteArray *ba_addr;
|
|
char buf[INET6_ADDRSTRLEN];
|
|
struct in6_addr *addr;
|
|
guint32 prefix;
|
|
|
|
if (i > 0)
|
|
g_string_append (printable, ", ");
|
|
|
|
g_string_append (printable, "{ ");
|
|
elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
|
|
if ( (elements->n_values != 3)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)) {
|
|
g_string_append (printable, "invalid }");
|
|
continue;
|
|
}
|
|
|
|
/* IPv6 address */
|
|
tmp = g_value_array_get_nth (elements, 0);
|
|
ba_addr = g_value_get_boxed (tmp);
|
|
if (ba_addr->len != 16) {
|
|
g_string_append (printable, "invalid }");
|
|
continue;
|
|
}
|
|
addr = (struct in6_addr *) ba_addr->data;
|
|
memset (buf, 0, sizeof (buf));
|
|
_nm_utils_inet6_ntop (addr, buf);
|
|
g_string_append_printf (printable, "ip = %s", buf);
|
|
g_string_append (printable, ", ");
|
|
|
|
/* Prefix */
|
|
tmp = g_value_array_get_nth (elements, 1);
|
|
prefix = g_value_get_uint (tmp);
|
|
if (prefix > 128) {
|
|
g_string_append (printable, "invalid }");
|
|
continue;
|
|
}
|
|
g_string_append_printf (printable, "px = %u", prefix);
|
|
g_string_append (printable, ", ");
|
|
|
|
/* IPv6 Gateway */
|
|
tmp = g_value_array_get_nth (elements, 2);
|
|
ba_addr = g_value_get_boxed (tmp);
|
|
if (ba_addr->len != 16) {
|
|
g_string_append (printable, "invalid }");
|
|
continue;
|
|
}
|
|
addr = (struct in6_addr *) ba_addr->data;
|
|
memset (buf, 0, sizeof (buf));
|
|
_nm_utils_inet6_ntop (addr, buf);
|
|
g_string_append_printf (printable, "gw = %s", buf);
|
|
g_string_append (printable, " }");
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
static void
|
|
_nm_utils_convert_ip6_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
|
|
{
|
|
GPtrArray *ptr_array;
|
|
GString *printable;
|
|
guint i = 0;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE));
|
|
|
|
ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
|
|
|
|
printable = g_string_new ("[");
|
|
while (ptr_array && (i < ptr_array->len)) {
|
|
GValueArray *elements;
|
|
GValue *tmp;
|
|
GByteArray *ba_addr;
|
|
char buf[INET6_ADDRSTRLEN];
|
|
struct in6_addr *addr;
|
|
guint32 prefix, metric;
|
|
|
|
if (i > 0)
|
|
g_string_append (printable, ", ");
|
|
|
|
g_string_append (printable, "{ ");
|
|
elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
|
|
if ( (elements->n_values != 4)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 3)) != G_TYPE_UINT)) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
|
|
/* Destination address */
|
|
tmp = g_value_array_get_nth (elements, 0);
|
|
ba_addr = g_value_get_boxed (tmp);
|
|
if (ba_addr->len != 16) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
addr = (struct in6_addr *) ba_addr->data;
|
|
memset (buf, 0, sizeof (buf));
|
|
_nm_utils_inet6_ntop (addr, buf);
|
|
g_string_append_printf (printable, "dst = %s", buf);
|
|
g_string_append (printable, ", ");
|
|
|
|
/* Prefix */
|
|
tmp = g_value_array_get_nth (elements, 1);
|
|
prefix = g_value_get_uint (tmp);
|
|
if (prefix > 128) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
g_string_append_printf (printable, "px = %u", prefix);
|
|
g_string_append (printable, ", ");
|
|
|
|
/* Next hop addresses */
|
|
tmp = g_value_array_get_nth (elements, 2);
|
|
ba_addr = g_value_get_boxed (tmp);
|
|
if (ba_addr->len != 16) {
|
|
g_string_append (printable, "invalid");
|
|
continue;
|
|
}
|
|
addr = (struct in6_addr *) ba_addr->data;
|
|
memset (buf, 0, sizeof (buf));
|
|
_nm_utils_inet6_ntop (addr, buf);
|
|
g_string_append_printf (printable, "nh = %s", buf);
|
|
g_string_append (printable, ", ");
|
|
|
|
/* Metric */
|
|
tmp = g_value_array_get_nth (elements, 3);
|
|
metric = g_value_get_uint (tmp);
|
|
g_string_append_printf (printable, "mt = %u", metric);
|
|
|
|
g_string_append (printable, " }");
|
|
}
|
|
g_string_append_c (printable, ']');
|
|
|
|
g_value_take_string (dest_value, printable->str);
|
|
g_string_free (printable, FALSE);
|
|
}
|
|
|
|
#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
|
|
#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS))
|
|
|
|
static void
|
|
_nm_utils_convert_old_ip6_addr_array (const GValue *src_value, GValue *dst_value)
|
|
{
|
|
GPtrArray *src_outer_array;
|
|
GPtrArray *dst_outer_array;
|
|
guint i;
|
|
|
|
g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
|
|
|
|
src_outer_array = (GPtrArray *) g_value_get_boxed (src_value);
|
|
dst_outer_array = g_ptr_array_new ();
|
|
|
|
for (i = 0; src_outer_array && (i < src_outer_array->len); i++) {
|
|
GValueArray *src_addr_array;
|
|
GValueArray *dst_addr_array;
|
|
GValue element = {0, };
|
|
GValue *src_addr, *src_prefix;
|
|
GByteArray *ba;
|
|
|
|
src_addr_array = (GValueArray *) g_ptr_array_index (src_outer_array, i);
|
|
|
|
if ( (src_addr_array->n_values != 2)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (src_addr_array, 1)) != G_TYPE_UINT)) {
|
|
g_warning ("%s: invalid old IPv6 address type", __func__);
|
|
return;
|
|
}
|
|
|
|
dst_addr_array = g_value_array_new (3);
|
|
|
|
src_addr = g_value_array_get_nth (src_addr_array, 0);
|
|
g_value_array_append (dst_addr_array, src_addr);
|
|
src_prefix = g_value_array_get_nth (src_addr_array, 1);
|
|
g_value_array_append (dst_addr_array, src_prefix);
|
|
|
|
/* Blank Gateway */
|
|
g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
|
|
ba = g_byte_array_new ();
|
|
g_byte_array_append (ba, (guint8 *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
|
|
g_value_take_boxed (&element, ba);
|
|
g_value_array_append (dst_addr_array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_ptr_array_add (dst_outer_array, dst_addr_array);
|
|
}
|
|
|
|
g_value_take_boxed (dst_value, dst_outer_array);
|
|
}
|
|
|
|
void
|
|
_nm_utils_register_value_transformations (void)
|
|
{
|
|
static gboolean registered = FALSE;
|
|
|
|
if (G_UNLIKELY (!registered)) {
|
|
g_value_register_transform_func (DBUS_TYPE_G_OBJECT_PATH,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_op_to_string);
|
|
g_value_register_transform_func (G_TYPE_STRV,
|
|
DBUS_TYPE_G_LIST_OF_STRING,
|
|
_nm_utils_convert_strv_to_slist);
|
|
g_value_register_transform_func (G_TYPE_STRV,
|
|
DBUS_TYPE_G_ARRAY_OF_STRING,
|
|
_nm_utils_convert_strv_to_ptrarray);
|
|
g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_strv_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_STRING,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_string_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_op_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_UINT_ARRAY,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_uint_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_ip4_addr_route_struct_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_VARIANT,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_gvalue_hash_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_STRING,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_string_hash_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_UCHAR_ARRAY,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_byte_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_ip6_dns_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_ip6_addr_struct_array_to_string);
|
|
g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
|
|
G_TYPE_STRING,
|
|
_nm_utils_convert_ip6_route_struct_array_to_string);
|
|
g_value_register_transform_func (OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
|
|
DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
|
|
_nm_utils_convert_old_ip6_addr_array);
|
|
registered = TRUE;
|
|
}
|
|
}
|
|
|
|
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 capabilties against,
|
|
* e.g. #NMU_SEC_STATIC_WEP
|
|
* @wifi_caps: bitfield of the capabilities of the specific WiFi 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:
|
|
return TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_security_valid:
|
|
* @type: the security type to check AP flags and device capabilties against,
|
|
* e.g. #NMU_SEC_STATIC_WEP
|
|
* @wifi_caps: bitfield of the capabilities of the specific WiFi 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,
|
|
* 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,
|
|
* 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 capabilties 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)
|
|
{
|
|
gboolean good = TRUE;
|
|
|
|
if (!have_ap) {
|
|
if (type == NMU_SEC_NONE)
|
|
return TRUE;
|
|
if ( (type == NMU_SEC_STATIC_WEP)
|
|
|| ((type == NMU_SEC_DYNAMIC_WEP) && !adhoc)
|
|
|| ((type == NMU_SEC_LEAP) && !adhoc)) {
|
|
if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch (type) {
|
|
case NMU_SEC_NONE:
|
|
g_assert (have_ap);
|
|
if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
|
|
return FALSE;
|
|
if (ap_wpa || ap_rsn)
|
|
return FALSE;
|
|
break;
|
|
case NMU_SEC_LEAP: /* require PRIVACY bit for LEAP? */
|
|
if (adhoc)
|
|
return FALSE;
|
|
/* Fall through */
|
|
case NMU_SEC_STATIC_WEP:
|
|
g_assert (have_ap);
|
|
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;
|
|
}
|
|
break;
|
|
case NMU_SEC_DYNAMIC_WEP:
|
|
if (adhoc)
|
|
return FALSE;
|
|
g_assert (have_ap);
|
|
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;
|
|
}
|
|
break;
|
|
case NMU_SEC_WPA_PSK:
|
|
if (adhoc)
|
|
return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */
|
|
if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
|
|
return FALSE;
|
|
if (have_ap) {
|
|
/* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and
|
|
* they don't have any pairwise ciphers. */
|
|
if (adhoc) {
|
|
if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_TKIP)
|
|
&& (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
|
|
return TRUE;
|
|
if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_CCMP)
|
|
&& (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
|
|
return TRUE;
|
|
} else {
|
|
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;
|
|
}
|
|
break;
|
|
case NMU_SEC_WPA2_PSK:
|
|
if (adhoc)
|
|
return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */
|
|
if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
|
|
return FALSE;
|
|
if (have_ap) {
|
|
/* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and
|
|
* they don't have any pairwise ciphers, nor any RSA flags yet. */
|
|
if (adhoc) {
|
|
if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
|
|
return TRUE;
|
|
if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
|
|
return TRUE;
|
|
} else {
|
|
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;
|
|
}
|
|
break;
|
|
case NMU_SEC_WPA_ENTERPRISE:
|
|
if (adhoc)
|
|
return FALSE;
|
|
if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
|
|
return FALSE;
|
|
if (have_ap) {
|
|
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;
|
|
}
|
|
break;
|
|
case NMU_SEC_WPA2_ENTERPRISE:
|
|
if (adhoc)
|
|
return FALSE;
|
|
if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
|
|
return FALSE;
|
|
if (have_ap) {
|
|
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;
|
|
}
|
|
break;
|
|
default:
|
|
good = FALSE;
|
|
break;
|
|
}
|
|
|
|
return good;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip4_addresses_from_gvalue:
|
|
* @value: gvalue containing a GPtrArray of GArrays of guint32s
|
|
*
|
|
* Utility function to convert a #GPtrArray of #GArrays of guint32s representing
|
|
* a list of NetworkManager IPv4 addresses (which is a tuple of address, gateway,
|
|
* and prefix) into a GSList of #NMIP4Address objects. The specific format of
|
|
* this serialization is not guaranteed to be stable and the #GArray may be
|
|
* extended in the future.
|
|
*
|
|
* Returns: (transfer full) (element-type NetworkManager.IP4Address): a newly allocated #GSList of #NMIP4Address objects
|
|
**/
|
|
GSList *
|
|
nm_utils_ip4_addresses_from_gvalue (const GValue *value)
|
|
{
|
|
GPtrArray *addresses;
|
|
int i;
|
|
GSList *list = NULL;
|
|
|
|
addresses = (GPtrArray *) g_value_get_boxed (value);
|
|
for (i = 0; addresses && (i < addresses->len); i++) {
|
|
GArray *array = (GArray *) g_ptr_array_index (addresses, i);
|
|
NMIP4Address *addr;
|
|
|
|
if (array->len < 3) {
|
|
g_warning ("Ignoring invalid IP4 address");
|
|
continue;
|
|
}
|
|
|
|
addr = nm_ip4_address_new ();
|
|
nm_ip4_address_set_address (addr, g_array_index (array, guint32, 0));
|
|
nm_ip4_address_set_prefix (addr, g_array_index (array, guint32, 1));
|
|
nm_ip4_address_set_gateway (addr, g_array_index (array, guint32, 2));
|
|
list = g_slist_prepend (list, addr);
|
|
}
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip4_addresses_to_gvalue:
|
|
* @list: (element-type NMIP4Address): a list of #NMIP4Address objects
|
|
* @value: a pointer to a #GValue into which to place the converted addresses,
|
|
* which should be unset by the caller (when no longer needed) with
|
|
* g_value_unset().
|
|
*
|
|
* Utility function to convert a #GSList of #NMIP4Address objects into a
|
|
* GPtrArray of GArrays of guint32s representing a list of NetworkManager IPv4
|
|
* addresses (which is a tuple of address, gateway, and prefix). The specific
|
|
* format of this serialization is not guaranteed to be stable and may be
|
|
* extended in the future.
|
|
**/
|
|
void
|
|
nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value)
|
|
{
|
|
GPtrArray *addresses;
|
|
GSList *iter;
|
|
|
|
addresses = g_ptr_array_new ();
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
NMIP4Address *addr = (NMIP4Address *) iter->data;
|
|
GArray *array;
|
|
guint32 tmp;
|
|
|
|
array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
|
|
|
|
tmp = nm_ip4_address_get_address (addr);
|
|
g_array_append_val (array, tmp);
|
|
|
|
tmp = nm_ip4_address_get_prefix (addr);
|
|
g_array_append_val (array, tmp);
|
|
|
|
tmp = nm_ip4_address_get_gateway (addr);
|
|
g_array_append_val (array, tmp);
|
|
|
|
g_ptr_array_add (addresses, array);
|
|
}
|
|
|
|
g_value_take_boxed (value, addresses);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip4_routes_from_gvalue:
|
|
* @value: gvalue containing a GPtrArray of GArrays of guint32s
|
|
*
|
|
* Utility function to convert a GPtrArray of GArrays of guint32s representing
|
|
* a list of NetworkManager IPv4 routes (which is a tuple of route, next hop,
|
|
* prefix, and metric) into a GSList of #NMIP4Route objects. The specific
|
|
* format of this serialization is not guaranteed to be stable and may be
|
|
* extended in the future.
|
|
*
|
|
* Returns: (transfer full) (element-type NetworkManager.IP4Route): a newly allocated #GSList of #NMIP4Route objects
|
|
**/
|
|
GSList *
|
|
nm_utils_ip4_routes_from_gvalue (const GValue *value)
|
|
{
|
|
GPtrArray *routes;
|
|
int i;
|
|
GSList *list = NULL;
|
|
|
|
routes = (GPtrArray *) g_value_get_boxed (value);
|
|
for (i = 0; routes && (i < routes->len); i++) {
|
|
GArray *array = (GArray *) g_ptr_array_index (routes, i);
|
|
NMIP4Route *route;
|
|
|
|
if (array->len < 4) {
|
|
g_warning ("Ignoring invalid IP4 route");
|
|
continue;
|
|
}
|
|
|
|
route = nm_ip4_route_new ();
|
|
nm_ip4_route_set_dest (route, g_array_index (array, guint32, 0));
|
|
nm_ip4_route_set_prefix (route, g_array_index (array, guint32, 1));
|
|
nm_ip4_route_set_next_hop (route, g_array_index (array, guint32, 2));
|
|
nm_ip4_route_set_metric (route, g_array_index (array, guint32, 3));
|
|
list = g_slist_prepend (list, route);
|
|
}
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip4_routes_to_gvalue:
|
|
* @list: (element-type NMIP4Route): a list of #NMIP4Route objects
|
|
* @value: a pointer to a #GValue into which to place the converted routes,
|
|
* which should be unset by the caller (when no longer needed) with
|
|
* g_value_unset().
|
|
*
|
|
* Utility function to convert a #GSList of #NMIP4Route objects into a
|
|
* GPtrArray of GArrays of guint32s representing a list of NetworkManager IPv4
|
|
* routes (which is a tuple of route, next hop, prefix, and metric). The
|
|
* specific format of this serialization is not guaranteed to be stable and may
|
|
* be extended in the future.
|
|
**/
|
|
void
|
|
nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value)
|
|
{
|
|
GPtrArray *routes;
|
|
GSList *iter;
|
|
|
|
routes = g_ptr_array_new ();
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
NMIP4Route *route = (NMIP4Route *) iter->data;
|
|
GArray *array;
|
|
guint32 tmp;
|
|
|
|
array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
|
|
|
|
tmp = nm_ip4_route_get_dest (route);
|
|
g_array_append_val (array, tmp);
|
|
|
|
tmp = nm_ip4_route_get_prefix (route);
|
|
g_array_append_val (array, tmp);
|
|
|
|
tmp = nm_ip4_route_get_next_hop (route);
|
|
g_array_append_val (array, tmp);
|
|
|
|
tmp = nm_ip4_route_get_metric (route);
|
|
g_array_append_val (array, tmp);
|
|
|
|
g_ptr_array_add (routes, array);
|
|
}
|
|
|
|
g_value_take_boxed (value, 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)
|
|
{
|
|
guchar *p, *end;
|
|
guint32 prefix = 0;
|
|
|
|
p = (guchar *) &netmask;
|
|
end = p + sizeof (guint32);
|
|
|
|
while ((*p == 0xFF) && p < end) {
|
|
prefix += 8;
|
|
p++;
|
|
}
|
|
|
|
if (p < end) {
|
|
guchar v = *p;
|
|
|
|
while (v) {
|
|
prefix++;
|
|
v <<= 1;
|
|
}
|
|
}
|
|
|
|
return prefix;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
guint32 msk = 0x80000000;
|
|
guint32 netmask = 0;
|
|
|
|
while (prefix > 0) {
|
|
netmask |= msk;
|
|
msk >>= 1;
|
|
prefix--;
|
|
}
|
|
|
|
return (guint32) htonl (netmask);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
|
|
return 8; /* Class A - 255.0.0.0 */
|
|
else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
|
|
return 16; /* Class B - 255.255.0.0 */
|
|
|
|
return 24; /* Class C - 255.255.255.0 */
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip6_addresses_from_gvalue:
|
|
* @value: gvalue containing a GPtrArray of GValueArrays of (GArray of guchars) and guint32
|
|
*
|
|
* Utility function to convert a #GPtrArray of #GValueArrays of (#GArray of guchars) and guint32
|
|
* representing a list of NetworkManager IPv6 addresses (which is a tuple of address,
|
|
* prefix, and gateway), into a GSList of #NMIP6Address objects. The specific format of
|
|
* this serialization is not guaranteed to be stable and the #GValueArray may be
|
|
* extended in the future.
|
|
*
|
|
* Returns: (transfer full) (element-type NetworkManager.IP6Address): a newly allocated #GSList of #NMIP6Address objects
|
|
**/
|
|
GSList *
|
|
nm_utils_ip6_addresses_from_gvalue (const GValue *value)
|
|
{
|
|
GPtrArray *addresses;
|
|
int i;
|
|
GSList *list = NULL;
|
|
|
|
addresses = (GPtrArray *) g_value_get_boxed (value);
|
|
|
|
for (i = 0; addresses && (i < addresses->len); i++) {
|
|
GValueArray *elements = (GValueArray *) g_ptr_array_index (addresses, i);
|
|
GValue *tmp;
|
|
GByteArray *ba_addr;
|
|
GByteArray *ba_gw = NULL;
|
|
NMIP6Address *addr;
|
|
guint32 prefix;
|
|
|
|
if (elements->n_values < 2 || elements->n_values > 3) {
|
|
g_warning ("%s: ignoring invalid IP6 address structure", __func__);
|
|
continue;
|
|
}
|
|
|
|
if ( (G_VALUE_TYPE (g_value_array_get_nth (elements, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (elements, 1)) != G_TYPE_UINT)) {
|
|
g_warning ("%s: ignoring invalid IP6 address structure", __func__);
|
|
continue;
|
|
}
|
|
|
|
/* Check optional 3rd element (gateway) */
|
|
if ( elements->n_values == 3
|
|
&& (G_VALUE_TYPE (g_value_array_get_nth (elements, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)) {
|
|
g_warning ("%s: ignoring invalid IP6 address structure", __func__);
|
|
continue;
|
|
}
|
|
|
|
tmp = g_value_array_get_nth (elements, 0);
|
|
ba_addr = g_value_get_boxed (tmp);
|
|
if (ba_addr->len != 16) {
|
|
g_warning ("%s: ignoring invalid IP6 address of length %d",
|
|
__func__, ba_addr->len);
|
|
continue;
|
|
}
|
|
|
|
tmp = g_value_array_get_nth (elements, 1);
|
|
prefix = g_value_get_uint (tmp);
|
|
if (prefix > 128) {
|
|
g_warning ("%s: ignoring invalid IP6 prefix %d",
|
|
__func__, prefix);
|
|
continue;
|
|
}
|
|
|
|
if (elements->n_values == 3) {
|
|
tmp = g_value_array_get_nth (elements, 2);
|
|
ba_gw = g_value_get_boxed (tmp);
|
|
if (ba_gw->len != 16) {
|
|
g_warning ("%s: ignoring invalid IP6 gateway address of length %d",
|
|
__func__, ba_gw->len);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
addr = nm_ip6_address_new ();
|
|
nm_ip6_address_set_prefix (addr, prefix);
|
|
nm_ip6_address_set_address (addr, (const struct in6_addr *) ba_addr->data);
|
|
if (ba_gw)
|
|
nm_ip6_address_set_gateway (addr, (const struct in6_addr *) ba_gw->data);
|
|
|
|
list = g_slist_prepend (list, addr);
|
|
}
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip6_addresses_to_gvalue:
|
|
* @list: (element-type NMIP6Address): a list of #NMIP6Address objects
|
|
* @value: a pointer to a #GValue into which to place the converted addresses,
|
|
* which should be unset by the caller (when no longer needed) with
|
|
* g_value_unset().
|
|
*
|
|
* Utility function to convert a #GSList of #NMIP6Address objects into a
|
|
* GPtrArray of GValueArrays representing a list of NetworkManager IPv6 addresses
|
|
* (which is a tuple of address, prefix, and gateway). The specific format of
|
|
* this serialization is not guaranteed to be stable and may be extended in the
|
|
* future.
|
|
**/
|
|
void
|
|
nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value)
|
|
{
|
|
GPtrArray *addresses;
|
|
GSList *iter;
|
|
|
|
addresses = g_ptr_array_new ();
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
NMIP6Address *addr = (NMIP6Address *) iter->data;
|
|
GValueArray *array;
|
|
GValue element = {0, };
|
|
GByteArray *ba;
|
|
|
|
array = g_value_array_new (3);
|
|
|
|
/* IP address */
|
|
g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
|
|
ba = g_byte_array_new ();
|
|
g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_address (addr), 16);
|
|
g_value_take_boxed (&element, ba);
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
/* Prefix */
|
|
g_value_init (&element, G_TYPE_UINT);
|
|
g_value_set_uint (&element, nm_ip6_address_get_prefix (addr));
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
/* Gateway */
|
|
g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
|
|
ba = g_byte_array_new ();
|
|
g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_gateway (addr), 16);
|
|
g_value_take_boxed (&element, ba);
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_ptr_array_add (addresses, array);
|
|
}
|
|
|
|
g_value_take_boxed (value, addresses);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip6_routes_from_gvalue:
|
|
* @value: gvalue containing a GPtrArray of GValueArrays of (GArray or guchars), guint32,
|
|
* (GArray of guchars), and guint32
|
|
*
|
|
* Utility function GPtrArray of GValueArrays of (GArray or guchars), guint32,
|
|
* (GArray of guchars), and guint32 representing a list of NetworkManager IPv6
|
|
* routes (which is a tuple of destination, prefix, next hop, and metric)
|
|
* into a GSList of #NMIP6Route objects. The specific format of this serialization
|
|
* is not guaranteed to be stable and may be extended in the future.
|
|
*
|
|
* Returns: (transfer full) (element-type NetworkManager.IP6Route): a newly allocated #GSList of #NMIP6Route objects
|
|
**/
|
|
GSList *
|
|
nm_utils_ip6_routes_from_gvalue (const GValue *value)
|
|
{
|
|
GPtrArray *routes;
|
|
int i;
|
|
GSList *list = NULL;
|
|
|
|
routes = (GPtrArray *) g_value_get_boxed (value);
|
|
for (i = 0; routes && (i < routes->len); i++) {
|
|
GValueArray *route_values = (GValueArray *) g_ptr_array_index (routes, i);
|
|
GByteArray *dest, *next_hop;
|
|
guint prefix, metric;
|
|
NMIP6Route *route;
|
|
|
|
if ( (route_values->n_values != 4)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 0)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 1)) != G_TYPE_UINT)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 2)) != DBUS_TYPE_G_UCHAR_ARRAY)
|
|
|| (G_VALUE_TYPE (g_value_array_get_nth (route_values, 3)) != G_TYPE_UINT)) {
|
|
g_warning ("Ignoring invalid IP6 route");
|
|
continue;
|
|
}
|
|
|
|
dest = g_value_get_boxed (g_value_array_get_nth (route_values, 0));
|
|
if (dest->len != 16) {
|
|
g_warning ("%s: ignoring invalid IP6 dest address of length %d",
|
|
__func__, dest->len);
|
|
continue;
|
|
}
|
|
|
|
prefix = g_value_get_uint (g_value_array_get_nth (route_values, 1));
|
|
|
|
next_hop = g_value_get_boxed (g_value_array_get_nth (route_values, 2));
|
|
if (next_hop->len != 16) {
|
|
g_warning ("%s: ignoring invalid IP6 next_hop address of length %d",
|
|
__func__, next_hop->len);
|
|
continue;
|
|
}
|
|
|
|
metric = g_value_get_uint (g_value_array_get_nth (route_values, 3));
|
|
|
|
route = nm_ip6_route_new ();
|
|
nm_ip6_route_set_dest (route, (struct in6_addr *)dest->data);
|
|
nm_ip6_route_set_prefix (route, prefix);
|
|
nm_ip6_route_set_next_hop (route, (struct in6_addr *)next_hop->data);
|
|
nm_ip6_route_set_metric (route, metric);
|
|
list = g_slist_prepend (list, route);
|
|
}
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip6_routes_to_gvalue:
|
|
* @list: (element-type NMIP6Route): a list of #NMIP6Route objects
|
|
* @value: a pointer to a #GValue into which to place the converted routes,
|
|
* which should be unset by the caller (when no longer needed) with
|
|
* g_value_unset().
|
|
*
|
|
* Utility function to convert a #GSList of #NMIP6Route objects into a GPtrArray of
|
|
* GValueArrays of (GArray or guchars), guint32, (GArray of guchars), and guint32
|
|
* representing a list of NetworkManager IPv6 routes (which is a tuple of destination,
|
|
* prefix, next hop, and metric). The specific format of this serialization is not
|
|
* guaranteed to be stable and may be extended in the future.
|
|
**/
|
|
void
|
|
nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value)
|
|
{
|
|
GPtrArray *routes;
|
|
GSList *iter;
|
|
|
|
routes = g_ptr_array_new ();
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
NMIP6Route *route = (NMIP6Route *) iter->data;
|
|
GValueArray *array;
|
|
const struct in6_addr *addr;
|
|
GByteArray *ba;
|
|
GValue element = {0, };
|
|
|
|
array = g_value_array_new (4);
|
|
|
|
g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
|
|
addr = nm_ip6_route_get_dest (route);
|
|
ba = g_byte_array_new ();
|
|
g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
|
|
g_value_take_boxed (&element, ba);
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_value_init (&element, G_TYPE_UINT);
|
|
g_value_set_uint (&element, nm_ip6_route_get_prefix (route));
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
|
|
addr = nm_ip6_route_get_next_hop (route);
|
|
ba = g_byte_array_new ();
|
|
g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
|
|
g_value_take_boxed (&element, ba);
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_value_init (&element, G_TYPE_UINT);
|
|
g_value_set_uint (&element, nm_ip6_route_get_metric (route));
|
|
g_value_array_append (array, &element);
|
|
g_value_unset (&element);
|
|
|
|
g_ptr_array_add (routes, array);
|
|
}
|
|
|
|
g_value_take_boxed (value, routes);
|
|
}
|
|
|
|
/* FIXME: the Posix namespace does not exist, and thus neither does
|
|
the in6_addr struct. Marking (skip) for now */
|
|
/**
|
|
* nm_utils_ip6_dns_from_gvalue: (skip)
|
|
* @value: a #GValue
|
|
*
|
|
* Converts a #GValue containing a #GPtrArray of IP6 DNS, represented as
|
|
* #GByteArray<!-- -->s into a #GSList of #in6_addr<!-- -->s.
|
|
*
|
|
* Returns: (transfer full) (element-type Posix.in6_addr): a #GSList of IP6
|
|
* addresses.
|
|
*/
|
|
GSList *
|
|
nm_utils_ip6_dns_from_gvalue (const GValue *value)
|
|
{
|
|
GPtrArray *dns;
|
|
int i;
|
|
GSList *list = NULL;
|
|
|
|
dns = (GPtrArray *) g_value_get_boxed (value);
|
|
for (i = 0; dns && (i < dns->len); i++) {
|
|
GByteArray *bytearray = (GByteArray *) g_ptr_array_index (dns, i);
|
|
struct in6_addr *addr;
|
|
|
|
if (bytearray->len != 16) {
|
|
g_warning ("%s: ignoring invalid IP6 address of length %d",
|
|
__func__, bytearray->len);
|
|
continue;
|
|
}
|
|
|
|
addr = g_malloc0 (sizeof (struct in6_addr));
|
|
memcpy (addr->s6_addr, bytearray->data, bytearray->len);
|
|
list = g_slist_prepend (list, addr);
|
|
}
|
|
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_ip6_dns_to_gvalue: (skip)
|
|
* @list: a list of #NMIP6Route objects
|
|
* @value: a pointer to a #GValue into which to place the converted DNS server
|
|
* addresses, which should be unset by the caller (when no longer needed) with
|
|
* g_value_unset().
|
|
*
|
|
* Utility function to convert a #GSList of 'struct in6_addr' structs into a
|
|
* GPtrArray of GByteArrays representing each server's IPv6 addresses in
|
|
* network byte order. The specific format of this serialization is not
|
|
* guaranteed to be stable and may be extended in the future.
|
|
*/
|
|
void
|
|
nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value)
|
|
{
|
|
GPtrArray *dns;
|
|
GSList *iter;
|
|
|
|
dns = g_ptr_array_new ();
|
|
|
|
for (iter = list; iter; iter = iter->next) {
|
|
struct in6_addr *addr = (struct in6_addr *) iter->data;
|
|
GByteArray *bytearray;
|
|
|
|
bytearray = g_byte_array_sized_new (16);
|
|
g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
|
|
g_ptr_array_add (dns, bytearray);
|
|
}
|
|
|
|
g_value_take_boxed (value, dns);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
uuid_t uuid;
|
|
char *buf;
|
|
|
|
buf = g_malloc0 (37);
|
|
uuid_generate_random (uuid);
|
|
uuid_unparse_lower (uuid, &buf[0]);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_uuid_generate_from_string:
|
|
* @s: a string to use as the seed for the UUID
|
|
*
|
|
* 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)
|
|
{
|
|
GError *error = NULL;
|
|
uuid_t *uuid;
|
|
char *buf = NULL;
|
|
|
|
if (!nm_utils_init (&error)) {
|
|
g_warning ("error initializing crypto: (%d) %s",
|
|
error ? error->code : 0,
|
|
error ? error->message : "unknown");
|
|
if (error)
|
|
g_error_free (error);
|
|
return NULL;
|
|
}
|
|
|
|
uuid = g_malloc0 (sizeof (*uuid));
|
|
if (!crypto_md5_hash (NULL, 0, s, strlen (s), (char *) uuid, sizeof (*uuid), &error)) {
|
|
g_warning ("error generating UUID: (%d) %s",
|
|
error ? error->code : 0,
|
|
error ? error->message : "unknown");
|
|
if (error)
|
|
g_error_free (error);
|
|
goto out;
|
|
}
|
|
|
|
buf = g_malloc0 (37);
|
|
uuid_unparse_lower (*uuid, &buf[0]);
|
|
|
|
out:
|
|
g_free (uuid);
|
|
return buf;
|
|
}
|
|
|
|
static char *
|
|
make_key (const char *salt,
|
|
const gsize salt_len,
|
|
const char *password,
|
|
gsize *out_len,
|
|
GError **error)
|
|
{
|
|
char *key;
|
|
guint32 digest_len = 24; /* DES-EDE3-CBC */
|
|
|
|
g_return_val_if_fail (salt != NULL, NULL);
|
|
g_return_val_if_fail (salt_len >= 8, NULL);
|
|
g_return_val_if_fail (password != NULL, NULL);
|
|
g_return_val_if_fail (out_len != NULL, NULL);
|
|
|
|
key = g_malloc0 (digest_len + 1);
|
|
if (!key) {
|
|
g_set_error (error,
|
|
NM_CRYPTO_ERROR,
|
|
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
|
_("Not enough memory to make encryption key."));
|
|
return NULL;
|
|
}
|
|
|
|
if (!crypto_md5_hash (salt, salt_len, password, strlen (password), key, digest_len, error)) {
|
|
*out_len = 0;
|
|
memset (key, 0, digest_len);
|
|
g_free (key);
|
|
key = NULL;
|
|
} else
|
|
*out_len = digest_len;
|
|
|
|
return key;
|
|
}
|
|
|
|
/*
|
|
* utils_bin2hexstr
|
|
*
|
|
* Convert a byte-array into a hexadecimal string.
|
|
*
|
|
* Code originally by Alex Larsson <alexl@redhat.com> and
|
|
* copyright Red Hat, Inc. under terms of the LGPL.
|
|
*
|
|
*/
|
|
static char *
|
|
utils_bin2hexstr (const char *bytes, int len, int final_len)
|
|
{
|
|
static char hex_digits[] = "0123456789abcdef";
|
|
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_rsa_key_encrypt:
|
|
* @data: RSA private key data to be encrypted
|
|
* @in_password: (allow-none): existing password to use, if any
|
|
* @out_password: (out) (allow-none): if @in_password was NULL, a random password will be generated
|
|
* and returned in this argument
|
|
* @error: detailed error information on return, if an error occurred
|
|
*
|
|
* Encrypts the given RSA private key data with the given password (or generates
|
|
* a password if no password was given) and converts the data to PEM format
|
|
* suitable for writing to a file.
|
|
*
|
|
* Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted
|
|
* certificate/private key file.
|
|
**/
|
|
GByteArray *
|
|
nm_utils_rsa_key_encrypt (const GByteArray *data,
|
|
const char *in_password,
|
|
char **out_password,
|
|
GError **error)
|
|
{
|
|
char salt[8];
|
|
char *key = NULL, *enc = NULL, *pw_buf[32];
|
|
gsize key_len = 0, enc_len = 0;
|
|
GString *pem = NULL;
|
|
char *tmp, *tmp_password = NULL;
|
|
int left;
|
|
const char *p;
|
|
GByteArray *ret = NULL;
|
|
|
|
g_return_val_if_fail (data != NULL, NULL);
|
|
g_return_val_if_fail (data->len > 0, NULL);
|
|
if (out_password)
|
|
g_return_val_if_fail (*out_password == NULL, NULL);
|
|
|
|
/* Make the password if needed */
|
|
if (!in_password) {
|
|
if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
|
|
return NULL;
|
|
in_password = tmp_password = utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1);
|
|
}
|
|
|
|
if (!crypto_randomize (salt, sizeof (salt), error))
|
|
goto out;
|
|
|
|
key = make_key (&salt[0], sizeof (salt), in_password, &key_len, error);
|
|
if (!key)
|
|
goto out;
|
|
|
|
enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, salt, sizeof (salt), key, key_len, &enc_len, error);
|
|
if (!enc)
|
|
goto out;
|
|
|
|
pem = g_string_sized_new (enc_len * 2 + 100);
|
|
if (!pem) {
|
|
g_set_error_literal (error, NM_CRYPTO_ERROR,
|
|
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
|
_("Could not allocate memory for PEM file creation."));
|
|
goto out;
|
|
}
|
|
|
|
g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n");
|
|
g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
|
|
|
|
/* Convert the salt to a hex string */
|
|
tmp = utils_bin2hexstr ((const char *) salt, sizeof (salt), 16);
|
|
if (!tmp) {
|
|
g_set_error (error, NM_CRYPTO_ERROR,
|
|
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
|
_("Could not allocate memory for writing IV to PEM file."));
|
|
goto out;
|
|
}
|
|
|
|
g_string_append_printf (pem, "DEK-Info: DES-EDE3-CBC,%s\n\n", tmp);
|
|
g_free (tmp);
|
|
|
|
/* Convert the encrypted key to a base64 string */
|
|
p = tmp = g_base64_encode ((const guchar *) enc, enc_len);
|
|
if (!tmp) {
|
|
g_set_error (error, NM_CRYPTO_ERROR,
|
|
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
|
_("Could not allocate memory for writing encrypted key to PEM file."));
|
|
goto out;
|
|
}
|
|
|
|
left = strlen (tmp);
|
|
while (left > 0) {
|
|
g_string_append_len (pem, p, (left < 64) ? left : 64);
|
|
g_string_append_c (pem, '\n');
|
|
left -= 64;
|
|
p += 64;
|
|
}
|
|
g_free (tmp);
|
|
|
|
g_string_append (pem, "-----END RSA PRIVATE KEY-----\n");
|
|
|
|
ret = g_byte_array_sized_new (pem->len);
|
|
if (!ret) {
|
|
g_set_error (error, NM_CRYPTO_ERROR,
|
|
NM_CRYPTO_ERR_OUT_OF_MEMORY,
|
|
_("Could not allocate memory for PEM file data."));
|
|
goto out;
|
|
}
|
|
g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len);
|
|
if (tmp_password && out_password)
|
|
*out_password = g_strdup (tmp_password);
|
|
|
|
out:
|
|
if (key) {
|
|
memset (key, 0, key_len);
|
|
g_free (key);
|
|
}
|
|
if (enc) {
|
|
memset (enc, 0, enc_len);
|
|
g_free (enc);
|
|
}
|
|
if (pem)
|
|
g_string_free (pem, TRUE);
|
|
|
|
if (tmp_password) {
|
|
memset (tmp_password, 0, strlen (tmp_password));
|
|
g_free (tmp_password);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_file_is_pkcs12:
|
|
* @filename: name of the file to test
|
|
*
|
|
* Utility function to find out if the @filename is in PKCS#12 format.
|
|
*
|
|
* Returns: TRUE if the file is PKCS#12, FALSE if it is not
|
|
**/
|
|
gboolean
|
|
nm_utils_file_is_pkcs12 (const char *filename)
|
|
{
|
|
return crypto_is_pkcs12_file (filename, NULL);
|
|
}
|
|
|
|
/* Band, channel/frequency stuff for wireless */
|
|
struct cf_pair {
|
|
guint32 chan;
|
|
guint32 freq;
|
|
};
|
|
|
|
static 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, -1 }
|
|
};
|
|
|
|
static 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, -1 }
|
|
};
|
|
|
|
/**
|
|
* nm_utils_wifi_freq_to_channel:
|
|
* @freq: frequency
|
|
*
|
|
* Utility function to translate a WiFi 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].chan && (a_table[i].freq != freq))
|
|
i++;
|
|
return a_table[i].chan;
|
|
} else {
|
|
while (bg_table[i].chan && (bg_table[i].freq != freq))
|
|
i++;
|
|
return bg_table[i].chan;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_wifi_channel_to_freq:
|
|
* @channel: channel
|
|
* @band: frequency band for wireless ("a" or "bg")
|
|
*
|
|
* Utility function to translate a WiFi 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 = 0;
|
|
|
|
if (!strcmp (band, "a")) {
|
|
while (a_table[i].chan && (a_table[i].chan != channel))
|
|
i++;
|
|
return a_table[i].freq;
|
|
} else if (!strcmp (band, "bg")) {
|
|
while (bg_table[i].chan && (bg_table[i].chan != channel))
|
|
i++;
|
|
return bg_table[i].freq;
|
|
}
|
|
|
|
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 WiFi 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 = sizeof (a_table) / sizeof (struct cf_pair);
|
|
size_t bg_size = sizeof (bg_table) / sizeof (struct cf_pair);
|
|
struct cf_pair *pair = NULL;
|
|
|
|
if (!strcmp (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 (!strcmp (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_assert_not_reached ();
|
|
return 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 WiFi channel validity.
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
**/
|
|
gboolean
|
|
nm_utils_wifi_is_channel_valid (guint32 channel, const char *band)
|
|
{
|
|
struct cf_pair *table = NULL;
|
|
int i = 0;
|
|
|
|
if (!strcmp (band, "a"))
|
|
table = a_table;
|
|
else if (!strcmp (band, "bg"))
|
|
table = bg_table;
|
|
else
|
|
return FALSE;
|
|
|
|
while (table[i].chan && (table[i].chan != channel))
|
|
i++;
|
|
|
|
if (table[i].chan != 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_hwaddr_len:
|
|
* @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
|
|
*
|
|
* Returns the length in octets of a hardware address of type @type.
|
|
*
|
|
* Return value: the length
|
|
*/
|
|
int
|
|
nm_utils_hwaddr_len (int type)
|
|
{
|
|
if (type == ARPHRD_ETHER)
|
|
return ETH_ALEN;
|
|
else if (type == ARPHRD_INFINIBAND)
|
|
return INFINIBAND_ALEN;
|
|
else
|
|
g_return_val_if_reached (-1);
|
|
}
|
|
|
|
/**
|
|
* nm_utils_hwaddr_type:
|
|
* @len: the length of hardware address in bytes
|
|
*
|
|
* Returns the type (either %ARPHRD_ETHER or %ARPHRD_INFINIBAND) of the raw
|
|
* address given its length.
|
|
*
|
|
* Return value: the type, either %ARPHRD_ETHER or %ARPHRD_INFINIBAND, or -1 if
|
|
* the address length was not recognized
|
|
*/
|
|
int
|
|
nm_utils_hwaddr_type (int len)
|
|
{
|
|
if (len == ETH_ALEN)
|
|
return ARPHRD_ETHER;
|
|
else if (len == INFINIBAND_ALEN)
|
|
return ARPHRD_INFINIBAND;
|
|
else
|
|
g_return_val_if_reached (-1);
|
|
}
|
|
|
|
#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
|
|
|
|
/**
|
|
* nm_utils_hwaddr_aton:
|
|
* @asc: the ASCII representation of a hardware address
|
|
* @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
|
|
* @buffer: buffer to store the result into
|
|
*
|
|
* Parses @asc and converts it to binary form in @buffer. See
|
|
* nm_utils_hwaddr_atoba() if you'd rather have the result in a
|
|
* #GByteArray.
|
|
*
|
|
* Return value: @buffer, or %NULL if @asc couldn't be parsed
|
|
*/
|
|
guint8 *
|
|
nm_utils_hwaddr_aton (const char *asc, int type, gpointer buffer)
|
|
{
|
|
const char *in = asc;
|
|
guint8 *out = (guint8 *)buffer;
|
|
int left = nm_utils_hwaddr_len (type);
|
|
|
|
while (left && *in) {
|
|
guint8 d1 = in[0], d2 = in[1];
|
|
|
|
if (!g_ascii_isxdigit (d1))
|
|
return NULL;
|
|
|
|
/* If there's no leading zero (ie "aa:b:cc") then fake it */
|
|
if (d2 && g_ascii_isxdigit (d2)) {
|
|
*out++ = (HEXVAL (d1) << 4) + HEXVAL (d2);
|
|
in += 2;
|
|
} else {
|
|
/* Fake leading zero */
|
|
*out++ = (HEXVAL ('0') << 4) + HEXVAL (d1);
|
|
in += 1;
|
|
}
|
|
|
|
left--;
|
|
if (*in) {
|
|
if (*in != ':')
|
|
return NULL;
|
|
in++;
|
|
}
|
|
}
|
|
|
|
if (left == 0 && !*in)
|
|
return buffer;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_hwaddr_atoba:
|
|
* @asc: the ASCII representation of a hardware address
|
|
* @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
|
|
*
|
|
* 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, int type)
|
|
{
|
|
GByteArray *ba;
|
|
int len = nm_utils_hwaddr_len (type);
|
|
|
|
ba = g_byte_array_sized_new (len);
|
|
ba->len = len;
|
|
if (!nm_utils_hwaddr_aton (asc, type, ba->data)) {
|
|
g_byte_array_unref (ba);
|
|
return NULL;
|
|
}
|
|
|
|
return ba;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_hwaddr_ntoa:
|
|
* @addr: a binary hardware address
|
|
* @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
|
|
*
|
|
* Converts @addr to textual form.
|
|
*
|
|
* Return value: (transfer full): the textual form of @addr
|
|
*/
|
|
char *
|
|
nm_utils_hwaddr_ntoa (gconstpointer addr, int type)
|
|
{
|
|
const guint8 *in = addr;
|
|
GString *out = g_string_new (NULL);
|
|
int left = nm_utils_hwaddr_len (type);
|
|
|
|
while (left--) {
|
|
if (out->len)
|
|
g_string_append_c (out, ':');
|
|
g_string_append_printf (out, "%02X", *in++);
|
|
}
|
|
|
|
return g_string_free (out, FALSE);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_is_uuid:
|
|
* @str: a string that might be a UUID
|
|
*
|
|
* Checks if @str is a UUID
|
|
*
|
|
* Returns: %TRUE if @str is a UUID, %FALSE if not
|
|
*/
|
|
gboolean
|
|
nm_utils_is_uuid (const char *str)
|
|
{
|
|
const char *p = str;
|
|
int num_dashes = 0;
|
|
|
|
while (*p) {
|
|
if (*p == '-')
|
|
num_dashes++;
|
|
else if (!g_ascii_isxdigit (*p))
|
|
return FALSE;
|
|
p++;
|
|
}
|
|
|
|
return (num_dashes == 4) && (p - str == 36);
|
|
}
|