[glib] add GNetworkMonitor, for... monitoring the network



commit fe5ba0f291c11a22fbfe812d1c8315837fa14177
Author: Dan Winship <danw gnome org>
Date:   Sun Jun 12 15:59:36 2011 -0400

    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

 configure.ac                        |    4 +
 docs/reference/gio/gio-docs.xml     |    5 +-
 docs/reference/gio/gio-sections.txt |   20 ++
 gio/Makefile.am                     |   11 +
 gio/gio.h                           |    1 +
 gio/gio.symbols                     |   10 +
 gio/giomodule.c                     |   11 +
 gio/giotypes.h                      |    1 +
 gio/gnetworkmonitor.c               |  261 ++++++++++++++++++++
 gio/gnetworkmonitor.h               |   89 +++++++
 gio/gnetworkmonitorbase.c           |  412 +++++++++++++++++++++++++++++++
 gio/gnetworkmonitorbase.h           |   66 +++++
 gio/gnetworkmonitornetlink.c        |  465 +++++++++++++++++++++++++++++++++++
 gio/gnetworkmonitornetlink.h        |   57 +++++
 gio/tests/.gitignore                |    1 +
 gio/tests/Makefile.am               |    3 +
 gio/tests/network-monitor.c         |  463 ++++++++++++++++++++++++++++++++++
 17 files changed, 1878 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index eeb74e8..82f5499 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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)
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index fd408aa..00ed715 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -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>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 33287c0..72b1ffb 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -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>
+
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 249cc7b..7e819a1 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -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 		\
diff --git a/gio/gio.h b/gio/gio.h
index ab65b1b..042c938 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -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>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 9ac949f..d861395 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -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
diff --git a/gio/giomodule.c b/gio/giomodule.c
index eac317a..efd8388 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -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);
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 6081322..4077dbb 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -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;
diff --git a/gio/gnetworkmonitor.c b/gio/gnetworkmonitor.c
new file mode 100644
index 0000000..33e9704
--- /dev/null
+++ b/gio/gnetworkmonitor.c
@@ -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));
+}
diff --git a/gio/gnetworkmonitor.h b/gio/gnetworkmonitor.h
new file mode 100644
index 0000000..e525293
--- /dev/null
+++ b/gio/gnetworkmonitor.h
@@ -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__ */
diff --git a/gio/gnetworkmonitorbase.c b/gio/gnetworkmonitorbase.c
new file mode 100644
index 0000000..2ac4115
--- /dev/null
+++ b/gio/gnetworkmonitorbase.c
@@ -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]);
+}
diff --git a/gio/gnetworkmonitorbase.h b/gio/gnetworkmonitorbase.h
new file mode 100644
index 0000000..47ef336
--- /dev/null
+++ b/gio/gnetworkmonitorbase.h
@@ -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__ */
diff --git a/gio/gnetworkmonitornetlink.c b/gio/gnetworkmonitornetlink.c
new file mode 100644
index 0000000..da60b8a
--- /dev/null
+++ b/gio/gnetworkmonitornetlink.c
@@ -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;
+}
diff --git a/gio/gnetworkmonitornetlink.h b/gio/gnetworkmonitornetlink.h
new file mode 100644
index 0000000..f6fa22a
--- /dev/null
+++ b/gio/gnetworkmonitornetlink.h
@@ -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__ */
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 8fdd74f..e3961af 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -70,6 +70,7 @@ live-g-file
 memory-input-stream
 memory-output-stream
 mimeapps
+network-monitor
 network-address
 org.gtk.test.enums.xml
 pollable
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 2091366..8d23c7a 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -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
diff --git a/gio/tests/network-monitor.c b/gio/tests/network-monitor.c
new file mode 100644
index 0000000..0ba9ec4
--- /dev/null
+++ b/gio/tests/network-monitor.c
@@ -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 ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]