diff options
Diffstat (limited to 'gdbus/gdbusaddress.c')
-rw-r--r-- | gdbus/gdbusaddress.c | 532 |
1 files changed, 465 insertions, 67 deletions
diff --git a/gdbus/gdbusaddress.c b/gdbus/gdbusaddress.c index 0d0c2dc..3534562 100644 --- a/gdbus/gdbusaddress.c +++ b/gdbus/gdbusaddress.c @@ -26,9 +26,11 @@ #include <glib/gi18n.h> +#include "gdbusutils.h" #include "gdbusaddress.h" #include "gdbuserror.h" #include "gdbusenumtypes.h" +#include "gdbusprivate.h" #ifdef G_OS_UNIX #include <gio/gunixsocketaddress.h> @@ -45,6 +47,430 @@ /* ---------------------------------------------------------------------------------------------------- */ +/** + * g_dbus_is_address: + * @string: A string. + * + * Checks if @string is a D-Bus address. + * + * This doesn't check if @string is actually supported by #GDBusServer + * or #GDBusConnection - use g_dbus_is_supported_address() to do more + * checks. + * + * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise. + */ +gboolean +g_dbus_is_address (const gchar *string) +{ + guint n; + gchar **a; + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (string != NULL, FALSE); + + a = g_strsplit (string, ";", 0); + for (n = 0; a[n] != NULL; n++) + { + if (!_g_dbus_address_parse_entry (a[n], + NULL, + NULL, + NULL)) + goto out; + } + + ret = TRUE; + + out: + g_strfreev (a); + return ret; +} + +static gboolean +is_valid_unix (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *path; + const gchar *tmpdir; + const gchar *abstract; + + ret = FALSE; + keys = NULL; + path = NULL; + tmpdir = NULL; + abstract = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "path") == 0) + path = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "tmpdir") == 0) + tmpdir = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "abstract") == 0) + abstract = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (path != NULL) + { + if (tmpdir != NULL || abstract != NULL) + goto meaningless; + /* TODO: validate path */ + } + else if (tmpdir != NULL) + { + if (path != NULL || abstract != NULL) + goto meaningless; + /* TODO: validate tmpdir */ + } + else if (abstract != NULL) + { + if (path != NULL || tmpdir != NULL) + goto meaningless; + /* TODO: validate abstract */ + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys"), + address_entry); + goto out; + } + + + ret= TRUE; + goto out; + + meaningless: + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Meaningless key/value pair combination in address entry `%s'"), + address_entry); + + out: + g_list_free (keys); + + return ret; +} + +static gboolean +is_valid_nonce_tcp (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *host; + const gchar *port; + const gchar *family; + const gchar *nonce_file; + gint port_num; + gchar *endp; + + ret = FALSE; + keys = NULL; + host = NULL; + port = NULL; + family = NULL; + nonce_file = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "host") == 0) + host = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "port") == 0) + port = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "family") == 0) + family = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "noncefile") == 0) + nonce_file = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (port != NULL) + { + port_num = strtol (port, &endp, 10); + if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the port attribute is malformed"), + address_entry); + goto out; + } + } + + if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the family attribute is malformed"), + address_entry); + goto out; + } + + ret= TRUE; + + out: + g_list_free (keys); + + return ret; +} + +static gboolean +is_valid_tcp (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *host; + const gchar *port; + const gchar *family; + gint port_num; + gchar *endp; + + ret = FALSE; + keys = NULL; + host = NULL; + port = NULL; + family = NULL; + + keys = g_hash_table_get_keys (key_value_pairs); + for (l = keys; l != NULL; l = l->next) + { + const gchar *key = l->data; + if (g_strcmp0 (key, "host") == 0) + host = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "port") == 0) + port = g_hash_table_lookup (key_value_pairs, key); + else if (g_strcmp0 (key, "family") == 0) + family = g_hash_table_lookup (key_value_pairs, key); + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Unsupported key `%s' in address entry `%s'"), + key, + address_entry); + goto out; + } + } + + if (port != NULL) + { + port_num = strtol (port, &endp, 10); + if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the port attribute is malformed"), + address_entry); + goto out; + } + } + + if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the family attribute is malformed"), + address_entry); + goto out; + } + + ret= TRUE; + + out: + g_list_free (keys); + + return ret; +} + +/** + * g_dbus_is_supported_address: + * @string: A string. + * @error: Return location for error or %NULL. + * + * Like g_dbus_is_address() but also checks if the library suppors the + * transports in @string and that key/value pairs for each transport + * are valid. + * + * Returns: %TRUE if @string is a valid D-Bus address that is + * supported by this library, %FALSE if @error is set. + */ +gboolean +g_dbus_is_supported_address (const gchar *string, + GError **error) +{ + guint n; + gchar **a; + gboolean ret; + + ret = FALSE; + + g_return_val_if_fail (string != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + a = g_strsplit (string, ";", 0); + for (n = 0; a[n] != NULL; n++) + { + gchar *transport_name; + GHashTable *key_value_pairs; + gboolean supported; + + if (!_g_dbus_address_parse_entry (a[n], + &transport_name, + &key_value_pairs, + error)) + goto out; + + supported = FALSE; + if (g_strcmp0 (transport_name, "unix") == 0) + supported = is_valid_unix (a[n], key_value_pairs, error); + else if (g_strcmp0 (transport_name, "tcp") == 0) + supported = is_valid_tcp (a[n], key_value_pairs, error); + else if (g_strcmp0 (transport_name, "nonce-tcp") == 0) + supported = is_valid_nonce_tcp (a[n], key_value_pairs, error); + + g_free (transport_name); + g_hash_table_unref (key_value_pairs); + + if (!supported) + goto out; + } + + ret = TRUE; + + out: + g_strfreev (a); + + g_assert (ret || (!ret && (error == NULL || *error != NULL))); + + return ret; +} + +gboolean +_g_dbus_address_parse_entry (const gchar *address_entry, + gchar **out_transport_name, + GHashTable **out_key_value_pairs, + GError **error) +{ + gboolean ret; + GHashTable *key_value_pairs; + gchar *transport_name; + gchar **kv_pairs; + const gchar *s; + guint n; + + ret = FALSE; + kv_pairs = NULL; + transport_name = NULL; + key_value_pairs = NULL; + + s = strchr (address_entry, ':'); + if (s == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Address element `%s', does not contain a colon (:)"), + address_entry); + goto out; + } + + transport_name = g_strndup (address_entry, s - address_entry); + key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + kv_pairs = g_strsplit (s + 1, ",", 0); + for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++) + { + const gchar *kv_pair = kv_pairs[n]; + gchar *key; + gchar *value; + + s = strchr (kv_pair, '='); + if (s == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"), + n, + kv_pair, + address_entry); + goto out; + } + + /* TODO: actually validate that no illegal characters are present before and after then '=' sign */ + key = g_uri_unescape_segment (kv_pair, s, NULL); + value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL); + g_hash_table_insert (key_value_pairs, key, value); + } + + ret = TRUE; + +out: + g_strfreev (kv_pairs); + if (ret) + { + if (out_transport_name != NULL) + *out_transport_name = transport_name; + else + g_free (transport_name); + if (out_key_value_pairs != NULL) + *out_key_value_pairs = key_value_pairs; + else if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + } + else + { + g_free (transport_name); + if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); + } + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + /* TODO: Declare an extension point called GDBusTransport (or similar) * and move code below to extensions implementing said extension * point. That way we can implement a D-Bus transport over X11 without @@ -100,12 +526,15 @@ g_dbus_address_connect (const gchar *address_entry, } } #endif - else if (g_strcmp0 (transport_name, "nonce-tcp") == 0) + else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0) { const gchar *s; const gchar *host; guint port; gchar *endp; + gboolean is_nonce; + + is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0); host = g_hash_table_lookup (key_value_pairs, "host"); if (host == NULL) @@ -122,7 +551,7 @@ g_dbus_address_connect (const gchar *address_entry, if (s == NULL) s = "0"; port = strtol (s, &endp, 10); - if ((*s == '\0' || *endp != '\0') || port == 0 || port >= 65536) + if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536) { g_set_error (error, G_IO_ERROR, @@ -132,18 +561,22 @@ g_dbus_address_connect (const gchar *address_entry, goto out; } - nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile"); - if (nonce_file == NULL) + + if (is_nonce) { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Error in address `%s' - the noncefile attribute is missing or malformed"), - address_entry); - goto out; + nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile"); + if (nonce_file == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address `%s' - the noncefile attribute is missing or malformed"), + address_entry); + goto out; + } } - /* TODO: deal with family? */ + /* TODO: deal with family */ connectable = g_network_address_new (host, port); } else @@ -167,12 +600,12 @@ g_dbus_address_connect (const gchar *address_entry, connectable, cancellable, error); - if (connection != NULL) - { - ret = G_IO_STREAM (connection); - } g_object_unref (connectable); g_object_unref (client); + if (connection == NULL) + goto out; + + ret = G_IO_STREAM (connection); if (nonce_file != NULL) { @@ -236,70 +669,35 @@ g_dbus_address_try_connect_one (const gchar *address_entry, GIOStream *ret; GHashTable *key_value_pairs; gchar *transport_name; - gchar **kv_pairs; - const gchar *s; - guint n; + const gchar *guid; ret = NULL; + transport_name = NULL; + key_value_pairs = NULL; - s = strchr (address_entry, ':'); - if (s == NULL) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Address element `%s', does not contain a colon (:)"), - address_entry); - goto out; - } - - transport_name = g_strndup (address_entry, s - address_entry); - key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - kv_pairs = g_strsplit (s + 1, ",", 0); - for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++) - { - const gchar *kv_pair = kv_pairs[n]; - gchar *key; - gchar *value; - - s = strchr (kv_pair, '='); - if (s == NULL) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"), - n, - kv_pair, - address_entry); - goto out; - } - - /* TODO: actually validate that no illegal characters are present before and after then '=' sign */ - key = g_uri_unescape_segment (kv_pair, s, NULL); - value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL); - g_hash_table_insert (key_value_pairs, key, value); - } + if (!_g_dbus_address_parse_entry (address_entry, + &transport_name, + &key_value_pairs, + error)) + goto out; ret = g_dbus_address_connect (address_entry, transport_name, key_value_pairs, cancellable, error); - if (ret != NULL) - { - const gchar *guid; - /* TODO: validate that guid is of correct format */ - guid = g_hash_table_lookup (key_value_pairs, "guid"); - if (guid != NULL && out_guid != NULL) - *out_guid = g_strdup (guid); - } + if (ret == NULL) + goto out; + + /* TODO: validate that guid is of correct format */ + guid = g_hash_table_lookup (key_value_pairs, "guid"); + if (guid != NULL && out_guid != NULL) + *out_guid = g_strdup (guid); out: - g_strfreev (kv_pairs); g_free (transport_name); - g_hash_table_unref (key_value_pairs); + if (key_value_pairs != NULL) + g_hash_table_unref (key_value_pairs); return ret; } |