diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 9cd3d0e39f28e446269a070a8ac2f765a1191cc0..ec1e48bb5ac08e87654a14a92ab803e8c7738a28 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -122,6 +122,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index 945c26adeaad6d566039bb2cbb8b931e4b1fb19a..5d46c25e667c72e8a767781bb948a7a948e1afad 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -1880,6 +1880,27 @@ G_TYPE_NATIVE_SOCKET_ADDRESS_TYPE g_native_socket_address_get_type +
+gvsocksocketaddress +GVsockSocketAddress +GVsockSocketAddress +g_vsock_socket_address_new +g_vsock_socket_address_get_cid +g_vsock_socket_address_get_port + +GVsockSocketAddressClass +GVsockSocketAddressPrivate +G_IS_VSOCK_SOCKET_ADDRESS +G_IS_VSOCK_SOCKET_ADDRESS_CLASS +G_TYPE_VSOCK_SOCKET_ADDRESS +G_VSOCK_SOCKET_ADDRESS +G_VSOCK_SOCKET_ADDRESS_CLASS +G_VSOCK_SOCKET_ADDRESS_GET_CLASS +G_TYPE_VSOCK_SOCKET_ADDRESS_TYPE + +g_vsock_socket_address_get_type +
+
gresolver GResolver @@ -2266,6 +2287,24 @@ GUnixConnectionPrivate g_unix_connection_get_type
+
+gvsockconnection +GVsockConnection +GVsockConnection + +GVsockConnectionClass +G_IS_VSOCK_CONNECTION +G_IS_VSOCK_CONNECTION_CLASS +G_TYPE_VSOCK_CONNECTION +G_VSOCK_CONNECTION +G_VSOCK_CONNECTION_CLASS +G_VSOCK_CONNECTION_GET_CLASS + +GVsockConnectionPrivate +g_vsock_connection_get_type +
+ +GUnixConnection
gtcpconnection GTcpConnection diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index d20f419afabb5e653353e9ed41a4fe9e80c46f7e..fb31280930ccf44694ec5e46ba465ca4f2444d9e 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -922,6 +922,7 @@ GLIB_SYSDEF_POLLPRI GLIB_SYSDEF_AF_INET GLIB_SYSDEF_AF_INET6 GLIB_SYSDEF_AF_UNIX +GLIB_SYSDEF_AF_VSOCK GLIB_SYSDEF_MSG_DONTROUTE GLIB_SYSDEF_MSG_OOB GLIB_SYSDEF_MSG_PEEK diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index d26c4d25f1f4bc9ebc818d287e9d4895adc7c0f7..a28b46e97b6435a57ea396672bf7bef52df9d2c4 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -46,6 +46,10 @@ #include #include #endif +#ifdef HAVE_VSOCK +#include +#include +#endif #ifdef G_OS_WIN32 #include @@ -191,6 +195,80 @@ is_valid_unix (const gchar *address_entry, return ret; } +#ifdef HAVE_VSOCK +static gboolean +is_valid_vsock (const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + gboolean ret; + GList *keys; + GList *l; + const gchar *cid; + const gchar *port; + gchar *endp; + + ret = FALSE; + cid = NULL; + port = 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, "cid") == 0) + cid = 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, "guid") != 0) + { + 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) + { + strtoul (port, &endp, 10); + if ((*port == '\0' || *endp != '\0')) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address “%s” — the “%s” attribute is malformed"), + address_entry, "port"); + goto out; + } + } + + if (cid != NULL) + { + strtoul (port, &endp, 10); + if ((*port == '\0' || *endp != '\0')) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address “%s” — the “%s” attribute is malformed"), + address_entry, "cid"); + goto out; + } + } + + ret = TRUE; + + out: + g_list_free (keys); + + return ret; +} +#endif + static gboolean is_valid_nonce_tcp (const gchar *address_entry, GHashTable *key_value_pairs, @@ -413,6 +491,10 @@ g_dbus_is_supported_address (const gchar *string, supported = is_valid_nonce_tcp (a[n], key_value_pairs, error); else if (g_strcmp0 (a[n], "autolaunch:") == 0) supported = TRUE; +#ifdef HAVE_VSOCK + else if (g_strcmp0 (transport_name, "vsock") == 0) + supported = is_valid_vsock (a[n], key_value_pairs, error); +#endif else g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Unknown or unsupported transport “%s” for address “%s”"), @@ -605,6 +687,45 @@ g_dbus_address_connect (const gchar *address_entry, g_assert_not_reached (); } } +#endif +#ifdef HAVE_VSOCK + else if (g_strcmp0 (transport_name, "vsock") == 0) + { + const gchar *s; + struct sockaddr_vm svm; + gchar *endp; + + memset (&svm, 0, sizeof (svm)); + svm.svm_family = AF_VSOCK; + + s = g_hash_table_lookup (key_value_pairs, "cid"); + if (s != NULL) + svm.svm_cid = strtoul (s, &endp, 10); + if (s == NULL || (*s == '\0' || *endp != '\0')) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address “%s” — the cid attribute is missing or malformed"), + address_entry); + goto out; + } + + s = g_hash_table_lookup (key_value_pairs, "port"); + if (s != NULL) + svm.svm_port = strtoul (s, &endp, 10); + if (s == NULL || (*s == '\0' || *endp != '\0')) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("Error in address “%s” — the port attribute is missing or malformed"), + address_entry); + goto out; + } + + connectable = G_SOCKET_CONNECTABLE (g_socket_address_new_from_native (&svm, sizeof (svm))); + } #endif else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0) { diff --git a/gio/gdbusserver.c b/gio/gdbusserver.c index 9009734d521d7ed763874e4b42a37193a2239cf1..7e37ae03db08f1e614daf1421b4b40994a6109b1 100644 --- a/gio/gdbusserver.c +++ b/gio/gdbusserver.c @@ -54,6 +54,10 @@ #ifdef G_OS_UNIX #include "gunixsocketaddress.h" #endif +#ifdef HAVE_VSOCK +#include +#include +#endif #include "glibintl.h" @@ -813,6 +817,63 @@ try_unix (GDBusServer *server, } #endif +#ifdef HAVE_VSOCK +/* note that address_entry has already been validated */ +static gboolean +try_vsock (GDBusServer *server, + const gchar *address_entry, + GHashTable *key_value_pairs, + GError **error) +{ + GSocketAddress *socket_address; + GSocketAddress *effective_address; + gboolean ret; + struct sockaddr_vm svm; + const gchar *cid; + const gchar *port; + + ret = FALSE; + + cid = g_hash_table_lookup (key_value_pairs, "cid"); + port = g_hash_table_lookup (key_value_pairs, "port"); + + memset (&svm, 0, sizeof (svm)); + svm.svm_family = AF_VSOCK; + svm.svm_cid = VMADDR_CID_LOCAL; + if (cid != NULL) + svm.svm_cid = strtoul (cid, NULL, 10); + svm.svm_port = VMADDR_PORT_ANY; + if (port != NULL) + svm.svm_port = strtoul (port, NULL, 10); + + socket_address = g_socket_address_new_from_native (&svm, sizeof (svm)); + if (!g_socket_listener_add_address (server->listener, + socket_address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, /* GObject *source_object */ + &effective_address, + error)) + { + g_object_unref (socket_address); + goto out; + } + + if (svm.svm_port == VMADDR_PORT_ANY) + svm.svm_port = g_vsock_socket_address_get_port (G_VSOCK_SOCKET_ADDRESS (effective_address)); + + g_object_unref (effective_address); + g_object_unref (socket_address); + + server->client_address = g_strdup_printf ("vsock:cid=%u,port=%u", svm.svm_cid, svm.svm_port); + server->is_using_listener = TRUE; + ret = TRUE; + + out: + return ret; +} +#endif + /* ---------------------------------------------------------------------------------------------------- */ /* note that address_entry has already been validated => @@ -1131,6 +1192,10 @@ initable_init (GInitable *initable, #ifdef G_OS_UNIX else if (g_strcmp0 (transport_name, "unix") == 0) ret = try_unix (server, address_entry, key_value_pairs, &this_error); +#endif +#ifdef HAVE_VSOCK + else if (g_strcmp0 (transport_name, "vsock") == 0) + ret = try_vsock (server, address_entry, key_value_pairs, &this_error); #endif else if (g_strcmp0 (transport_name, "tcp") == 0) ret = try_tcp (server, address_entry, key_value_pairs, FALSE, &this_error); diff --git a/gio/gio.h b/gio/gio.h index f5d2dd5a36a4cbbbd255160bba2b770e17554617..f8b745428e4d03391e620c5b6fdc88e3e9f53123 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -167,6 +167,8 @@ #include #include #include +#include +#include #include #include diff --git a/gio/gioenums.h b/gio/gioenums.h index 2692b746d72052e680841347ecce76c0d1201984..8efaef32d48d3b87dcf378cbd50d85dd2f7ced97 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -507,6 +507,7 @@ typedef enum { * value, which has this more logical name. Since 2.44. * @G_IO_ERROR_NOT_CONNECTED: Transport endpoint is not connected. Since 2.44 * @G_IO_ERROR_MESSAGE_TOO_LARGE: Message too large. Since 2.48. + * @G_IO_ERROR_ADDRESS_NOT_AVAILABLE: Cannot assign requested address. Since 2.68. * * Error codes returned by GIO functions. * @@ -575,7 +576,8 @@ typedef enum { G_IO_ERROR_BROKEN_PIPE, G_IO_ERROR_CONNECTION_CLOSED = G_IO_ERROR_BROKEN_PIPE, G_IO_ERROR_NOT_CONNECTED, - G_IO_ERROR_MESSAGE_TOO_LARGE + G_IO_ERROR_MESSAGE_TOO_LARGE, + G_IO_ERROR_ADDRESS_NOT_AVAILABLE } GIOErrorEnum; @@ -812,10 +814,11 @@ typedef enum /*< flags >*/ { * @G_SOCKET_FAMILY_IPV4: the IPv4 family * @G_SOCKET_FAMILY_IPV6: the IPv6 family * @G_SOCKET_FAMILY_UNIX: the UNIX domain family + * @G_SOCKET_FAMILY_VSOCK: the VSOCK domain family. Since: 2.68 * * The protocol family of a #GSocketAddress. (These values are - * identical to the system defines %AF_INET, %AF_INET6 and %AF_UNIX, - * if available.) + * identical to the system defines %AF_INET, %AF_INET6, %AF_UNIX, + * and %AF_VSOCK if available.) * * Since: 2.22 */ @@ -823,7 +826,8 @@ typedef enum { G_SOCKET_FAMILY_INVALID, G_SOCKET_FAMILY_UNIX = GLIB_SYSDEF_AF_UNIX, G_SOCKET_FAMILY_IPV4 = GLIB_SYSDEF_AF_INET, - G_SOCKET_FAMILY_IPV6 = GLIB_SYSDEF_AF_INET6 + G_SOCKET_FAMILY_IPV6 = GLIB_SYSDEF_AF_INET6, + G_SOCKET_FAMILY_VSOCK = GLIB_SYSDEF_AF_VSOCK } GSocketFamily; /** diff --git a/gio/gioerror.c b/gio/gioerror.c index 477906c0c606b85e8e670d251dd5a3f14fc3f8c1..120c234ff3dc28b3bd1ce7ccae2324eac5d50a8b 100644 --- a/gio/gioerror.c +++ b/gio/gioerror.c @@ -278,6 +278,12 @@ g_io_error_from_errno (gint err_no) break; #endif +#ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: + return G_IO_ERROR_ADDRESS_NOT_AVAILABLE; + break; +#endif + default: return G_IO_ERROR_FAILED; break; @@ -366,6 +372,11 @@ g_io_error_from_win32_error (gint error_code) case WSAEMSGSIZE: return G_IO_ERROR_MESSAGE_TOO_LARGE; +#ifdef WSAEADDRNOTAVAIL + case WSAEADDRNOTAVAIL: + return G_IO_ERROR_ADDRESS_NOT_AVAILABLE; +#endif + default: return G_IO_ERROR_FAILED; } diff --git a/gio/giotypes.h b/gio/giotypes.h index 995c9cb152aeefb76a07eb520ab869d1c4badf89..d7f0a67d3d2d4cdb49127f537c45eb49848fa7dc 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -105,6 +105,7 @@ typedef struct _GInetAddress GInetAddress; typedef struct _GInetAddressMask GInetAddressMask; typedef struct _GInetSocketAddress GInetSocketAddress; typedef struct _GNativeSocketAddress GNativeSocketAddress; +typedef struct _GVsockSocketAddress GVsockSocketAddress; typedef struct _GInputStream GInputStream; typedef struct _GInitable GInitable; typedef struct _GIOModule GIOModule; diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c index ef1940a920083b9e7a0a6ca8617e4cc956e078be..baf1d5e2afafa99de7615ed3d14aee7025735507 100644 --- a/gio/gnetworkaddress.c +++ b/gio/gnetworkaddress.c @@ -702,6 +702,7 @@ list_split_families (GList *list, break; case G_SOCKET_FAMILY_INVALID: case G_SOCKET_FAMILY_UNIX: + case G_SOCKET_FAMILY_VSOCK: g_assert_not_reached (); } diff --git a/gio/gsocket.c b/gio/gsocket.c index 52a198378ca83f73ff028f11ab3232a3d39cf539..c0252b578159552ceadf24be5dc7fd110ff9c89c 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -540,8 +540,9 @@ g_socket_details_from_fd (GSocket *socket) } break; + case G_SOCKET_FAMILY_VSOCK: case G_SOCKET_FAMILY_UNIX: - socket->priv->family = G_SOCKET_FAMILY_UNIX; + socket->priv->family = family; socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT; break; diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c index 2b7e83ccf98a2aae5e3087fd8d545542280335a3..5fbc5ef3675e4bb84d558608a01567b9c6cacff9 100644 --- a/gio/gsocketaddress.c +++ b/gio/gsocketaddress.c @@ -27,6 +27,7 @@ #include "ginetaddress.h" #include "ginetsocketaddress.h" #include "gnativesocketaddress.h" +#include "gvsocksocketaddress.h" #include "gnetworkingprivate.h" #include "gproxyaddress.h" #include "gproxyaddressenumerator.h" @@ -39,6 +40,10 @@ #include "gunixsocketaddress.h" #endif +#ifdef HAVE_VSOCK +#include +#endif + /** * SECTION:gsocketaddress @@ -300,6 +305,14 @@ g_socket_address_new_from_native (gpointer native, return g_unix_socket_address_new (addr->sun_path); } #endif +#ifdef HAVE_VSOCK + if (family == AF_VSOCK) + { + struct sockaddr_vm *addr = (struct sockaddr_vm *) native; + + return g_vsock_socket_address_new (addr->svm_cid, addr->svm_port); + } +#endif return g_native_socket_address_new (native, len); } diff --git a/gio/gsocketconnection.c b/gio/gsocketconnection.c index 37d5d330cf4ca9e0cab7210565727705c8b2bac9..36c4d3ee4c5fe190b58b5fe82da63fce5401b04f 100644 --- a/gio/gsocketconnection.c +++ b/gio/gsocketconnection.c @@ -34,6 +34,7 @@ #include #include "gunixconnection.h" #include "gtcpconnection.h" +#include "gvsockconnection.h" #include "glibintl.h" @@ -615,6 +616,9 @@ g_socket_connection_factory_register_type (GType g_type, static void init_builtin_types (void) { +#ifdef HAVE_VSOCK + g_type_ensure (G_TYPE_VSOCK_CONNECTION); +#endif #ifndef G_OS_WIN32 g_type_ensure (G_TYPE_UNIX_CONNECTION); #endif diff --git a/gio/gvsockconnection.c b/gio/gvsockconnection.c new file mode 100644 index 0000000000000000000000000000000000000000..3db53cce0989f88dea9f12d553414b615c2b460a --- /dev/null +++ b/gio/gvsockconnection.c @@ -0,0 +1,55 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2021 Red Hat, Inc. + * + * 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.1 of the License, or (at your option) any later version. + * + * See the included COPYING file for more information. + * + * Authors: Marc-André Lureau + */ + +#include "config.h" + +#include "gvsockconnection.h" + +/** + * SECTION:gvsockconnection + * @title: GVsockConnection + * @short_description: A VSOCK domain GSocketConnection + * @include: gio/gvsockconnection.h + * @see_also: #GSocketConnection. + * + * This is the subclass of #GSocketConnection that is created + * for VSOCK domain sockets. + * + * Since: 2.68 + */ + +/** + * GVsockConnection: + * + * #GVsockConnection is an opaque data structure. + **/ + +G_DEFINE_TYPE_WITH_CODE (GVsockConnection, + g_vsock_connection, + G_TYPE_SOCKET_CONNECTION, + g_socket_connection_factory_register_type (g_define_type_id, + G_SOCKET_FAMILY_VSOCK, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT); +); + +static void +g_vsock_connection_init (GVsockConnection *connection) +{ +} + +static void +g_vsock_connection_class_init (GVsockConnectionClass *class) +{ +} diff --git a/gio/gvsockconnection.h b/gio/gvsockconnection.h new file mode 100644 index 0000000000000000000000000000000000000000..32dd9fc120ae2e69e95fe87c9926b64ebc1148a2 --- /dev/null +++ b/gio/gvsockconnection.h @@ -0,0 +1,63 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2021 Red Hat, Inc. + * + * 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.1 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, see . + * + * Authors: Marc-André Lureau + */ + +#ifndef __G_VSOCK_CONNECTION_H__ +#define __G_VSOCK_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_VSOCK_CONNECTION (g_vsock_connection_get_type ()) +#define G_VSOCK_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_VSOCK_CONNECTION, GVsockConnection)) +#define G_VSOCK_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_VSOCK_CONNECTION, GVsockConnectionClass)) +#define G_IS_VSOCK_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_VSOCK_CONNECTION)) +#define G_IS_VSOCK_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_VSOCK_CONNECTION)) +#define G_VSOCK_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_VSOCK_CONNECTION, GVsockConnectionClass)) + +typedef struct _GVsockConnection GVsockConnection; +typedef struct _GVsockConnectionPrivate GVsockConnectionPrivate; +typedef struct _GVsockConnectionClass GVsockConnectionClass; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVsockConnection, g_object_unref) + +struct _GVsockConnectionClass +{ + GSocketConnectionClass parent_class; +}; + +struct _GVsockConnection +{ + GSocketConnection parent_instance; + GVsockConnectionPrivate *priv; +}; + +GLIB_AVAILABLE_IN_2_68 +GType g_vsock_connection_get_type (void); + + +G_END_DECLS + +#endif /* __G_VSOCK_CONNECTION_H__ */ diff --git a/gio/gvsocksocketaddress.c b/gio/gvsocksocketaddress.c new file mode 100644 index 0000000000000000000000000000000000000000..78155b892fa860fe706ac5c9aad919eb0c550e6a --- /dev/null +++ b/gio/gvsocksocketaddress.c @@ -0,0 +1,300 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2021 Red Hat, Inc. + * + * 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.1 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, see . + * + * Authors: Marc-André Lureau + */ + +#include +#include +#include + +#include "gvsocksocketaddress.h" +#include "gnetworkingprivate.h" +#include "gsocketconnectable.h" +#include "gioerror.h" +#include "glibintl.h" + +#ifdef HAVE_VSOCK +#include +#endif + +/** + * SECTION:gvsocksocketaddress + * @short_description: VSOCK GSocketAddress + * @include: gio/gio.h + * + * An VSOCK socket address; that is, the combination of a Context Identifier + * (CID) and a port number. + */ + +/** + * GVsockSocketAddress: + * + * A VSOCK socket address, corresponding to a struct sockaddr_vm. + * + * Since: 2.68 + */ + +struct _GVsockSocketAddressPrivate +{ + guint cid; + guint port; +}; + +static void g_vsock_socket_address_connectable_iface_init (GSocketConnectableIface *iface); +static gchar *g_vsock_socket_address_connectable_to_string (GSocketConnectable *connectable); + +G_DEFINE_TYPE_WITH_CODE (GVsockSocketAddress, g_vsock_socket_address, G_TYPE_SOCKET_ADDRESS, + G_ADD_PRIVATE (GVsockSocketAddress) + G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE, + g_vsock_socket_address_connectable_iface_init)) + +enum { + PROP_0, + PROP_CID, + PROP_PORT, +}; + +static void +g_vsock_socket_address_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GVsockSocketAddress *address = G_VSOCK_SOCKET_ADDRESS (object); + + switch (prop_id) + { + case PROP_CID: + g_value_set_uint (value, address->priv->cid); + break; + + case PROP_PORT: + g_value_set_uint (value, address->priv->port); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_vsock_socket_address_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GVsockSocketAddress *address = G_VSOCK_SOCKET_ADDRESS (object); + + switch (prop_id) + { + case PROP_CID: + address->priv->cid = g_value_get_uint (value); + break; + + case PROP_PORT: + address->priv->port = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GSocketFamily +g_vsock_socket_address_get_family (GSocketAddress *address) +{ + g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0); + + return G_SOCKET_FAMILY_VSOCK; +} + +static gssize +g_vsock_socket_address_get_native_size (GSocketAddress *address) +{ +#ifdef HAVE_VSOCK + g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0); + + return sizeof (struct sockaddr_vm); +#else + g_warning_once ("VSOCK is not supported on this platform yet."); + return -1; +#endif +} + +static gboolean +g_vsock_socket_address_to_native (GSocketAddress *address, + gpointer dest, + gsize destlen, + GError **error) +{ +#ifdef HAVE_VSOCK + GVsockSocketAddress *addr; + struct sockaddr_vm *sock = (struct sockaddr_vm *) dest; + + g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), FALSE); + + addr = G_VSOCK_SOCKET_ADDRESS (address); + if (destlen < sizeof (*sock)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, + _("Not enough space for socket address")); + return FALSE; + } + + memset (sock, 0, sizeof (*sock)); + sock->svm_family = AF_VSOCK; + sock->svm_cid = addr->priv->cid; + sock->svm_port = addr->priv->port; + return TRUE; +#else + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("VSOCK is not support for this platform yet.")); + return FALSE; +#endif +} + +static void +g_vsock_socket_address_class_init (GVsockSocketAddressClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GSocketAddressClass *gsocketaddress_class = G_SOCKET_ADDRESS_CLASS (klass); + + gobject_class->set_property = g_vsock_socket_address_set_property; + gobject_class->get_property = g_vsock_socket_address_get_property; + + gsocketaddress_class->get_family = g_vsock_socket_address_get_family; + gsocketaddress_class->to_native = g_vsock_socket_address_to_native; + gsocketaddress_class->get_native_size = g_vsock_socket_address_get_native_size; + + /** + * GVsockSocketAddress:cid: + * + * The `svm_cid` field, for VSOCK addresses. + * + * Since: 2.68 + */ + g_object_class_install_property (gobject_class, PROP_CID, + g_param_spec_uint ("cid", + P_("CID"), + P_("The Context Identifier (CID)"), + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * GVsockSocketAddress:port: + * + * The `svm_port` field, for VSOCK addresses. + * + * Since: 2.68 + */ + g_object_class_install_property (gobject_class, PROP_PORT, + g_param_spec_uint ("port", + P_("Port"), + P_("The port"), + 0, + G_MAXUINT, + 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void +g_vsock_socket_address_connectable_iface_init (GSocketConnectableIface *iface) +{ + GSocketConnectableIface *parent_iface = g_type_interface_peek_parent (iface); + + iface->enumerate = parent_iface->enumerate; + iface->proxy_enumerate = parent_iface->proxy_enumerate; + iface->to_string = g_vsock_socket_address_connectable_to_string; +} + +static gchar * +g_vsock_socket_address_connectable_to_string (GSocketConnectable *connectable) +{ + GVsockSocketAddress *sa = G_VSOCK_SOCKET_ADDRESS (connectable); + + return g_strdup_printf ("vsock:cid=%u,port=%u", sa->priv->cid, sa->priv->port); +} + +static void +g_vsock_socket_address_init (GVsockSocketAddress *address) +{ + address->priv = g_vsock_socket_address_get_instance_private (address); +} + +/** + * g_vsock_socket_address_new: + * @cid: a Context Identifier (CID) + * @port: a port number + * + * Creates a new #GVsockSocketAddress for @cid and @port. + * + * Returns: a new #GVsockSocketAddress + * + * Since: 2.68 + */ +GSocketAddress * +g_vsock_socket_address_new (guint cid, + guint port) +{ + return g_object_new (G_TYPE_VSOCK_SOCKET_ADDRESS, + "cid", cid, + "port", port, + NULL); +} + +/** + * g_vsock_socket_address_get_cid: + * @address: a #GVsockSocketAddress + * + * Gets @address's Context Identifier (CID). + * + * Returns: the Context Identifier (CID) for @address + * + * Since: 2.68 + */ +guint +g_vsock_socket_address_get_cid (GVsockSocketAddress *address) +{ + g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0); + + return address->priv->cid; +} + +/** + * g_vsock_socket_address_get_port: + * @address: a #GVsockSocketAddress + * + * Gets @address's port. + * + * Returns: the port for @address + * + * Since: 2.68 + */ +guint +g_vsock_socket_address_get_port (GVsockSocketAddress *address) +{ + g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0); + + return address->priv->port; +} diff --git a/gio/gvsocksocketaddress.h b/gio/gvsocksocketaddress.h new file mode 100644 index 0000000000000000000000000000000000000000..8e76d70686c5ee3f290a8f6e8c960f0655d22426 --- /dev/null +++ b/gio/gvsocksocketaddress.h @@ -0,0 +1,70 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2021 Red Hat, Inc. + * + * 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.1 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, see . + * + * Authors: Marc-André Lureau + */ + +#ifndef __G_VSOCK_SOCKET_ADDRESS_H__ +#define __G_VSOCK_SOCKET_ADDRESS_H__ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define G_TYPE_VSOCK_SOCKET_ADDRESS (g_vsock_socket_address_get_type ()) +#define G_VSOCK_SOCKET_ADDRESS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_VSOCK_SOCKET_ADDRESS, GVsockSocketAddress)) +#define G_VSOCK_SOCKET_ADDRESS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_VSOCK_SOCKET_ADDRESS, GVsockSocketAddressClass)) +#define G_IS_VSOCK_SOCKET_ADDRESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_VSOCK_SOCKET_ADDRESS)) +#define G_IS_VSOCK_SOCKET_ADDRESS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_VSOCK_SOCKET_ADDRESS)) +#define G_VSOCK_SOCKET_ADDRESS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_VSOCK_SOCKET_ADDRESS, GVsockSocketAddressClass)) + +typedef struct _GVsockSocketAddressClass GVsockSocketAddressClass; +typedef struct _GVsockSocketAddressPrivate GVsockSocketAddressPrivate; + +struct _GVsockSocketAddress +{ + GSocketAddress parent_instance; + + /*< private >*/ + GVsockSocketAddressPrivate *priv; +}; + +struct _GVsockSocketAddressClass +{ + GSocketAddressClass parent_class; +}; + +GLIB_AVAILABLE_IN_2_68 +GType g_vsock_socket_address_get_type (void) G_GNUC_CONST; + +GLIB_AVAILABLE_IN_2_68 +GSocketAddress *g_vsock_socket_address_new (guint cid, + guint port); + +GLIB_AVAILABLE_IN_2_68 +guint g_vsock_socket_address_get_cid (GVsockSocketAddress *address); + +GLIB_AVAILABLE_IN_2_68 +guint g_vsock_socket_address_get_port (GVsockSocketAddress *address); + +G_END_DECLS + +#endif /* __G_VSOCK_SOCKET_ADDRESS_H__ */ diff --git a/gio/meson.build b/gio/meson.build index 8e039b68c61c89d816698ab2110f6a61ecfab31f..b4cca8d1ab186df34ce939391c5962d798bea806 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -580,6 +580,8 @@ gio_sources = files( 'gvfs.c', 'gvolume.c', 'gvolumemonitor.c', + 'gvsockconnection.c', + 'gvsocksocketaddress.c', 'gzlibcompressor.c', 'gzlibdecompressor.c', 'glistmodel.c', @@ -714,6 +716,8 @@ gio_headers = files( 'gvfs.h', 'gvolume.h', 'gvolumemonitor.h', + 'gvsockconnection.h', + 'gvsocksocketaddress.h', 'gzlibcompressor.h', 'gzlibdecompressor.h', 'glistmodel.h', diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 23a22981a05151c8f59a367d18ee9f2d32e8f55f..ac8538d9408ec3290e5c8bdc0f619253d3ab7435 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -1736,6 +1736,98 @@ test_tcp_anonymous (void) g_free (test_guid); } +#ifdef HAVE_VSOCK +static gpointer +vsock_service_thread_func (gpointer user_data) +{ + gboolean *seen_connection = user_data; + GMainContext *service_context; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + error = NULL; + server = g_dbus_server_new_sync ("vsock:", + G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, + test_guid, + NULL, /* GDBusObserver* */ + NULL, /* GCancellable* */ + &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_NOT_AVAILABLE)) + { + g_test_skip ("VSOCK not enabled or not avaialble"); + goto run_loop; + } + g_assert_no_error (error); + + g_signal_connect (server, + "new-connection", + G_CALLBACK (tcp_anonymous_on_new_connection), + seen_connection); + + g_dbus_server_start (server); + +run_loop: + run_service_loop (service_context); + + g_main_context_pop_thread_default (service_context); + + teardown_service_loop (); + g_main_context_unref (service_context); + + return NULL; +} + +static void +test_vsock (void) +{ + gboolean seen_connection; + GThread *service_thread; + GDBusConnection *connection; + GError *error; + + test_guid = g_dbus_generate_guid (); + loop = g_main_loop_new (NULL, FALSE); + + seen_connection = FALSE; + service_thread = g_thread_new ("vsock-service", + vsock_service_thread_func, + &seen_connection); + await_service_loop (); + if (server == NULL) + goto end; + + error = NULL; + connection = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* GDBusAuthObserver* */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (connection != NULL); + + while (!seen_connection) + g_thread_yield (); + + g_object_unref (connection); + +end: + g_main_loop_quit (service_loop); + if (server) + { + g_dbus_server_stop (server); + g_object_unref (server); + server = NULL; + } + + g_thread_join (service_thread); + + g_main_loop_unref (loop); + g_free (test_guid); +} +#endif + /* ---------------------------------------------------------------------------------------------------- */ static GDBusServer *codegen_server = NULL; @@ -2017,6 +2109,9 @@ main (int argc, g_test_add_func ("/gdbus/tcp-anonymous", test_tcp_anonymous); g_test_add_func ("/gdbus/credentials", test_credentials); g_test_add_func ("/gdbus/codegen-peer-to-peer", codegen_test_peer); +#ifdef HAVE_VSOCK + g_test_add_func ("/gdbus/vsock", test_vsock); +#endif ret = g_test_run (); diff --git a/gio/tests/socket.c b/gio/tests/socket.c index 683866ede37ff809ea5534ad7857aa4a8c6d0049..7a272c8d3cd9c4123b53ca28ce2813439b76a26c 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -16,6 +16,7 @@ * Public License along with this library; if not, see . */ +#include "config.h" #include #include @@ -27,8 +28,10 @@ #include #include #endif - -#include "gnetworkingprivate.h" +#include +#ifdef HAVE_VSOCK +#include +#endif static gboolean ipv6_supported; @@ -1306,6 +1309,9 @@ static void test_sockaddr (void) { struct sockaddr_in6 sin6, gsin6; +#ifdef HAVE_VSOCK + struct sockaddr_vm svm; +#endif GSocketAddress *saddr; GInetSocketAddress *isaddr; GInetAddress *iaddr; @@ -1339,8 +1345,68 @@ test_sockaddr (void) g_assert_cmpint (sin6.sin6_flowinfo, ==, gsin6.sin6_flowinfo); g_object_unref (saddr); + +#ifdef HAVE_VSOCK + memset (&svm, 0, sizeof (svm)); + svm.svm_family = AF_VSOCK; + svm.svm_port = 42; + svm.svm_cid = 1729; + + saddr = g_socket_address_new_from_native (&svm, sizeof (svm)); + g_assert (G_IS_VSOCK_SOCKET_ADDRESS (saddr)); + g_assert_cmpint (g_socket_address_get_family (saddr), ==, AF_VSOCK); + g_object_unref (saddr); +#endif +} + +#ifdef HAVE_VSOCK +static void +test_vsock_from_fd (void) +{ + gint fd; + GError *error; + GSocket *s; + + fd = socket (AF_VSOCK, SOCK_STREAM, 0); + g_assert_cmpint (fd, !=, -1); + + error = NULL; + s = g_socket_new_from_fd (fd, &error); + g_assert_no_error (error); + g_assert_cmpint (g_socket_get_family (s), ==, G_SOCKET_FAMILY_VSOCK); + g_assert_cmpint (g_socket_get_socket_type (s), ==, G_SOCKET_TYPE_STREAM); + g_assert_cmpint (g_socket_get_protocol (s), ==, G_SOCKET_PROTOCOL_DEFAULT); + g_object_unref (s); } +static void +test_vsock_connection (void) +{ + gint fd; + GError *error; + GSocket *s; + GSocketConnection *c; + GSocketAddress *addr; + + fd = socket (AF_VSOCK, SOCK_STREAM, 0); + g_assert_cmpint (fd, !=, -1); + + error = NULL; + s = g_socket_new_from_fd (fd, &error); + g_assert_no_error (error); + c = g_socket_connection_factory_create_connection (s); + g_assert (G_IS_VSOCK_CONNECTION (c)); + + addr = g_socket_connection_get_local_address (c, &error); + g_assert_no_error (error); + g_assert (G_IS_VSOCK_SOCKET_ADDRESS (addr)); + + g_object_unref (addr); + g_object_unref (c); + g_object_unref (s); +} +#endif + #ifdef G_OS_UNIX static void test_unix_from_fd (void) @@ -2154,6 +2220,10 @@ main (int argc, g_test_add_func ("/socket/unix-connection", test_unix_connection); g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data); g_test_add_func ("/socket/source-postmortem", test_source_postmortem); +#endif +#ifdef HAVE_VSOCK + g_test_add_func ("/socket/vsock-from-fd", test_vsock_from_fd); + g_test_add_func ("/socket/vsock-connection", test_vsock_connection); #endif g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp); g_test_add_func ("/socket/reuse/udp", test_reuse_udp); diff --git a/glib/glibconfig.h.in b/glib/glibconfig.h.in index 873cb03142dcc346003e19fa655079e861eea091..613f1be17ab7d595ad6c2206531467db30c40f0c 100644 --- a/glib/glibconfig.h.in +++ b/glib/glibconfig.h.in @@ -193,6 +193,7 @@ typedef @g_pid_type@ GPid; #define GLIB_SYSDEF_AF_UNIX @g_af_unix@ #define GLIB_SYSDEF_AF_INET @g_af_inet@ #define GLIB_SYSDEF_AF_INET6 @g_af_inet6@ +#define GLIB_SYSDEF_AF_VSOCK @g_af_vsock@ #define GLIB_SYSDEF_MSG_OOB @g_msg_oob@ #define GLIB_SYSDEF_MSG_PEEK @g_msg_peek@ diff --git a/meson.build b/meson.build index d7d64118d75a91fe8f1e0fd040ee1dc08016bed4..f66c7f0f18cee207e0fda0ed5f80b52cb4e8440c 100644 --- a/meson.build +++ b/meson.build @@ -333,6 +333,9 @@ endif if cc.has_header('linux/netlink.h') glib_conf.set('HAVE_NETLINK', 1) endif +if cc.has_header('linux/vm_sockets.h') + glib_conf.set('HAVE_VSOCK', 1) +endif # Is statx() supported? Android systems don’t reliably support it as of August 2020. statx_code = ''' @@ -1761,6 +1764,27 @@ inet_defines = [ [ 'MSG_PEEK', 'g_msg_peek' ], [ 'MSG_DONTROUTE', 'g_msg_dontroute' ], ] + +if glib_conf.has('HAVE_VSOCK') + vsock_defines = [ + [ 'VMADDR_CID_ANY', '-1U' ], + [ 'VMADDR_PORT_ANY', '-1U' ], + [ 'VMADDR_CID_HYPERVISOR', '0' ], + [ 'VMADDR_CID_LOCAL', '1' ], + [ 'VMADDR_CID_HOST', '2' ], + ] + foreach d : vsock_defines + if not cc.has_header_symbol('linux/vm_sockets.h', d[0], prefix: inet_includes) + glib_conf.set(d[0], d[1]) + endif + endforeach + inet_defines += [ [ 'AF_VSOCK', 'g_af_vsock' ] ] + inet_includes += ''' + #include ''' +else + glibconfig_conf.set('g_af_vsock', '-1') +endif + foreach d : inet_defines val = cc.compute_int(d[0], prefix: inet_includes) glibconfig_conf.set(d[1], val)