mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-15 14:57:15 +01:00
add GNetworkMonitor, for... monitoring the network
Add GNetworkMonitor and its associated extension point, provide a base implementation that always claims the network is available, and a netlink-based implementation built on top of that that actually tracks the network state. https://bugzilla.gnome.org/show_bug.cgi?id=620932
This commit is contained in:
parent
eb9755dc9c
commit
fe5ba0f291
@ -1109,6 +1109,10 @@ if test $glib_native_win32 = no; then
|
|||||||
fi
|
fi
|
||||||
AC_SUBST(NETWORK_LIBS)
|
AC_SUBST(NETWORK_LIBS)
|
||||||
|
|
||||||
|
AC_CHECK_HEADER([linux/netlink.h],
|
||||||
|
[AC_DEFINE(HAVE_NETLINK, 1, [We have AF_NETLINK sockets])])
|
||||||
|
AM_CONDITIONAL(HAVE_NETLINK, [test "$ac_cv_header_linux_netlink_h" = "yes"])
|
||||||
|
|
||||||
case $host in
|
case $host in
|
||||||
*-*-solaris* )
|
*-*-solaris* )
|
||||||
AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
|
AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
<xi:include href="xml/gasyncinitable.xml"/>
|
<xi:include href="xml/gasyncinitable.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="networking">
|
<chapter id="networking">
|
||||||
<title>Lowlevel network support</title>
|
<title>Low-level network support</title>
|
||||||
<xi:include href="xml/gsocket.xml"/>
|
<xi:include href="xml/gsocket.xml"/>
|
||||||
<xi:include href="xml/ginetaddress.xml"/>
|
<xi:include href="xml/ginetaddress.xml"/>
|
||||||
<xi:include href="xml/ginetaddressmask.xml"/>
|
<xi:include href="xml/ginetaddressmask.xml"/>
|
||||||
@ -120,7 +120,7 @@
|
|||||||
<xi:include href="xml/gproxyaddress.xml"/>
|
<xi:include href="xml/gproxyaddress.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="highlevel-socket">
|
<chapter id="highlevel-socket">
|
||||||
<title>Highlevel network functionallity</title>
|
<title>High-level network functionallity</title>
|
||||||
<xi:include href="xml/gsocketclient.xml"/>
|
<xi:include href="xml/gsocketclient.xml"/>
|
||||||
<xi:include href="xml/gsocketconnection.xml"/>
|
<xi:include href="xml/gsocketconnection.xml"/>
|
||||||
<xi:include href="xml/gunixconnection.xml"/>
|
<xi:include href="xml/gunixconnection.xml"/>
|
||||||
@ -129,6 +129,7 @@
|
|||||||
<xi:include href="xml/gsocketlistener.xml"/>
|
<xi:include href="xml/gsocketlistener.xml"/>
|
||||||
<xi:include href="xml/gsocketservice.xml"/>
|
<xi:include href="xml/gsocketservice.xml"/>
|
||||||
<xi:include href="xml/gthreadedsocketservice.xml"/>
|
<xi:include href="xml/gthreadedsocketservice.xml"/>
|
||||||
|
<xi:include href="xml/gnetworkmonitor.xml"/>
|
||||||
</chapter>
|
</chapter>
|
||||||
<chapter id="tls">
|
<chapter id="tls">
|
||||||
<title>TLS (SSL) support</title>
|
<title>TLS (SSL) support</title>
|
||||||
|
@ -3460,3 +3460,23 @@ G_DBUS_OBJECT_MANAGER_SERVER_GET_CLASS
|
|||||||
<SUBSECTION Private>
|
<SUBSECTION Private>
|
||||||
GDBusObjectManagerServerPrivate
|
GDBusObjectManagerServerPrivate
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
|
||||||
|
<SECTION>
|
||||||
|
<FILE>gnetworkmonitor</FILE>
|
||||||
|
<TITLE>GNetworkMonitor</TITLE>
|
||||||
|
GNetworkMonitor
|
||||||
|
GNetworkMonitorInterface
|
||||||
|
G_NETWORK_MONITOR_EXTENSION_POINT_NAME
|
||||||
|
g_network_monitor_get_default
|
||||||
|
g_network_monitor_get_network_available
|
||||||
|
g_network_monitor_can_reach
|
||||||
|
g_network_monitor_can_reach_async
|
||||||
|
g_network_monitor_can_reach_finish
|
||||||
|
<SUBSECTION Standard>
|
||||||
|
g_network_monitor_get_type
|
||||||
|
G_TYPE_NETWORK_MONITOR
|
||||||
|
G_NETWORK_MONITOR
|
||||||
|
G_IS_NETWORK_MONITOR
|
||||||
|
G_NETWORK_MONITOR_GET_INTERFACE
|
||||||
|
</SECTION>
|
||||||
|
|
||||||
|
@ -232,6 +232,13 @@ giounixinclude_HEADERS = \
|
|||||||
gunixoutputstream.h \
|
gunixoutputstream.h \
|
||||||
gunixsocketaddress.h \
|
gunixsocketaddress.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
if HAVE_NETLINK
|
||||||
|
unix_sources += \
|
||||||
|
gnetworkmonitornetlink.c \
|
||||||
|
gnetworkmonitornetlink.h \
|
||||||
|
$(NULL)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
win32_actual_sources = \
|
win32_actual_sources = \
|
||||||
@ -332,6 +339,9 @@ libgio_2_0_la_SOURCES = \
|
|||||||
gnativevolumemonitor.h \
|
gnativevolumemonitor.h \
|
||||||
gnetworkaddress.c \
|
gnetworkaddress.c \
|
||||||
gnetworkingprivate.h \
|
gnetworkingprivate.h \
|
||||||
|
gnetworkmonitor.c \
|
||||||
|
gnetworkmonitorbase.c \
|
||||||
|
gnetworkmonitorbase.h \
|
||||||
gnetworkservice.c \
|
gnetworkservice.c \
|
||||||
goutputstream.c \
|
goutputstream.c \
|
||||||
gpermission.c \
|
gpermission.c \
|
||||||
@ -498,6 +508,7 @@ gio_headers = \
|
|||||||
gmountoperation.h \
|
gmountoperation.h \
|
||||||
gnativevolumemonitor.h \
|
gnativevolumemonitor.h \
|
||||||
gnetworkaddress.h \
|
gnetworkaddress.h \
|
||||||
|
gnetworkmonitor.h \
|
||||||
gnetworkservice.h \
|
gnetworkservice.h \
|
||||||
goutputstream.h \
|
goutputstream.h \
|
||||||
gpermission.h \
|
gpermission.h \
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
#include <gio/gmountoperation.h>
|
#include <gio/gmountoperation.h>
|
||||||
#include <gio/gnativevolumemonitor.h>
|
#include <gio/gnativevolumemonitor.h>
|
||||||
#include <gio/gnetworkaddress.h>
|
#include <gio/gnetworkaddress.h>
|
||||||
|
#include <gio/gnetworkmonitor.h>
|
||||||
#include <gio/gnetworkservice.h>
|
#include <gio/gnetworkservice.h>
|
||||||
#include <gio/goutputstream.h>
|
#include <gio/goutputstream.h>
|
||||||
#include <gio/gpermission.h>
|
#include <gio/gpermission.h>
|
||||||
|
@ -1564,3 +1564,13 @@ g_dbus_object_manager_server_set_connection
|
|||||||
g_dbus_object_manager_server_get_type
|
g_dbus_object_manager_server_get_type
|
||||||
g_dbus_object_manager_server_new
|
g_dbus_object_manager_server_new
|
||||||
g_dbus_object_manager_server_unexport
|
g_dbus_object_manager_server_unexport
|
||||||
|
g_network_monitor_can_reach
|
||||||
|
g_network_monitor_can_reach_async
|
||||||
|
g_network_monitor_can_reach_finish
|
||||||
|
g_network_monitor_get_default
|
||||||
|
g_network_monitor_get_network_available
|
||||||
|
g_network_monitor_get_type
|
||||||
|
g_network_monitor_base_add_network
|
||||||
|
g_network_monitor_base_get_type
|
||||||
|
g_network_monitor_base_remove_network
|
||||||
|
g_network_monitor_base_set_networks
|
||||||
|
@ -771,6 +771,10 @@ extern GType _g_winhttp_vfs_get_type (void);
|
|||||||
|
|
||||||
extern GType _g_dummy_proxy_resolver_get_type (void);
|
extern GType _g_dummy_proxy_resolver_get_type (void);
|
||||||
extern GType _g_dummy_tls_backend_get_type (void);
|
extern GType _g_dummy_tls_backend_get_type (void);
|
||||||
|
extern GType g_network_monitor_base_get_type (void);
|
||||||
|
#ifdef HAVE_NETLINK
|
||||||
|
extern GType _g_network_monitor_netlink_get_type (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef G_PLATFORM_WIN32
|
#ifdef G_PLATFORM_WIN32
|
||||||
|
|
||||||
@ -850,6 +854,9 @@ _g_io_modules_ensure_extension_points_registered (void)
|
|||||||
|
|
||||||
ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
|
ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
|
||||||
g_io_extension_point_set_required_type (ep, G_TYPE_TLS_BACKEND);
|
g_io_extension_point_set_required_type (ep, G_TYPE_TLS_BACKEND);
|
||||||
|
|
||||||
|
ep = g_io_extension_point_register (G_NETWORK_MONITOR_EXTENSION_POINT_NAME);
|
||||||
|
g_io_extension_point_set_required_type (ep, G_TYPE_NETWORK_MONITOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
G_UNLOCK (registered_extensions);
|
G_UNLOCK (registered_extensions);
|
||||||
@ -921,6 +928,10 @@ _g_io_modules_ensure_loaded (void)
|
|||||||
_g_socks4_proxy_get_type ();
|
_g_socks4_proxy_get_type ();
|
||||||
_g_socks5_proxy_get_type ();
|
_g_socks5_proxy_get_type ();
|
||||||
_g_dummy_tls_backend_get_type ();
|
_g_dummy_tls_backend_get_type ();
|
||||||
|
g_network_monitor_base_get_type ();
|
||||||
|
#ifdef HAVE_NETLINK
|
||||||
|
_g_network_monitor_netlink_get_type ();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
G_UNLOCK (loaded_dirs);
|
G_UNLOCK (loaded_dirs);
|
||||||
|
@ -123,6 +123,7 @@ typedef struct _GMemoryOutputStream GMemoryOutputStream;
|
|||||||
typedef struct _GMount GMount; /* Dummy typedef */
|
typedef struct _GMount GMount; /* Dummy typedef */
|
||||||
typedef struct _GMountOperation GMountOperation;
|
typedef struct _GMountOperation GMountOperation;
|
||||||
typedef struct _GNetworkAddress GNetworkAddress;
|
typedef struct _GNetworkAddress GNetworkAddress;
|
||||||
|
typedef struct _GNetworkMonitor GNetworkMonitor;
|
||||||
typedef struct _GNetworkService GNetworkService;
|
typedef struct _GNetworkService GNetworkService;
|
||||||
typedef struct _GOutputStream GOutputStream;
|
typedef struct _GOutputStream GOutputStream;
|
||||||
typedef struct _GIOStream GIOStream;
|
typedef struct _GIOStream GIOStream;
|
||||||
|
261
gio/gnetworkmonitor.c
Normal file
261
gio/gnetworkmonitor.c
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "glib.h"
|
||||||
|
#include "glibintl.h"
|
||||||
|
|
||||||
|
#include "gnetworkmonitor.h"
|
||||||
|
#include "ginetaddress.h"
|
||||||
|
#include "ginetsocketaddress.h"
|
||||||
|
#include "ginitable.h"
|
||||||
|
#include "gioenumtypes.h"
|
||||||
|
#include "giomodule-priv.h"
|
||||||
|
#include "gsimpleasyncresult.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:gnetworkmonitor
|
||||||
|
* @title: GNetworkMonitor
|
||||||
|
* @short_description: Network status monitor
|
||||||
|
* @include: gio/gio.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GNetworkMonitor:
|
||||||
|
*
|
||||||
|
* #GNetworkMonitor monitors the status of network connections and
|
||||||
|
* indicates when a possibly-user-visible change has occurred.
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
|
||||||
|
G_DEFINE_INTERFACE_WITH_CODE (GNetworkMonitor, g_network_monitor, G_TYPE_OBJECT,
|
||||||
|
g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE);)
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NETWORK_CHANGED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_get_default:
|
||||||
|
*
|
||||||
|
* Gets the default #GNetworkMonitor for the system.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a #GNetworkMonitor
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
GNetworkMonitor *
|
||||||
|
g_network_monitor_get_default (void)
|
||||||
|
{
|
||||||
|
return _g_io_module_get_default (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
|
||||||
|
"GIO_USE_NETWORK_MONITOR",
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_get_network_available:
|
||||||
|
* @monitor: the #GNetworkMonitor
|
||||||
|
*
|
||||||
|
* Checks if the network is available. "Available" here means that the
|
||||||
|
* system has a default route available for at least one of IPv4 or
|
||||||
|
* IPv6. It does not necessarily imply that the public Internet is
|
||||||
|
* reachable. See #GNetworkMonitor:network-available for more details.
|
||||||
|
*
|
||||||
|
* Return value: whether the network is available
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
g_network_monitor_get_network_available (GNetworkMonitor *monitor)
|
||||||
|
{
|
||||||
|
gboolean available = FALSE;
|
||||||
|
|
||||||
|
g_object_get (G_OBJECT (monitor), "network-available", &available, NULL);
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_can_reach:
|
||||||
|
* @monitor: a #GNetworkMonitor
|
||||||
|
* @connectable: a #GSocketConnectable
|
||||||
|
* @cancellable: a #GCancellable, or %NULL
|
||||||
|
* @error: return location for a #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Attempts to determine whether or not the host pointed to by
|
||||||
|
* @connectable can be reached, without actually trying to connect to
|
||||||
|
* it.
|
||||||
|
*
|
||||||
|
* This may return %TRUE even when #GNetworkMonitor:network-available
|
||||||
|
* is %FALSE, if, for example, @monitor can determine that
|
||||||
|
* @connectable refers to a host on a local network.
|
||||||
|
*
|
||||||
|
* If @monitor believes that an attempt to connect to @connectable
|
||||||
|
* will succeed, it will return %TRUE. Otherwise, it will return
|
||||||
|
* %FALSE and set @error to an appropriate error (such as
|
||||||
|
* %G_IO_ERROR_HOST_UNREACHABLE).
|
||||||
|
*
|
||||||
|
* Note that although this does not attempt to connect to
|
||||||
|
* @connectable, it may still block for a brief period of time (eg,
|
||||||
|
* trying to do multicast DNS on the local network), so if you do not
|
||||||
|
* want to block, you should use g_network_monitor_can_reach_async().
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if @connectable is reachable, %FALSE if not.
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
g_network_monitor_can_reach (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GNetworkMonitorInterface *iface;
|
||||||
|
|
||||||
|
iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
|
||||||
|
return iface->can_reach (monitor, connectable, cancellable, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_real_can_reach_async (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GSimpleAsyncResult *simple;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
simple = g_simple_async_result_new (G_OBJECT (monitor),
|
||||||
|
callback, user_data,
|
||||||
|
g_network_monitor_real_can_reach_async);
|
||||||
|
if (g_network_monitor_can_reach (monitor, connectable, cancellable, &error))
|
||||||
|
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
|
||||||
|
else
|
||||||
|
g_simple_async_result_take_error (simple, error);
|
||||||
|
g_simple_async_result_complete_in_idle (simple);
|
||||||
|
g_object_unref (simple);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
g_network_monitor_can_reach_async (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GNetworkMonitorInterface *iface;
|
||||||
|
|
||||||
|
iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
|
||||||
|
iface->can_reach_async (monitor, connectable, cancellable, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_network_monitor_real_can_reach_finish (GNetworkMonitor *monitor,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GSimpleAsyncResult *simple;
|
||||||
|
|
||||||
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (monitor), g_network_monitor_real_can_reach_async), FALSE);
|
||||||
|
|
||||||
|
simple = G_SIMPLE_ASYNC_RESULT (result);
|
||||||
|
if (g_simple_async_result_propagate_error (simple, error))
|
||||||
|
return FALSE;
|
||||||
|
else
|
||||||
|
return g_simple_async_result_get_op_res_gboolean (simple);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
g_network_monitor_can_reach_finish (GNetworkMonitor *monitor,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GNetworkMonitorInterface *iface;
|
||||||
|
|
||||||
|
iface = G_NETWORK_MONITOR_GET_INTERFACE (monitor);
|
||||||
|
return iface->can_reach_finish (monitor, result, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_default_init (GNetworkMonitorInterface *iface)
|
||||||
|
{
|
||||||
|
iface->can_reach_async = g_network_monitor_real_can_reach_async;
|
||||||
|
iface->can_reach_finish = g_network_monitor_real_can_reach_finish;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GNetworkMonitor::network-changed:
|
||||||
|
* @monitor: a #GNetworkMonitor
|
||||||
|
* @available: the current value of #GNetworkMonitor:network-available
|
||||||
|
*
|
||||||
|
* Emitted when the network configuration changes. If @available is
|
||||||
|
* %TRUE, then some hosts may be reachable that were not reachable
|
||||||
|
* before, while others that were reachable before may no longer be
|
||||||
|
* reachable. If @available is %FALSE, then no remote hosts are
|
||||||
|
* reachable.
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
signals[NETWORK_CHANGED] =
|
||||||
|
g_signal_new (I_("network-changed"),
|
||||||
|
G_TYPE_NETWORK_MONITOR,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (GNetworkMonitorInterface, network_changed),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__BOOLEAN,
|
||||||
|
G_TYPE_NONE, 1,
|
||||||
|
G_TYPE_BOOLEAN);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GNetworkMonitor:network-available:
|
||||||
|
*
|
||||||
|
* Whether the network is considered available. That is, whether the
|
||||||
|
* system has a default route for at least one of IPv4 or IPv6.
|
||||||
|
*
|
||||||
|
* Real-world networks are of course much more complicated than
|
||||||
|
* this; the machine may be connected to a wifi hotspot that
|
||||||
|
* requires payment before allowing traffic through, or may be
|
||||||
|
* connected to a functioning router that has lost its own upstream
|
||||||
|
* connectivity. Some hosts might only be accessible when a VPN is
|
||||||
|
* active. Other hosts might only be accessible when the VPN is
|
||||||
|
* <emphasis>not</emphasis> active. Thus, it is best to use
|
||||||
|
* g_network_monitor_can_reach() or
|
||||||
|
* g_network_monitor_can_reach_async() to test for reachability on a
|
||||||
|
* host-by-host basis. (On the other hand, when the property is
|
||||||
|
* %FALSE, the application can reasonably expect that no remote
|
||||||
|
* hosts at all are reachable, and should indicate this to the user
|
||||||
|
* in its UI.)
|
||||||
|
*
|
||||||
|
* See also #GNetworkMonitor::network-changed.
|
||||||
|
*
|
||||||
|
* Since: 2.32
|
||||||
|
*/
|
||||||
|
g_object_interface_install_property (iface,
|
||||||
|
g_param_spec_boolean ("network-available",
|
||||||
|
P_("Network available"),
|
||||||
|
P_("Whether the network is available"),
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
}
|
89
gio/gnetworkmonitor.h
Normal file
89
gio/gnetworkmonitor.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||||
|
#error "Only <gio/gio.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __G_NETWORK_MONITOR_H__
|
||||||
|
#define __G_NETWORK_MONITOR_H__
|
||||||
|
|
||||||
|
#include <gio/giotypes.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* G_NETWORK_MONITOR_EXTENSION_POINT_NAME:
|
||||||
|
*
|
||||||
|
* Extension point for network status monitoring functionality.
|
||||||
|
* See <link linkend="extending-gio">Extending GIO</link>.
|
||||||
|
*
|
||||||
|
* Since: 2.30
|
||||||
|
*/
|
||||||
|
#define G_NETWORK_MONITOR_EXTENSION_POINT_NAME "gio-network-monitor"
|
||||||
|
|
||||||
|
#define G_TYPE_NETWORK_MONITOR (g_network_monitor_get_type ())
|
||||||
|
#define G_NETWORK_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR, GNetworkMonitor))
|
||||||
|
#define G_IS_NETWORK_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR))
|
||||||
|
#define G_NETWORK_MONITOR_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_NETWORK_MONITOR, GNetworkMonitorInterface))
|
||||||
|
|
||||||
|
typedef struct _GNetworkMonitorInterface GNetworkMonitorInterface;
|
||||||
|
|
||||||
|
struct _GNetworkMonitorInterface {
|
||||||
|
GTypeInterface g_iface;
|
||||||
|
|
||||||
|
void (*network_changed) (GNetworkMonitor *monitor,
|
||||||
|
gboolean available);
|
||||||
|
|
||||||
|
gboolean (*can_reach) (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
void (*can_reach_async) (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (*can_reach_finish) (GNetworkMonitor *monitor,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType g_network_monitor_get_type (void) G_GNUC_CONST;
|
||||||
|
GNetworkMonitor *g_network_monitor_get_default (void);
|
||||||
|
|
||||||
|
gboolean g_network_monitor_get_network_available (GNetworkMonitor *monitor);
|
||||||
|
|
||||||
|
gboolean g_network_monitor_can_reach (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
void g_network_monitor_can_reach_async (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean g_network_monitor_can_reach_finish (GNetworkMonitor *monitor,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __G_NETWORK_MONITOR_H__ */
|
412
gio/gnetworkmonitorbase.c
Normal file
412
gio/gnetworkmonitorbase.c
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gnetworkmonitorbase.h"
|
||||||
|
#include "ginetaddress.h"
|
||||||
|
#include "ginetaddressmask.h"
|
||||||
|
#include "ginetsocketaddress.h"
|
||||||
|
#include "ginitable.h"
|
||||||
|
#include "gioerror.h"
|
||||||
|
#include "giomodule-priv.h"
|
||||||
|
#include "gnetworkmonitor.h"
|
||||||
|
#include "gsocketaddressenumerator.h"
|
||||||
|
#include "gsocketconnectable.h"
|
||||||
|
#include "glibintl.h"
|
||||||
|
|
||||||
|
static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
|
||||||
|
static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
||||||
|
g_network_monitor_base_initable_iface_init)
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
|
||||||
|
g_network_monitor_base_iface_init)
|
||||||
|
_g_io_modules_ensure_extension_points_registered ();
|
||||||
|
g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
|
||||||
|
g_define_type_id,
|
||||||
|
"base",
|
||||||
|
0))
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
|
||||||
|
PROP_NETWORK_AVAILABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GNetworkMonitorBasePrivate
|
||||||
|
{
|
||||||
|
GPtrArray *networks;
|
||||||
|
gboolean have_ipv4_default_route;
|
||||||
|
gboolean have_ipv6_default_route;
|
||||||
|
gboolean is_available;
|
||||||
|
|
||||||
|
GMainContext *context;
|
||||||
|
GSource *network_changed_source;
|
||||||
|
gboolean initializing;
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint network_changed_signal = 0;
|
||||||
|
|
||||||
|
static void queue_network_changed (GNetworkMonitorBase *monitor);
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_init (GNetworkMonitorBase *monitor)
|
||||||
|
{
|
||||||
|
monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
|
||||||
|
G_TYPE_NETWORK_MONITOR_BASE,
|
||||||
|
GNetworkMonitorBasePrivate);
|
||||||
|
|
||||||
|
monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
|
||||||
|
monitor->priv->context = g_main_context_get_thread_default ();
|
||||||
|
if (monitor->priv->context)
|
||||||
|
g_main_context_ref (monitor->priv->context);
|
||||||
|
|
||||||
|
monitor->priv->initializing = TRUE;
|
||||||
|
queue_network_changed (monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_constructed (GObject *object)
|
||||||
|
{
|
||||||
|
GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
|
||||||
|
|
||||||
|
if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
|
||||||
|
{
|
||||||
|
GInetAddressMask *mask;
|
||||||
|
|
||||||
|
/* We're the dumb base class, not a smarter subclass. So just
|
||||||
|
* assume that the network is available.
|
||||||
|
*/
|
||||||
|
mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
|
||||||
|
g_network_monitor_base_add_network (monitor, mask);
|
||||||
|
g_object_unref (mask);
|
||||||
|
|
||||||
|
mask = g_inet_address_mask_new_from_string ("::/0", NULL);
|
||||||
|
g_network_monitor_base_add_network (monitor, mask);
|
||||||
|
g_object_unref (mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_NETWORK_AVAILABLE:
|
||||||
|
g_value_set_boolean (value, monitor->priv->is_available);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
|
||||||
|
|
||||||
|
g_ptr_array_free (monitor->priv->networks, TRUE);
|
||||||
|
if (monitor->priv->network_changed_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (monitor->priv->network_changed_source);
|
||||||
|
g_source_unref (monitor->priv->network_changed_source);
|
||||||
|
}
|
||||||
|
if (monitor->priv->context)
|
||||||
|
g_main_context_unref (monitor->priv->context);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
|
||||||
|
|
||||||
|
g_type_class_add_private (monitor_class, sizeof (GNetworkMonitorBasePrivate));
|
||||||
|
|
||||||
|
gobject_class->constructed = g_network_monitor_base_constructed;
|
||||||
|
gobject_class->get_property = g_network_monitor_base_get_property;
|
||||||
|
gobject_class->finalize = g_network_monitor_base_finalize;
|
||||||
|
|
||||||
|
g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
|
||||||
|
GSocketConnectable *connectable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GNetworkMonitorBasePrivate *priv = G_NETWORK_MONITOR_BASE (monitor)->priv;
|
||||||
|
GSocketAddressEnumerator *enumerator;
|
||||||
|
GSocketAddress *addr;
|
||||||
|
|
||||||
|
if (priv->have_ipv4_default_route &&
|
||||||
|
priv->have_ipv6_default_route)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (priv->networks->len == 0)
|
||||||
|
{
|
||||||
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
|
||||||
|
_("Network unreachable"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enumerator = g_socket_connectable_proxy_enumerate (connectable);
|
||||||
|
addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
|
||||||
|
if (!addr)
|
||||||
|
{
|
||||||
|
/* Either the user cancelled, or DNS resolution failed */
|
||||||
|
g_object_unref (enumerator);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (addr)
|
||||||
|
{
|
||||||
|
if (G_IS_INET_SOCKET_ADDRESS (addr))
|
||||||
|
{
|
||||||
|
GInetAddress *iaddr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
|
||||||
|
for (i = 0; i < priv->networks->len; i++)
|
||||||
|
{
|
||||||
|
if (g_inet_address_mask_matches (priv->networks->pdata[i], iaddr))
|
||||||
|
{
|
||||||
|
g_object_unref (addr);
|
||||||
|
g_object_unref (enumerator);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (addr);
|
||||||
|
addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
|
||||||
|
}
|
||||||
|
g_object_unref (enumerator);
|
||||||
|
|
||||||
|
if (error && !*error)
|
||||||
|
{
|
||||||
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
|
||||||
|
_("Host unreachable"));
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
|
||||||
|
{
|
||||||
|
monitor_iface->can_reach = g_network_monitor_base_can_reach;
|
||||||
|
|
||||||
|
network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_network_monitor_base_initable_init (GInitable *initable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_base_initable_iface_init (GInitableIface *iface)
|
||||||
|
{
|
||||||
|
iface->init = g_network_monitor_base_initable_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_network_changed (gpointer user_data)
|
||||||
|
{
|
||||||
|
GNetworkMonitorBase *monitor = user_data;
|
||||||
|
gboolean is_available;
|
||||||
|
|
||||||
|
g_object_ref (monitor);
|
||||||
|
|
||||||
|
if (monitor->priv->initializing)
|
||||||
|
monitor->priv->initializing = FALSE;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_available = (monitor->priv->have_ipv4_default_route ||
|
||||||
|
monitor->priv->have_ipv6_default_route);
|
||||||
|
if (monitor->priv->is_available != is_available)
|
||||||
|
{
|
||||||
|
monitor->priv->is_available = is_available;
|
||||||
|
g_object_notify (G_OBJECT (monitor), "network-available");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_emit (monitor, network_changed_signal, 0, is_available);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_source_unref (monitor->priv->network_changed_source);
|
||||||
|
monitor->priv->network_changed_source = NULL;
|
||||||
|
|
||||||
|
g_object_unref (monitor);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
queue_network_changed (GNetworkMonitorBase *monitor)
|
||||||
|
{
|
||||||
|
if (!monitor->priv->network_changed_source)
|
||||||
|
{
|
||||||
|
GSource *source;
|
||||||
|
|
||||||
|
source = g_idle_source_new ();
|
||||||
|
/* Use G_PRIORITY_HIGH_IDLE priority so that multiple
|
||||||
|
* network-change-related notifications coming in at
|
||||||
|
* G_PRIORITY_DEFAULT will get coalesced into one signal
|
||||||
|
* emission.
|
||||||
|
*/
|
||||||
|
g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
|
||||||
|
g_source_set_callback (source, emit_network_changed, monitor, NULL);
|
||||||
|
g_source_attach (source, monitor->priv->context);
|
||||||
|
monitor->priv->network_changed_source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Normally we wait to update is_available until we emit the signal,
|
||||||
|
* to keep things consistent. But when we're first creating the
|
||||||
|
* object, we want it to be correct right away.
|
||||||
|
*/
|
||||||
|
if (monitor->priv->initializing)
|
||||||
|
{
|
||||||
|
monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
|
||||||
|
monitor->priv->have_ipv6_default_route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_base_add_network:
|
||||||
|
* @monitor: the #GNetworkMonitorBase
|
||||||
|
* @network: a #GInetAddressMask
|
||||||
|
*
|
||||||
|
* Adds @network to @monitor's list of available networks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask *network)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < monitor->priv->networks->len; i++)
|
||||||
|
{
|
||||||
|
if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
|
||||||
|
if (g_inet_address_mask_get_length (network) == 0)
|
||||||
|
{
|
||||||
|
switch (g_inet_address_mask_get_family (network))
|
||||||
|
{
|
||||||
|
case G_SOCKET_FAMILY_IPV4:
|
||||||
|
monitor->priv->have_ipv4_default_route = TRUE;
|
||||||
|
break;
|
||||||
|
case G_SOCKET_FAMILY_IPV6:
|
||||||
|
monitor->priv->have_ipv6_default_route = TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't emit network-changed when multicast-link-local routing
|
||||||
|
* changes. This rather arbitrary decision is mostly because it
|
||||||
|
* seems to change quite often...
|
||||||
|
*/
|
||||||
|
if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
queue_network_changed (monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_base_remove_network:
|
||||||
|
* @monitor: the #GNetworkMonitorBase
|
||||||
|
* @network: a #GInetAddressMask
|
||||||
|
*
|
||||||
|
* Removes @network from @monitor's list of available networks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask *network)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < monitor->priv->networks->len; i++)
|
||||||
|
{
|
||||||
|
if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
|
||||||
|
{
|
||||||
|
g_ptr_array_remove_index_fast (monitor->priv->networks, i);
|
||||||
|
|
||||||
|
if (g_inet_address_mask_get_length (network) == 0)
|
||||||
|
{
|
||||||
|
switch (g_inet_address_mask_get_family (network))
|
||||||
|
{
|
||||||
|
case G_SOCKET_FAMILY_IPV4:
|
||||||
|
monitor->priv->have_ipv4_default_route = FALSE;
|
||||||
|
break;
|
||||||
|
case G_SOCKET_FAMILY_IPV6:
|
||||||
|
monitor->priv->have_ipv6_default_route = FALSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_network_changed (monitor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_network_monitor_base_set_networks:
|
||||||
|
* @monitor: the #GNetworkMonitorBase
|
||||||
|
* @networks: (array length=length): an array of #GInetAddressMask
|
||||||
|
* @length: length of @networks
|
||||||
|
*
|
||||||
|
* Drops @monitor's current list of available networks and replaces
|
||||||
|
* it with @networks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask **networks,
|
||||||
|
gint length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
g_ptr_array_set_size (monitor->priv->networks, 0);
|
||||||
|
monitor->priv->have_ipv4_default_route = FALSE;
|
||||||
|
monitor->priv->have_ipv6_default_route = FALSE;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
g_network_monitor_base_add_network (monitor, networks[i]);
|
||||||
|
}
|
66
gio/gnetworkmonitorbase.h
Normal file
66
gio/gnetworkmonitorbase.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __G_NETWORK_MONITOR_BASE_H__
|
||||||
|
#define __G_NETWORK_MONITOR_BASE_H__
|
||||||
|
|
||||||
|
#include <gio/giotypes.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define G_TYPE_NETWORK_MONITOR_BASE (g_network_monitor_base_get_type ())
|
||||||
|
#define G_NETWORK_MONITOR_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBase))
|
||||||
|
#define G_NETWORK_MONITOR_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBaseClass))
|
||||||
|
#define G_IS_NETWORK_MONITOR_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR_BASE))
|
||||||
|
#define G_IS_NETWORK_MONITOR_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_MONITOR_BASE))
|
||||||
|
#define G_NETWORK_MONITOR_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_MONITOR_BASE, GNetworkMonitorBaseClass))
|
||||||
|
|
||||||
|
typedef struct _GNetworkMonitorBase GNetworkMonitorBase;
|
||||||
|
typedef struct _GNetworkMonitorBaseClass GNetworkMonitorBaseClass;
|
||||||
|
typedef struct _GNetworkMonitorBasePrivate GNetworkMonitorBasePrivate;
|
||||||
|
|
||||||
|
struct _GNetworkMonitorBase {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
GNetworkMonitorBasePrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GNetworkMonitorBaseClass {
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
/* Padding for future expansion */
|
||||||
|
gpointer padding[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
GType g_network_monitor_base_get_type (void);
|
||||||
|
|
||||||
|
/*< protected >*/
|
||||||
|
void g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask *network);
|
||||||
|
void g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask *network);
|
||||||
|
void g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
|
||||||
|
GInetAddressMask **networks,
|
||||||
|
gint length);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __G_NETWORK_MONITOR_BASE_H__ */
|
465
gio/gnetworkmonitornetlink.c
Normal file
465
gio/gnetworkmonitornetlink.c
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
#include "gnetworkmonitornetlink.h"
|
||||||
|
#include "gcredentials.h"
|
||||||
|
#include "ginetaddressmask.h"
|
||||||
|
#include "ginitable.h"
|
||||||
|
#include "giomodule-priv.h"
|
||||||
|
#include "glibintl.h"
|
||||||
|
#include "gnetworkingprivate.h"
|
||||||
|
#include "gnetworkmonitor.h"
|
||||||
|
#include "gsocket.h"
|
||||||
|
#include "gunixcredentialsmessage.h"
|
||||||
|
|
||||||
|
static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
|
||||||
|
static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
|
||||||
|
|
||||||
|
#define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
|
||||||
|
g_network_monitor_netlink_iface_init)
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
|
||||||
|
g_network_monitor_netlink_initable_iface_init)
|
||||||
|
_g_io_modules_ensure_extension_points_registered ();
|
||||||
|
g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
|
||||||
|
g_define_type_id,
|
||||||
|
"netlink",
|
||||||
|
20))
|
||||||
|
|
||||||
|
struct _GNetworkMonitorNetlinkPrivate
|
||||||
|
{
|
||||||
|
GSocket *sock;
|
||||||
|
GSource *source, *dump_source;
|
||||||
|
|
||||||
|
GPtrArray *dump_networks;
|
||||||
|
};
|
||||||
|
|
||||||
|
static gboolean read_netlink_messages (GSocket *socket,
|
||||||
|
GIOCondition condition,
|
||||||
|
gpointer user_data);
|
||||||
|
static gboolean request_dump (GNetworkMonitorNetlink *nl,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
|
||||||
|
{
|
||||||
|
nl->priv = G_TYPE_INSTANCE_GET_PRIVATE (nl,
|
||||||
|
G_TYPE_NETWORK_MONITOR_NETLINK,
|
||||||
|
GNetworkMonitorNetlinkPrivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
g_network_monitor_netlink_initable_init (GInitable *initable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
|
||||||
|
gint sockfd, val;
|
||||||
|
struct sockaddr_nl snl;
|
||||||
|
|
||||||
|
/* We create the socket the old-school way because sockaddr_netlink
|
||||||
|
* can't be represented as a GSocketAddress
|
||||||
|
*/
|
||||||
|
sockfd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||||
|
if (sockfd == -1)
|
||||||
|
{
|
||||||
|
int errsv = errno;
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
|
||||||
|
_("Could not create netlink socket: %s"),
|
||||||
|
g_strerror (errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
snl.nl_family = AF_NETLINK;
|
||||||
|
snl.nl_pid = snl.nl_pad = 0;
|
||||||
|
snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
|
||||||
|
if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
|
||||||
|
{
|
||||||
|
int errsv = errno;
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
|
||||||
|
_("Could not bind netlink socket: %s"),
|
||||||
|
g_strerror (errno));
|
||||||
|
close (sockfd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = 1;
|
||||||
|
if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &val, sizeof (val)) != 0)
|
||||||
|
{
|
||||||
|
int errsv = errno;
|
||||||
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
|
||||||
|
_("Could not set options on netlink socket: %s"),
|
||||||
|
g_strerror (errno));
|
||||||
|
close (sockfd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl->priv->sock = g_socket_new_from_fd (sockfd, error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_prefix_error (error, "%s", _("Could not wrap netlink socket: "));
|
||||||
|
close (sockfd);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request the current state */
|
||||||
|
if (!request_dump (nl, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* And read responses; since we haven't yet marked the socket
|
||||||
|
* non-blocking, each call will block until a message is received.
|
||||||
|
*/
|
||||||
|
while (nl->priv->dump_networks)
|
||||||
|
{
|
||||||
|
if (!read_netlink_messages (NULL, G_IO_IN, nl))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_socket_set_blocking (nl->priv->sock, FALSE);
|
||||||
|
nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
|
||||||
|
g_source_set_callback (nl->priv->source,
|
||||||
|
(GSourceFunc) read_netlink_messages, nl, NULL);
|
||||||
|
g_source_attach (nl->priv->source,
|
||||||
|
g_main_context_get_thread_default ());
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
request_dump (GNetworkMonitorNetlink *nl,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *n;
|
||||||
|
struct rtgenmsg *gen;
|
||||||
|
gchar buf[NLMSG_SPACE (sizeof (*gen))];
|
||||||
|
|
||||||
|
memset (buf, 0, sizeof (buf));
|
||||||
|
n = (struct nlmsghdr*) buf;
|
||||||
|
n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
|
||||||
|
n->nlmsg_type = RTM_GETROUTE;
|
||||||
|
n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||||
|
n->nlmsg_pid = 0;
|
||||||
|
gen = NLMSG_DATA (n);
|
||||||
|
gen->rtgen_family = AF_UNSPEC;
|
||||||
|
|
||||||
|
if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
|
||||||
|
NULL, error) < 0)
|
||||||
|
{
|
||||||
|
g_prefix_error (error, "%s", _("Could not send netlink request: "));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timeout_request_dump (gpointer user_data)
|
||||||
|
{
|
||||||
|
GNetworkMonitorNetlink *nl = user_data;
|
||||||
|
|
||||||
|
g_source_destroy (nl->priv->dump_source);
|
||||||
|
g_source_unref (nl->priv->dump_source);
|
||||||
|
nl->priv->dump_source = NULL;
|
||||||
|
|
||||||
|
request_dump (nl, NULL);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
queue_request_dump (GNetworkMonitorNetlink *nl)
|
||||||
|
{
|
||||||
|
if (nl->priv->dump_networks)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nl->priv->dump_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (nl->priv->dump_source);
|
||||||
|
g_source_unref (nl->priv->dump_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
nl->priv->dump_source = g_timeout_source_new (1000);
|
||||||
|
g_source_set_callback (nl->priv->dump_source,
|
||||||
|
(GSourceFunc) timeout_request_dump, nl, NULL);
|
||||||
|
g_source_attach (nl->priv->dump_source,
|
||||||
|
g_main_context_get_thread_default ());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_network (GNetworkMonitorNetlink *nl,
|
||||||
|
GSocketFamily family,
|
||||||
|
gint dest_len,
|
||||||
|
guint8 *dest,
|
||||||
|
guint8 *gateway)
|
||||||
|
{
|
||||||
|
GInetAddress *dest_addr;
|
||||||
|
GInetAddressMask *network;
|
||||||
|
|
||||||
|
if (dest)
|
||||||
|
dest_addr = g_inet_address_new_from_bytes (dest, family);
|
||||||
|
else
|
||||||
|
dest_addr = g_inet_address_new_any (family);
|
||||||
|
network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
|
||||||
|
g_object_unref (dest_addr);
|
||||||
|
g_return_if_fail (network != NULL);
|
||||||
|
|
||||||
|
if (nl->priv->dump_networks)
|
||||||
|
g_ptr_array_add (nl->priv->dump_networks, network);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
|
||||||
|
g_object_unref (network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_network (GNetworkMonitorNetlink *nl,
|
||||||
|
GSocketFamily family,
|
||||||
|
gint dest_len,
|
||||||
|
guint8 *dest,
|
||||||
|
guint8 *gateway)
|
||||||
|
{
|
||||||
|
GInetAddress *dest_addr;
|
||||||
|
GInetAddressMask *network;
|
||||||
|
|
||||||
|
if (dest)
|
||||||
|
dest_addr = g_inet_address_new_from_bytes (dest, family);
|
||||||
|
else
|
||||||
|
dest_addr = g_inet_address_new_any (family);
|
||||||
|
network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
|
||||||
|
g_object_unref (dest_addr);
|
||||||
|
g_return_if_fail (network != NULL);
|
||||||
|
|
||||||
|
if (nl->priv->dump_networks)
|
||||||
|
{
|
||||||
|
GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < nl->priv->dump_networks->len; i++)
|
||||||
|
{
|
||||||
|
if (g_inet_address_mask_equal (network, dump_networks[i]))
|
||||||
|
g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
|
||||||
|
}
|
||||||
|
g_object_unref (network);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
|
||||||
|
g_object_unref (network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finish_dump (GNetworkMonitorNetlink *nl)
|
||||||
|
{
|
||||||
|
g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
|
||||||
|
(GInetAddressMask **)nl->priv->dump_networks->pdata,
|
||||||
|
nl->priv->dump_networks->len);
|
||||||
|
g_ptr_array_free (nl->priv->dump_networks, FALSE);
|
||||||
|
nl->priv->dump_networks = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
read_netlink_messages (GSocket *socket,
|
||||||
|
GIOCondition condition,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GNetworkMonitorNetlink *nl = user_data;
|
||||||
|
GInputVector iv;
|
||||||
|
gsize len;
|
||||||
|
GSocketControlMessage **cmsgs = NULL;
|
||||||
|
gint num_cmsgs = 0, i, flags;
|
||||||
|
GError *error = NULL;
|
||||||
|
GCredentials *creds;
|
||||||
|
uid_t sender;
|
||||||
|
struct nlmsghdr *msg;
|
||||||
|
struct rtmsg *rtmsg;
|
||||||
|
struct rtattr *attr;
|
||||||
|
gsize attrlen;
|
||||||
|
guint8 *dest, *gateway;
|
||||||
|
gboolean retval = TRUE;
|
||||||
|
|
||||||
|
iv.buffer = NULL;
|
||||||
|
iv.size = 0;
|
||||||
|
|
||||||
|
flags = MSG_PEEK | MSG_TRUNC;
|
||||||
|
len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
|
||||||
|
NULL, NULL, &flags, NULL, &error);
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
g_warning ("Error on netlink socket: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
if (nl->priv->dump_networks)
|
||||||
|
finish_dump (nl);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
iv.buffer = g_malloc (len);
|
||||||
|
iv.size = len;
|
||||||
|
len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
|
||||||
|
&cmsgs, &num_cmsgs, NULL, NULL, &error);
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
g_warning ("Error on netlink socket: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
if (nl->priv->dump_networks)
|
||||||
|
finish_dump (nl);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_cmsgs != 1 || !G_IS_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
creds = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]));
|
||||||
|
sender = g_credentials_get_unix_user (creds, NULL);
|
||||||
|
if (sender != 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
msg = (struct nlmsghdr *) iv.buffer;
|
||||||
|
for (; len > 0; msg = NLMSG_NEXT (msg, len))
|
||||||
|
{
|
||||||
|
if (!NLMSG_OK (msg, (size_t) len))
|
||||||
|
{
|
||||||
|
g_warning ("netlink message was truncated; shouldn't happen...");
|
||||||
|
retval = FALSE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg->nlmsg_type)
|
||||||
|
{
|
||||||
|
case RTM_NEWROUTE:
|
||||||
|
case RTM_DELROUTE:
|
||||||
|
rtmsg = NLMSG_DATA (msg);
|
||||||
|
|
||||||
|
if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
|
||||||
|
continue;
|
||||||
|
if (rtmsg->rtm_type == RTN_UNREACHABLE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
|
||||||
|
attr = RTM_RTA (rtmsg);
|
||||||
|
dest = gateway = NULL;
|
||||||
|
while (RTA_OK (attr, attrlen))
|
||||||
|
{
|
||||||
|
if (attr->rta_type == RTA_DST)
|
||||||
|
dest = RTA_DATA (attr);
|
||||||
|
else if (attr->rta_type == RTA_GATEWAY)
|
||||||
|
gateway = RTA_DATA (attr);
|
||||||
|
attr = RTA_NEXT (attr, attrlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest || gateway)
|
||||||
|
{
|
||||||
|
if (msg->nlmsg_type == RTM_NEWROUTE)
|
||||||
|
add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
|
||||||
|
else
|
||||||
|
remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
|
||||||
|
queue_request_dump (nl);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NLMSG_DONE:
|
||||||
|
finish_dump (nl);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
case NLMSG_ERROR:
|
||||||
|
{
|
||||||
|
struct nlmsgerr *e = NLMSG_DATA (msg);
|
||||||
|
|
||||||
|
g_warning ("netlink error: %s", g_strerror (-e->error));
|
||||||
|
}
|
||||||
|
retval = FALSE;
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_warning ("unexpected netlink message %d", msg->nlmsg_type);
|
||||||
|
retval = FALSE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
for (i = 0; i < num_cmsgs; i++)
|
||||||
|
g_object_unref (cmsgs[i]);
|
||||||
|
g_free (cmsgs);
|
||||||
|
|
||||||
|
g_free (iv.buffer);
|
||||||
|
|
||||||
|
if (!retval && nl->priv->dump_networks)
|
||||||
|
finish_dump (nl);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_netlink_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
|
||||||
|
|
||||||
|
if (nl->priv->sock)
|
||||||
|
{
|
||||||
|
g_socket_close (nl->priv->sock, NULL);
|
||||||
|
g_object_unref (nl->priv->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nl->priv->source)
|
||||||
|
{
|
||||||
|
g_source_destroy (nl->priv->source);
|
||||||
|
g_source_unref (nl->priv->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nl->priv->dump_source)
|
||||||
|
{
|
||||||
|
g_source_destroy (nl->priv->dump_source);
|
||||||
|
g_source_unref (nl->priv->dump_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
|
||||||
|
|
||||||
|
g_type_class_add_private (nl_class, sizeof (GNetworkMonitorNetlinkPrivate));
|
||||||
|
|
||||||
|
gobject_class->finalize = g_network_monitor_netlink_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
|
||||||
|
{
|
||||||
|
iface->init = g_network_monitor_netlink_initable_init;
|
||||||
|
}
|
57
gio/gnetworkmonitornetlink.h
Normal file
57
gio/gnetworkmonitornetlink.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __G_NETWORK_MONITOR_NETLINK_H__
|
||||||
|
#define __G_NETWORK_MONITOR_NETLINK_H__
|
||||||
|
|
||||||
|
#include "gnetworkmonitorbase.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define G_TYPE_NETWORK_MONITOR_NETLINK (_g_network_monitor_netlink_get_type ())
|
||||||
|
#define G_NETWORK_MONITOR_NETLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlink))
|
||||||
|
#define G_NETWORK_MONITOR_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlinkClass))
|
||||||
|
#define G_IS_NETWORK_MONITOR_NETLINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NETWORK_MONITOR_NETLINK))
|
||||||
|
#define G_IS_NETWORK_MONITOR_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NETWORK_MONITOR_NETLINK))
|
||||||
|
#define G_NETWORK_MONITOR_NETLINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_NETWORK_MONITOR_NETLINK, GNetworkMonitorNetlinkClass))
|
||||||
|
|
||||||
|
typedef struct _GNetworkMonitorNetlink GNetworkMonitorNetlink;
|
||||||
|
typedef struct _GNetworkMonitorNetlinkClass GNetworkMonitorNetlinkClass;
|
||||||
|
typedef struct _GNetworkMonitorNetlinkPrivate GNetworkMonitorNetlinkPrivate;
|
||||||
|
|
||||||
|
struct _GNetworkMonitorNetlink {
|
||||||
|
GNetworkMonitorBase parent_instance;
|
||||||
|
|
||||||
|
GNetworkMonitorNetlinkPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GNetworkMonitorNetlinkClass {
|
||||||
|
GNetworkMonitorBaseClass parent_class;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
/* Padding for future expansion */
|
||||||
|
gpointer padding[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
GType _g_network_monitor_netlink_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __G_NETWORK_MONITOR_NETLINK_H__ */
|
1
gio/tests/.gitignore
vendored
1
gio/tests/.gitignore
vendored
@ -70,6 +70,7 @@ live-g-file
|
|||||||
memory-input-stream
|
memory-input-stream
|
||||||
memory-output-stream
|
memory-output-stream
|
||||||
mimeapps
|
mimeapps
|
||||||
|
network-monitor
|
||||||
network-address
|
network-address
|
||||||
org.gtk.test.enums.xml
|
org.gtk.test.enums.xml
|
||||||
pollable
|
pollable
|
||||||
|
@ -56,6 +56,7 @@ TEST_PROGS += \
|
|||||||
tls-interaction \
|
tls-interaction \
|
||||||
cancellable \
|
cancellable \
|
||||||
vfs \
|
vfs \
|
||||||
|
network-monitor \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
if OS_UNIX
|
if OS_UNIX
|
||||||
@ -492,6 +493,8 @@ cancellable_LDADD = $(progs_ldadd)
|
|||||||
|
|
||||||
vfs_LDADD = $(progs_ldadd)
|
vfs_LDADD = $(progs_ldadd)
|
||||||
|
|
||||||
|
network_monitor_LDADD = $(progs_ldadd)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
if OS_UNIX
|
if OS_UNIX
|
||||||
|
463
gio/tests/network-monitor.c
Normal file
463
gio/tests/network-monitor.c
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
/* GIO - GLib Input, Output and Streaming Library
|
||||||
|
*
|
||||||
|
* Copyright 2011 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 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gio.h"
|
||||||
|
|
||||||
|
/* hack */
|
||||||
|
#define GIO_COMPILATION
|
||||||
|
#include "gnetworkmonitorbase.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Test data; the GInetAddresses and GInetAddressMasks get filled in
|
||||||
|
* in main(). Each address in a TestAddress matches the mask in its
|
||||||
|
* corresponding TestMask, and none of them match any of the other
|
||||||
|
* masks. The addresses in unmatched don't match any of the masks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *string;
|
||||||
|
GInetAddress *address;
|
||||||
|
} TestAddress;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *mask_string;
|
||||||
|
GInetAddressMask *mask;
|
||||||
|
TestAddress *addresses;
|
||||||
|
} TestMask;
|
||||||
|
|
||||||
|
TestAddress net127addrs[] = {
|
||||||
|
{ "127.0.0.1", NULL },
|
||||||
|
{ "127.0.0.2", NULL },
|
||||||
|
{ "127.0.0.255", NULL },
|
||||||
|
{ "127.0.1.0", NULL },
|
||||||
|
{ "127.0.255.0", NULL },
|
||||||
|
{ "127.0.255.0", NULL },
|
||||||
|
{ "127.255.255.255", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
TestMask net127 = { "127.0.0.0/8", NULL, net127addrs };
|
||||||
|
|
||||||
|
TestAddress net10addrs[] = {
|
||||||
|
{ "10.0.0.1", NULL },
|
||||||
|
{ "10.0.0.2", NULL },
|
||||||
|
{ "10.0.0.255", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
TestMask net10 = { "10.0.0.0/24", NULL, net10addrs };
|
||||||
|
|
||||||
|
TestAddress net192addrs[] = {
|
||||||
|
{ "192.168.0.1", NULL },
|
||||||
|
{ "192.168.0.2", NULL },
|
||||||
|
{ "192.168.0.255", NULL },
|
||||||
|
{ "192.168.1.0", NULL },
|
||||||
|
{ "192.168.15.0", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
TestMask net192 = { "192.168.0.0/20", NULL, net192addrs };
|
||||||
|
|
||||||
|
TestAddress netlocal6addrs[] = {
|
||||||
|
{ "::1", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
TestMask netlocal6 = { "::1/128", NULL, netlocal6addrs };
|
||||||
|
|
||||||
|
TestAddress netfe80addrs[] = {
|
||||||
|
{ "fe80::", NULL },
|
||||||
|
{ "fe80::1", NULL },
|
||||||
|
{ "fe80::21b:77ff:fea2:972a", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
TestMask netfe80 = { "fe80::/64", NULL, netfe80addrs };
|
||||||
|
|
||||||
|
TestAddress unmatched[] = {
|
||||||
|
{ "10.0.1.0", NULL },
|
||||||
|
{ "10.0.255.0", NULL },
|
||||||
|
{ "10.255.255.255", NULL },
|
||||||
|
{ "192.168.16.0", NULL },
|
||||||
|
{ "192.168.255.0", NULL },
|
||||||
|
{ "192.169.0.0", NULL },
|
||||||
|
{ "192.255.255.255", NULL },
|
||||||
|
{ "::2", NULL },
|
||||||
|
{ "1::1", NULL },
|
||||||
|
{ "fe80::1:0:0:0:0", NULL },
|
||||||
|
{ "fe80:8000::0:0:0:0", NULL },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
GInetAddressMask *ip4_default, *ip6_default;
|
||||||
|
|
||||||
|
static void
|
||||||
|
notify_handler (GObject *object,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gboolean *emitted = user_data;
|
||||||
|
|
||||||
|
*emitted = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
network_changed_handler (GNetworkMonitor *monitor,
|
||||||
|
gboolean available,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
gboolean *emitted = user_data;
|
||||||
|
|
||||||
|
*emitted = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
assert_signals (GNetworkMonitor *monitor,
|
||||||
|
gboolean should_emit_notify,
|
||||||
|
gboolean should_emit_network_changed,
|
||||||
|
gboolean expected_network_available)
|
||||||
|
{
|
||||||
|
gboolean emitted_notify = FALSE, emitted_network_changed = FALSE;
|
||||||
|
guint h1, h2;
|
||||||
|
|
||||||
|
h1 = g_signal_connect (monitor, "notify::network-available",
|
||||||
|
G_CALLBACK (notify_handler),
|
||||||
|
&emitted_notify);
|
||||||
|
h2 = g_signal_connect (monitor, "network-changed",
|
||||||
|
G_CALLBACK (network_changed_handler),
|
||||||
|
&emitted_network_changed);
|
||||||
|
|
||||||
|
g_main_context_iteration (NULL, FALSE);
|
||||||
|
|
||||||
|
g_signal_handler_disconnect (monitor, h1);
|
||||||
|
g_signal_handler_disconnect (monitor, h2);
|
||||||
|
|
||||||
|
g_assert (emitted_notify == should_emit_notify);
|
||||||
|
g_assert (emitted_network_changed == should_emit_network_changed);
|
||||||
|
|
||||||
|
g_assert (g_network_monitor_get_network_available (monitor) == expected_network_available);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_tests (GNetworkMonitor *monitor,
|
||||||
|
TestAddress *addresses,
|
||||||
|
gboolean should_be_reachable)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
int i;
|
||||||
|
gboolean reachable;
|
||||||
|
GSocketAddress *sockaddr;
|
||||||
|
|
||||||
|
for (i = 0; addresses[i].address; i++)
|
||||||
|
{
|
||||||
|
sockaddr = g_inet_socket_address_new (addresses[i].address, 0);
|
||||||
|
reachable = g_network_monitor_can_reach (monitor,
|
||||||
|
G_SOCKET_CONNECTABLE (sockaddr),
|
||||||
|
NULL, &error);
|
||||||
|
g_object_unref (sockaddr);
|
||||||
|
g_assert_cmpint (reachable, ==, should_be_reachable);
|
||||||
|
if (should_be_reachable)
|
||||||
|
g_assert_no_error (error);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_assert (error != NULL);
|
||||||
|
g_clear_error (&error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_default (void)
|
||||||
|
{
|
||||||
|
GNetworkMonitor *monitor;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
/* In the default configuration, all addresses are reachable */
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, TRUE);
|
||||||
|
|
||||||
|
assert_signals (monitor, FALSE, FALSE, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_remove_default (void)
|
||||||
|
{
|
||||||
|
GNetworkMonitor *monitor;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
assert_signals (monitor, FALSE, FALSE, TRUE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip4_default);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, TRUE);
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip6_default);
|
||||||
|
assert_signals (monitor, TRUE, TRUE, FALSE);
|
||||||
|
|
||||||
|
/* Now nothing should be reachable */
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_add_networks (void)
|
||||||
|
{
|
||||||
|
GNetworkMonitor *monitor;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
assert_signals (monitor, FALSE, FALSE, TRUE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip4_default);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, TRUE);
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip6_default);
|
||||||
|
assert_signals (monitor, TRUE, TRUE, FALSE);
|
||||||
|
|
||||||
|
/* Now add the masks one by one */
|
||||||
|
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net127.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net10.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net192.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netlocal6.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netfe80.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_remove_networks (void)
|
||||||
|
{
|
||||||
|
GNetworkMonitor *monitor;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
assert_signals (monitor, FALSE, FALSE, TRUE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip4_default);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, TRUE);
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
ip6_default);
|
||||||
|
assert_signals (monitor, TRUE, TRUE, FALSE);
|
||||||
|
|
||||||
|
/* First add them */
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net127.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net10.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net192.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netlocal6.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netfe80.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
|
||||||
|
run_tests (monitor, net127.addresses, TRUE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
/* Now remove them one by one */
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net127.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, TRUE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net10.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, TRUE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
net192.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, TRUE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netlocal6.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, TRUE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
|
||||||
|
g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor),
|
||||||
|
netfe80.mask);
|
||||||
|
assert_signals (monitor, FALSE, TRUE, FALSE);
|
||||||
|
run_tests (monitor, net127.addresses, FALSE);
|
||||||
|
run_tests (monitor, net10.addresses, FALSE);
|
||||||
|
run_tests (monitor, net192.addresses, FALSE);
|
||||||
|
run_tests (monitor, netlocal6.addresses, FALSE);
|
||||||
|
run_tests (monitor, netfe80.addresses, FALSE);
|
||||||
|
run_tests (monitor, unmatched, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_test (TestMask *test)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
test->mask = g_inet_address_mask_new_from_string (test->mask_string, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
for (i = 0; test->addresses[i].string; i++)
|
||||||
|
{
|
||||||
|
test->addresses[i].address = g_inet_address_new_from_string (test->addresses[i].string);
|
||||||
|
if (strchr (test->addresses[i].string, ':'))
|
||||||
|
g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV6);
|
||||||
|
else
|
||||||
|
g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
watch_network_changed (GNetworkMonitor *monitor,
|
||||||
|
gboolean available,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_print ("Network is %s\n", available ? "up" : "down");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_watch_network (void)
|
||||||
|
{
|
||||||
|
GNetworkMonitor *monitor = g_network_monitor_get_default ();
|
||||||
|
GMainLoop *loop;
|
||||||
|
|
||||||
|
g_print ("Monitoring via %s\n", g_type_name_from_instance ((GTypeInstance *) monitor));
|
||||||
|
|
||||||
|
g_signal_connect (monitor, "network-changed",
|
||||||
|
G_CALLBACK (watch_network_changed), NULL);
|
||||||
|
watch_network_changed (monitor, g_network_monitor_get_network_available (monitor), NULL);
|
||||||
|
|
||||||
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_type_init ();
|
||||||
|
|
||||||
|
if (argc == 2 && !strcmp (argv[1], "--watch"))
|
||||||
|
{
|
||||||
|
do_watch_network ();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
init_test (&net127);
|
||||||
|
init_test (&net10);
|
||||||
|
init_test (&net192);
|
||||||
|
init_test (&netlocal6);
|
||||||
|
init_test (&netfe80);
|
||||||
|
ip4_default = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
|
||||||
|
ip6_default = g_inet_address_mask_new_from_string ("::/0", NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/network-monitor/default", test_default);
|
||||||
|
g_test_add_func ("/network-monitor/remove_default", test_remove_default);
|
||||||
|
g_test_add_func ("/network-monitor/add_networks", test_add_networks);
|
||||||
|
g_test_add_func ("/network-monitor/remove_networks", test_remove_networks);
|
||||||
|
|
||||||
|
return g_test_run ();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user