mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2026-10-24 00:49:00 +03:00
gio/vsock: add AF_VSOCK socket and address support
Add G_SOCKET_FAMILY_VSOCK and a GVsockSocketAddress type wrapping Linux struct sockaddr_vm. The new address type exposes cid, port, and flags, plus constants for common VMADDR_CID_* and VMADDR_PORT_* values. Teach native socket address conversion, g_socket_new(), and g_socket_new_from_fd() to preserve AF_VSOCK where supported, allowing the existing GSocketClient, GSocketListener, and GSocketConnection paths to work without application special casing. Add tests for native conversion, fd introspection, socket creation, and VMADDR_CID_LOCAL loopback, with graceful skips when vsock is unavailable. Fixes: #3969
This commit is contained in:
@@ -82,6 +82,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIcon, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInetAddress, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInetAddressMask, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInetSocketAddress, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVsockSocketAddress, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInitable, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOModule, g_object_unref)
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
#include <gio/gunixcredentialsmessage.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
#include <gio/gvsocksocketaddress.h>
|
||||
#include <gio/gvfs.h>
|
||||
#include <gio/gvolume.h>
|
||||
#include <gio/gvolumemonitor.h>
|
||||
|
||||
@@ -825,10 +825,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 Linux virtio-vsock family. Since 2.90
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
@@ -836,7 +837,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 GIO_AVAILABLE_ENUMERATOR_IN_2_90 = GLIB_SYSDEF_AF_VSOCK
|
||||
} GSocketFamily;
|
||||
|
||||
/**
|
||||
|
||||
@@ -94,6 +94,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;
|
||||
|
||||
@@ -706,6 +706,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 ();
|
||||
}
|
||||
|
||||
|
||||
@@ -593,6 +593,13 @@ g_socket_details_from_fd (GSocket *socket)
|
||||
socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
case G_SOCKET_FAMILY_VSOCK:
|
||||
socket->priv->family = G_SOCKET_FAMILY_VSOCK;
|
||||
socket->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
socket->priv->family = G_SOCKET_FAMILY_INVALID;
|
||||
break;
|
||||
@@ -750,6 +757,13 @@ g_socket_create_socket (GSocketFamily family,
|
||||
|
||||
if (family <= 0)
|
||||
{
|
||||
if (family == G_SOCKET_FAMILY_VSOCK)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Vsock sockets not supported on this system"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
_("Unable to create socket: %s"), _("Unknown family was specified"));
|
||||
return -1;
|
||||
|
||||
@@ -34,11 +34,16 @@
|
||||
#include "gproxyaddressenumerator.h"
|
||||
#include "gsocketaddressenumerator.h"
|
||||
#include "gsocketconnectable.h"
|
||||
#include "gvsocksocketaddress.h"
|
||||
#include "glibintl.h"
|
||||
#include "gioenumtypes.h"
|
||||
|
||||
#include "gunixsocketaddress.h"
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
#include <linux/vm_sockets.h>
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include "giowin32-afunix.h"
|
||||
#endif
|
||||
@@ -302,6 +307,20 @@ g_socket_address_new_from_native (gpointer native,
|
||||
return g_unix_socket_address_new (addr->sun_path);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
if (family == AF_VSOCK)
|
||||
{
|
||||
struct sockaddr_vm *addr = (struct sockaddr_vm *) native;
|
||||
|
||||
if (len < sizeof (*addr))
|
||||
return NULL;
|
||||
|
||||
return g_vsock_socket_address_new_with_flags (addr->svm_cid,
|
||||
addr->svm_port,
|
||||
addr->svm_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
return g_native_socket_address_new (native, len);
|
||||
}
|
||||
|
||||
|
||||
351
gio/gvsocksocketaddress.c
Normal file
351
gio/gvsocksocketaddress.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright 2026 Christian Hergert
|
||||
*
|
||||
* 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "gvsocksocketaddress.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gioerror.h"
|
||||
#include "glibintl.h"
|
||||
#include "gsocketconnectable.h"
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
#include <linux/vm_sockets.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* GVsockSocketAddress:
|
||||
*
|
||||
* A virtio-vsock socket address, corresponding to Linux `struct sockaddr_vm`.
|
||||
*
|
||||
* `GVsockSocketAddress` is available on all platforms, but can only be
|
||||
* converted to a native socket address on Linux systems with
|
||||
* `<linux/vm_sockets.h>` support.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CID,
|
||||
PROP_PORT,
|
||||
PROP_FLAGS
|
||||
};
|
||||
|
||||
struct _GVsockSocketAddressPrivate
|
||||
{
|
||||
guint32 cid;
|
||||
guint32 port;
|
||||
guint8 flags;
|
||||
};
|
||||
|
||||
static void g_vsock_socket_address_connectable_iface_init (GSocketConnectableIface *iface);
|
||||
static char *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))
|
||||
|
||||
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;
|
||||
|
||||
case PROP_FLAGS:
|
||||
address->priv->flags = (guint8) g_value_get_uchar (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case PROP_FLAGS:
|
||||
g_value_set_uchar (value, address->priv->flags);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static GSocketFamily
|
||||
g_vsock_socket_address_get_family (GSocketAddress *address)
|
||||
{
|
||||
return G_SOCKET_FAMILY_VSOCK;
|
||||
}
|
||||
|
||||
static gssize
|
||||
g_vsock_socket_address_get_native_size (GSocketAddress *address)
|
||||
{
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
return sizeof (struct sockaddr_vm);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
g_vsock_socket_address_to_native (GSocketAddress *address,
|
||||
gpointer dest,
|
||||
gsize destlen,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
GVsockSocketAddress *addr = G_VSOCK_SOCKET_ADDRESS (address);
|
||||
struct sockaddr_vm *sock = dest;
|
||||
|
||||
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;
|
||||
sock->svm_flags = addr->priv->flags;
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
_("Vsock socket addresses not supported on this system"));
|
||||
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 vsock context ID.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_CID,
|
||||
g_param_spec_uint ("cid", NULL, NULL,
|
||||
0,
|
||||
G_MAXUINT32,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GVsockSocketAddress:port:
|
||||
*
|
||||
* The vsock port.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_PORT,
|
||||
g_param_spec_uint ("port", NULL, NULL,
|
||||
0,
|
||||
G_MAXUINT32,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GVsockSocketAddress:flags:
|
||||
*
|
||||
* The vsock flags.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_FLAGS,
|
||||
g_param_spec_uchar ("flags", NULL, NULL,
|
||||
0,
|
||||
G_MAXUINT8,
|
||||
0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
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 char *
|
||||
g_vsock_socket_address_connectable_to_string (GSocketConnectable *connectable)
|
||||
{
|
||||
GVsockSocketAddress *address = G_VSOCK_SOCKET_ADDRESS (connectable);
|
||||
|
||||
return g_strdup_printf ("%u:%u", address->priv->cid, address->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: the vsock context ID
|
||||
* @port: the vsock port
|
||||
*
|
||||
* Creates a new #GVsockSocketAddress for @cid and @port.
|
||||
*
|
||||
* Returns: (transfer full): a new #GVsockSocketAddress
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
GSocketAddress *
|
||||
g_vsock_socket_address_new (guint32 cid,
|
||||
guint32 port)
|
||||
{
|
||||
return g_vsock_socket_address_new_with_flags (cid, port, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_vsock_socket_address_new_with_flags:
|
||||
* @cid: the vsock context ID
|
||||
* @port: the vsock port
|
||||
* @flags: the vsock flags
|
||||
*
|
||||
* Creates a new #GVsockSocketAddress for @cid, @port, and @flags.
|
||||
*
|
||||
* Returns: (transfer full): a new #GVsockSocketAddress
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
GSocketAddress *
|
||||
g_vsock_socket_address_new_with_flags (guint32 cid,
|
||||
guint32 port,
|
||||
guint8 flags)
|
||||
{
|
||||
return g_object_new (G_TYPE_VSOCK_SOCKET_ADDRESS,
|
||||
"cid", cid,
|
||||
"port", port,
|
||||
"flags", flags,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_vsock_socket_address_get_cid:
|
||||
* @address: a #GVsockSocketAddress
|
||||
*
|
||||
* Gets @address's vsock context ID.
|
||||
*
|
||||
* Returns: the vsock context ID
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
guint32
|
||||
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 vsock port.
|
||||
*
|
||||
* Returns: the vsock port
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
guint32
|
||||
g_vsock_socket_address_get_port (GVsockSocketAddress *address)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0);
|
||||
|
||||
return address->priv->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_vsock_socket_address_get_flags:
|
||||
* @address: a #GVsockSocketAddress
|
||||
*
|
||||
* Gets @address's vsock flags.
|
||||
*
|
||||
* Returns: the vsock flags
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
guint8
|
||||
g_vsock_socket_address_get_flags (GVsockSocketAddress *address)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_VSOCK_SOCKET_ADDRESS (address), 0);
|
||||
|
||||
return address->priv->flags;
|
||||
}
|
||||
118
gio/gvsocksocketaddress.h
Normal file
118
gio/gvsocksocketaddress.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright 2026 Christian Hergert
|
||||
*
|
||||
* 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef __G_VSOCK_SOCKET_ADDRESS_H__
|
||||
#define __G_VSOCK_SOCKET_ADDRESS_H__
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gsocketaddress.h>
|
||||
|
||||
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))
|
||||
|
||||
/**
|
||||
* G_VSOCK_SOCKET_ADDRESS_CID_ANY:
|
||||
*
|
||||
* The wildcard VM address, mapping to Linux `VMADDR_CID_ANY`.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
#define G_VSOCK_SOCKET_ADDRESS_CID_ANY ((guint32) -1) GIO_AVAILABLE_MACRO_IN_2_90
|
||||
|
||||
/**
|
||||
* G_VSOCK_SOCKET_ADDRESS_CID_HYPERVISOR:
|
||||
*
|
||||
* The hypervisor VM address, mapping to Linux `VMADDR_CID_HYPERVISOR`.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
#define G_VSOCK_SOCKET_ADDRESS_CID_HYPERVISOR ((guint32) 0) GIO_AVAILABLE_MACRO_IN_2_90
|
||||
|
||||
/**
|
||||
* G_VSOCK_SOCKET_ADDRESS_CID_LOCAL:
|
||||
*
|
||||
* The local VM address, mapping to Linux `VMADDR_CID_LOCAL`.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
#define G_VSOCK_SOCKET_ADDRESS_CID_LOCAL ((guint32) 1) GIO_AVAILABLE_MACRO_IN_2_90
|
||||
|
||||
/**
|
||||
* G_VSOCK_SOCKET_ADDRESS_CID_HOST:
|
||||
*
|
||||
* The host VM address, mapping to Linux `VMADDR_CID_HOST`.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
#define G_VSOCK_SOCKET_ADDRESS_CID_HOST ((guint32) 2) GIO_AVAILABLE_MACRO_IN_2_90
|
||||
|
||||
/**
|
||||
* G_VSOCK_SOCKET_ADDRESS_PORT_ANY:
|
||||
*
|
||||
* The wildcard VM port, mapping to Linux `VMADDR_PORT_ANY`.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
#define G_VSOCK_SOCKET_ADDRESS_PORT_ANY ((guint32) -1) GIO_AVAILABLE_MACRO_IN_2_90
|
||||
|
||||
typedef struct _GVsockSocketAddressClass GVsockSocketAddressClass;
|
||||
typedef struct _GVsockSocketAddressPrivate GVsockSocketAddressPrivate;
|
||||
|
||||
struct _GVsockSocketAddress
|
||||
{
|
||||
GSocketAddress parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GVsockSocketAddressPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GVsockSocketAddressClass
|
||||
{
|
||||
GSocketAddressClass parent_class;
|
||||
};
|
||||
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
GType g_vsock_socket_address_get_type (void) G_GNUC_CONST;
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
GSocketAddress *g_vsock_socket_address_new (guint32 cid,
|
||||
guint32 port);
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
GSocketAddress *g_vsock_socket_address_new_with_flags (guint32 cid,
|
||||
guint32 port,
|
||||
guint8 flags);
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
guint32 g_vsock_socket_address_get_cid (GVsockSocketAddress *address);
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
guint32 g_vsock_socket_address_get_port (GVsockSocketAddress *address);
|
||||
GIO_AVAILABLE_IN_2_90
|
||||
guint8 g_vsock_socket_address_get_flags (GVsockSocketAddress *address);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_VSOCK_SOCKET_ADDRESS_H__ */
|
||||
@@ -79,6 +79,14 @@ if host_system != 'windows'
|
||||
endif
|
||||
endif
|
||||
|
||||
if host_system == 'linux' and cc.has_header('linux/vm_sockets.h')
|
||||
if cc.has_type('struct sockaddr_vm',
|
||||
prefix : '''#include <sys/socket.h>
|
||||
#include <linux/vm_sockets.h>''')
|
||||
glib_conf.set('HAVE_LINUX_VM_SOCKETS_H', 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
# dn_comp()
|
||||
if cc.links('''#include <resolv.h>
|
||||
int main (int argc, char ** argv) {
|
||||
@@ -629,6 +637,7 @@ gio_base_sources = files(
|
||||
'gunixfdlist.c',
|
||||
'gunixcredentialsmessage.c',
|
||||
'gunixsocketaddress.c',
|
||||
'gvsocksocketaddress.c',
|
||||
'gvfs.c',
|
||||
'gvolume.c',
|
||||
'gvolumemonitor.c',
|
||||
@@ -776,6 +785,7 @@ gio_headers = files(
|
||||
'gunixcredentialsmessage.h',
|
||||
'gunixfdlist.h',
|
||||
'gunixsocketaddress.h',
|
||||
'gvsocksocketaddress.h',
|
||||
'gvfs.h',
|
||||
'gvolume.h',
|
||||
'gvolumemonitor.h',
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include <gio/gunixsocketaddress.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
#include <linux/vm_sockets.h>
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_unix_socket_address_construct (void)
|
||||
@@ -106,6 +112,83 @@ test_unix_socket_address_to_string (void)
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_socket_address_construct (void)
|
||||
{
|
||||
GSocketAddress *addr = NULL;
|
||||
GVsockSocketAddress *vaddr;
|
||||
|
||||
addr = g_vsock_socket_address_new_with_flags (G_VSOCK_SOCKET_ADDRESS_CID_HOST,
|
||||
1234,
|
||||
7);
|
||||
vaddr = G_VSOCK_SOCKET_ADDRESS (addr);
|
||||
|
||||
g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_VSOCK);
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_cid (vaddr), ==,
|
||||
G_VSOCK_SOCKET_ADDRESS_CID_HOST);
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_port (vaddr), ==, 1234);
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_flags (vaddr), ==, 7);
|
||||
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_socket_address_to_string (void)
|
||||
{
|
||||
GSocketAddress *addr = NULL;
|
||||
char *str = NULL;
|
||||
|
||||
addr = g_vsock_socket_address_new (G_VSOCK_SOCKET_ADDRESS_CID_HOST, 1234);
|
||||
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
|
||||
|
||||
g_assert_cmpstr (str, ==, "2:1234");
|
||||
|
||||
g_free (str);
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_socket_address_native (void)
|
||||
{
|
||||
GSocketAddress *addr = NULL;
|
||||
GSocketAddress *roundtrip = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
struct sockaddr_vm native;
|
||||
|
||||
addr = g_vsock_socket_address_new_with_flags (G_VSOCK_SOCKET_ADDRESS_CID_HOST,
|
||||
1234,
|
||||
7);
|
||||
g_assert_cmpint (g_socket_address_get_native_size (addr), ==, sizeof (native));
|
||||
g_assert_true (g_socket_address_to_native (addr, &native, sizeof (native), &error));
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpint (native.svm_family, ==, AF_VSOCK);
|
||||
g_assert_cmpuint (native.svm_cid, ==, G_VSOCK_SOCKET_ADDRESS_CID_HOST);
|
||||
g_assert_cmpuint (native.svm_port, ==, 1234);
|
||||
g_assert_cmpuint (native.svm_flags, ==, 7);
|
||||
|
||||
roundtrip = g_socket_address_new_from_native (&native, sizeof (native));
|
||||
g_assert_true (G_IS_VSOCK_SOCKET_ADDRESS (roundtrip));
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_cid (G_VSOCK_SOCKET_ADDRESS (roundtrip)),
|
||||
==, G_VSOCK_SOCKET_ADDRESS_CID_HOST);
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_port (G_VSOCK_SOCKET_ADDRESS (roundtrip)),
|
||||
==, 1234);
|
||||
g_assert_cmpuint (g_vsock_socket_address_get_flags (G_VSOCK_SOCKET_ADDRESS (roundtrip)),
|
||||
==, 7);
|
||||
|
||||
g_object_unref (roundtrip);
|
||||
g_object_unref (addr);
|
||||
#else
|
||||
addr = g_vsock_socket_address_new (G_VSOCK_SOCKET_ADDRESS_CID_HOST, 1234);
|
||||
g_assert_cmpint (g_socket_address_get_native_size (addr), ==, -1);
|
||||
g_assert_false (g_socket_address_to_native (addr, NULL, 0, &error));
|
||||
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
@@ -114,6 +197,9 @@ main (int argc,
|
||||
|
||||
g_test_add_func ("/socket/address/unix/construct", test_unix_socket_address_construct);
|
||||
g_test_add_func ("/socket/address/unix/to-string", test_unix_socket_address_to_string);
|
||||
g_test_add_func ("/socket/address/vsock/construct", test_vsock_socket_address_construct);
|
||||
g_test_add_func ("/socket/address/vsock/to-string", test_vsock_socket_address_to_string);
|
||||
g_test_add_func ("/socket/address/vsock/native", test_vsock_socket_address_native);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include "glib-private.h"
|
||||
@@ -33,6 +35,10 @@
|
||||
#include <gio/gnetworking.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
#include <linux/vm_sockets.h>
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include "giowin32-afunix.h"
|
||||
#include <io.h>
|
||||
@@ -1435,6 +1441,246 @@ test_unix_connection (void)
|
||||
g_object_unref (s);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vsock_error_is_skip (const GError *error)
|
||||
{
|
||||
return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE) ||
|
||||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
static void
|
||||
skip_vsock_error (const char *operation,
|
||||
const GError *error)
|
||||
{
|
||||
char *message = g_strdup_printf ("AF_VSOCK %s unavailable: %s",
|
||||
operation,
|
||||
error->message);
|
||||
|
||||
g_test_skip (message);
|
||||
g_free (message);
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_new (void)
|
||||
{
|
||||
GSocket *socket = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
socket = g_socket_new (G_SOCKET_FAMILY_VSOCK,
|
||||
G_SOCKET_TYPE_STREAM,
|
||||
G_SOCKET_PROTOCOL_DEFAULT,
|
||||
&error);
|
||||
|
||||
if (socket == NULL)
|
||||
{
|
||||
g_assert_true (vsock_error_is_skip (error));
|
||||
skip_vsock_error ("socket creation", error);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert_cmpint (g_socket_get_family (socket), ==, G_SOCKET_FAMILY_VSOCK);
|
||||
g_assert_cmpint (g_socket_get_socket_type (socket), ==, G_SOCKET_TYPE_STREAM);
|
||||
g_assert_cmpint (g_socket_get_protocol (socket), ==, G_SOCKET_PROTOCOL_DEFAULT);
|
||||
|
||||
g_object_unref (socket);
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_new_from_fd (void)
|
||||
{
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
GSocket *sock = NULL;
|
||||
GError *error = NULL;
|
||||
int fd;
|
||||
|
||||
fd = socket (AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
g_test_skip ("AF_VSOCK socket creation unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
sock = g_socket_new_from_fd (fd, &error);
|
||||
if (sock == NULL)
|
||||
{
|
||||
int saved_fd = fd;
|
||||
|
||||
g_assert_true (vsock_error_is_skip (error));
|
||||
skip_vsock_error ("fd introspection", error);
|
||||
g_clear_error (&error);
|
||||
g_close (saved_fd, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert_cmpint (g_socket_get_family (sock), ==, G_SOCKET_FAMILY_VSOCK);
|
||||
g_assert_cmpint (g_socket_get_socket_type (sock), ==, G_SOCKET_TYPE_STREAM);
|
||||
g_assert_cmpint (g_socket_get_protocol (sock), ==, G_SOCKET_PROTOCOL_DEFAULT);
|
||||
|
||||
g_object_unref (sock);
|
||||
#else
|
||||
g_test_skip ("AF_VSOCK not available at compile time");
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GSocketListener *listener;
|
||||
GSocketConnection *client_connection;
|
||||
GSocketConnection *server_connection;
|
||||
GError *client_error;
|
||||
GError *server_error;
|
||||
guint pending;
|
||||
} VsockAsyncData;
|
||||
|
||||
static void
|
||||
vsock_async_done (VsockAsyncData *data)
|
||||
{
|
||||
data->pending--;
|
||||
|
||||
if (data->pending == 0)
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
vsock_client_connected_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
VsockAsyncData *data = user_data;
|
||||
|
||||
data->client_connection = g_socket_client_connect_finish (G_SOCKET_CLIENT (object),
|
||||
result,
|
||||
&data->client_error);
|
||||
if (data->client_connection == NULL)
|
||||
g_socket_listener_close (data->listener);
|
||||
|
||||
vsock_async_done (data);
|
||||
}
|
||||
|
||||
static void
|
||||
vsock_listener_accepted_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
VsockAsyncData *data = user_data;
|
||||
|
||||
data->server_connection = g_socket_listener_accept_finish (G_SOCKET_LISTENER (object),
|
||||
result,
|
||||
NULL,
|
||||
&data->server_error);
|
||||
vsock_async_done (data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vsock_async_timeout_cb (gpointer user_data)
|
||||
{
|
||||
VsockAsyncData *data = user_data;
|
||||
|
||||
g_socket_listener_close (data->listener);
|
||||
g_main_loop_quit (data->loop);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_vsock_listener_client_loopback (void)
|
||||
{
|
||||
#ifdef HAVE_LINUX_VM_SOCKETS_H
|
||||
GSocketListener *listener = NULL;
|
||||
GSocketClient *client = NULL;
|
||||
GSocketAddress *address = NULL;
|
||||
GSocketAddress *effective_address = NULL;
|
||||
GSocketConnection *connection = NULL;
|
||||
GSocketConnection *accepted_connection = NULL;
|
||||
GError *error = NULL;
|
||||
VsockAsyncData data = { 0 };
|
||||
guint timeout_id;
|
||||
|
||||
listener = g_socket_listener_new ();
|
||||
address = g_vsock_socket_address_new (G_VSOCK_SOCKET_ADDRESS_CID_LOCAL,
|
||||
G_VSOCK_SOCKET_ADDRESS_PORT_ANY);
|
||||
|
||||
if (!g_socket_listener_add_address (listener,
|
||||
address,
|
||||
G_SOCKET_TYPE_STREAM,
|
||||
G_SOCKET_PROTOCOL_DEFAULT,
|
||||
NULL,
|
||||
&effective_address,
|
||||
&error))
|
||||
{
|
||||
g_assert_true (vsock_error_is_skip (error));
|
||||
skip_vsock_error ("loopback listen", error);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (address);
|
||||
g_object_unref (listener);
|
||||
return;
|
||||
}
|
||||
|
||||
client = g_socket_client_new ();
|
||||
connection = g_socket_client_connect (client,
|
||||
G_SOCKET_CONNECTABLE (effective_address),
|
||||
NULL,
|
||||
&error);
|
||||
if (connection == NULL)
|
||||
{
|
||||
g_assert_true (vsock_error_is_skip (error));
|
||||
skip_vsock_error ("loopback connect", error);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (effective_address);
|
||||
g_object_unref (address);
|
||||
g_object_unref (client);
|
||||
g_object_unref (listener);
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert_true (G_IS_SOCKET_CONNECTION (connection));
|
||||
accepted_connection = g_socket_listener_accept (listener, NULL, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (G_IS_SOCKET_CONNECTION (accepted_connection));
|
||||
g_clear_object (&accepted_connection);
|
||||
g_clear_object (&connection);
|
||||
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
data.listener = listener;
|
||||
data.pending = 2;
|
||||
|
||||
g_socket_listener_accept_async (listener,
|
||||
NULL,
|
||||
vsock_listener_accepted_cb,
|
||||
&data);
|
||||
g_socket_client_connect_async (client,
|
||||
G_SOCKET_CONNECTABLE (effective_address),
|
||||
NULL,
|
||||
vsock_client_connected_cb,
|
||||
&data);
|
||||
|
||||
timeout_id = g_timeout_add_seconds (5, vsock_async_timeout_cb, &data);
|
||||
g_main_loop_run (data.loop);
|
||||
g_clear_handle_id (&timeout_id, g_source_remove);
|
||||
|
||||
g_assert_no_error (data.client_error);
|
||||
g_assert_no_error (data.server_error);
|
||||
g_assert_true (G_IS_SOCKET_CONNECTION (data.client_connection));
|
||||
g_assert_true (G_IS_SOCKET_CONNECTION (data.server_connection));
|
||||
|
||||
g_clear_object (&data.client_connection);
|
||||
g_clear_object (&data.server_connection);
|
||||
g_main_loop_unref (data.loop);
|
||||
g_object_unref (effective_address);
|
||||
g_object_unref (address);
|
||||
g_object_unref (client);
|
||||
g_object_unref (listener);
|
||||
#else
|
||||
g_test_skip ("AF_VSOCK not available at compile time");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
static GSocketConnection *
|
||||
create_connection_for_fd (int fd)
|
||||
@@ -2813,6 +3059,10 @@ main (int argc,
|
||||
g_test_add_func ("/socket/address", test_sockaddr);
|
||||
g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd);
|
||||
g_test_add_func ("/socket/unix-connection", test_unix_connection);
|
||||
g_test_add_func ("/socket/vsock-new", test_vsock_new);
|
||||
g_test_add_func ("/socket/vsock-new-from-fd", test_vsock_new_from_fd);
|
||||
g_test_add_func ("/socket/vsock-listener-client-loopback",
|
||||
test_vsock_listener_client_loopback);
|
||||
#ifdef G_OS_UNIX
|
||||
g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data);
|
||||
#endif
|
||||
|
||||
@@ -201,6 +201,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@
|
||||
|
||||
@@ -2056,6 +2056,13 @@ foreach d : inet_defines
|
||||
val = cc.compute_int(d[0], prefix: inet_includes, guess: d[2])
|
||||
glibconfig_conf.set(d[1], val)
|
||||
endforeach
|
||||
if host_system == 'linux' and cc.has_header_symbol('sys/socket.h', 'AF_VSOCK')
|
||||
glibconfig_conf.set('g_af_vsock', cc.compute_int('AF_VSOCK',
|
||||
prefix: inet_includes,
|
||||
guess: 40))
|
||||
else
|
||||
glibconfig_conf.set('g_af_vsock', -1)
|
||||
endif
|
||||
|
||||
if host_system == 'windows'
|
||||
have_ipv6 = true
|
||||
|
||||
Reference in New Issue
Block a user