1
0
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:
Christian Hergert
2026-05-27 09:46:14 +02:00
parent 94d297fe6b
commit 9b7ebf6586
14 changed files with 866 additions and 4 deletions

View File

@@ -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)

View File

@@ -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>

View File

@@ -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;
/**

View File

@@ -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;

View File

@@ -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 ();
}

View File

@@ -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;

View File

@@ -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
View 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
View 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__ */

View File

@@ -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',

View File

@@ -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 ();
}

View File

@@ -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

View File

@@ -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@

View File

@@ -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