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:
Dan Winship 2011-06-12 15:59:36 -04:00
parent eb9755dc9c
commit fe5ba0f291
17 changed files with 1878 additions and 2 deletions

View File

@ -1109,6 +1109,10 @@ if test $glib_native_win32 = no; then
fi
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
*-*-solaris* )
AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)

View File

@ -104,7 +104,7 @@
<xi:include href="xml/gasyncinitable.xml"/>
</chapter>
<chapter id="networking">
<title>Lowlevel network support</title>
<title>Low-level network support</title>
<xi:include href="xml/gsocket.xml"/>
<xi:include href="xml/ginetaddress.xml"/>
<xi:include href="xml/ginetaddressmask.xml"/>
@ -120,7 +120,7 @@
<xi:include href="xml/gproxyaddress.xml"/>
</chapter>
<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/gsocketconnection.xml"/>
<xi:include href="xml/gunixconnection.xml"/>
@ -129,6 +129,7 @@
<xi:include href="xml/gsocketlistener.xml"/>
<xi:include href="xml/gsocketservice.xml"/>
<xi:include href="xml/gthreadedsocketservice.xml"/>
<xi:include href="xml/gnetworkmonitor.xml"/>
</chapter>
<chapter id="tls">
<title>TLS (SSL) support</title>

View File

@ -3460,3 +3460,23 @@ G_DBUS_OBJECT_MANAGER_SERVER_GET_CLASS
<SUBSECTION Private>
GDBusObjectManagerServerPrivate
</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>

View File

@ -232,6 +232,13 @@ giounixinclude_HEADERS = \
gunixoutputstream.h \
gunixsocketaddress.h \
$(NULL)
if HAVE_NETLINK
unix_sources += \
gnetworkmonitornetlink.c \
gnetworkmonitornetlink.h \
$(NULL)
endif
endif
win32_actual_sources = \
@ -332,6 +339,9 @@ libgio_2_0_la_SOURCES = \
gnativevolumemonitor.h \
gnetworkaddress.c \
gnetworkingprivate.h \
gnetworkmonitor.c \
gnetworkmonitorbase.c \
gnetworkmonitorbase.h \
gnetworkservice.c \
goutputstream.c \
gpermission.c \
@ -498,6 +508,7 @@ gio_headers = \
gmountoperation.h \
gnativevolumemonitor.h \
gnetworkaddress.h \
gnetworkmonitor.h \
gnetworkservice.h \
goutputstream.h \
gpermission.h \

View File

@ -92,6 +92,7 @@
#include <gio/gmountoperation.h>
#include <gio/gnativevolumemonitor.h>
#include <gio/gnetworkaddress.h>
#include <gio/gnetworkmonitor.h>
#include <gio/gnetworkservice.h>
#include <gio/goutputstream.h>
#include <gio/gpermission.h>

View File

@ -1564,3 +1564,13 @@ g_dbus_object_manager_server_set_connection
g_dbus_object_manager_server_get_type
g_dbus_object_manager_server_new
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

View File

@ -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_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
@ -850,6 +854,9 @@ _g_io_modules_ensure_extension_points_registered (void)
ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
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);
@ -921,6 +928,10 @@ _g_io_modules_ensure_loaded (void)
_g_socks4_proxy_get_type ();
_g_socks5_proxy_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);

View File

@ -123,6 +123,7 @@ typedef struct _GMemoryOutputStream GMemoryOutputStream;
typedef struct _GMount GMount; /* Dummy typedef */
typedef struct _GMountOperation GMountOperation;
typedef struct _GNetworkAddress GNetworkAddress;
typedef struct _GNetworkMonitor GNetworkMonitor;
typedef struct _GNetworkService GNetworkService;
typedef struct _GOutputStream GOutputStream;
typedef struct _GIOStream GIOStream;

261
gio/gnetworkmonitor.c Normal file
View 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
View 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
View 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
View 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__ */

View 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;
}

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

View File

@ -70,6 +70,7 @@ live-g-file
memory-input-stream
memory-output-stream
mimeapps
network-monitor
network-address
org.gtk.test.enums.xml
pollable

View File

@ -56,6 +56,7 @@ TEST_PROGS += \
tls-interaction \
cancellable \
vfs \
network-monitor \
$(NULL)
if OS_UNIX
@ -492,6 +493,8 @@ cancellable_LDADD = $(progs_ldadd)
vfs_LDADD = $(progs_ldadd)
network_monitor_LDADD = $(progs_ldadd)
# -----------------------------------------------------------------------------
if OS_UNIX

463
gio/tests/network-monitor.c Normal file
View 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 ();
}