[glib] GNetworkMonitorWindows: Add IPv4/IPv6 network monitor backend for windows



commit f9aacf3952effff897ab42991b5ba9090de5d970
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Tue Dec 26 14:06:08 2017 +0100

    GNetworkMonitorWindows: Add IPv4/IPv6 network monitor backend for windows
    
    Added a Windows backend to GNetworkMonitor, using NotifyRouteChange2()
    (available on Vista and later). It marshals the route change callbacks
    to the thread-specific default main context the GNetworkMonitor was
    constructed in.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=685442

 gio/Makefile.am              |    2 +
 gio/giomodule.c              |    4 +
 gio/gnetworkmonitorwindows.c |  333 ++++++++++++++++++++++++++++++++++++++++++
 gio/gnetworkmonitorwindows.h |   53 +++++++
 gio/meson.build              |    2 +
 5 files changed, 394 insertions(+), 0 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 9b3d04e..c000852 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -345,6 +345,8 @@ win32_actual_sources = \
        gwin32outputstream.c \
        gwin32outputstream.h \
        gwin32networking.h \
+       gnetworkmonitorwindows.c \
+       gnetworkmonitorwindows.h \
        $(NULL)
 
 win32_more_sources_for_vcproj = \
diff --git a/gio/giomodule.c b/gio/giomodule.c
index 1adfd93..1d8043d 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -964,6 +964,7 @@ extern GType g_cocoa_notification_backend_get_type (void);
 #ifdef G_PLATFORM_WIN32
 
 #include <windows.h>
+extern GType _g_network_monitor_windows_get_type (void);
 
 static HMODULE gio_dll = NULL;
 
@@ -1180,6 +1181,9 @@ _g_io_modules_ensure_loaded (void)
       g_type_ensure (_g_network_monitor_netlink_get_type ());
       g_type_ensure (_g_network_monitor_nm_get_type ());
 #endif
+#ifdef G_OS_WIN32
+      g_type_ensure (_g_network_monitor_windows_get_type ());
+#endif
     }
 
   G_UNLOCK (loaded_dirs);
diff --git a/gio/gnetworkmonitorwindows.c b/gio/gnetworkmonitorwindows.c
new file mode 100644
index 0000000..369438b
--- /dev/null
+++ b/gio/gnetworkmonitorwindows.c
@@ -0,0 +1,333 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2014-2018 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ * 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 <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <stdio.h>
+
+#include "gnetworkmonitorwindows.h"
+#include "ginetaddress.h"
+#include "ginetaddressmask.h"
+#include "ginitable.h"
+#include "giomodule-priv.h"
+#include "glibintl.h"
+#include "glib/gstdio.h"
+#include "gnetworkingprivate.h"
+#include "gsocket.h"
+#include "gnetworkmonitor.h"
+#include "gioerror.h"
+
+static void g_network_monitor_windows_iface_init (GNetworkMonitorInterface *iface);
+static void g_network_monitor_windows_initable_iface_init (GInitableIface *iface);
+
+struct _GNetworkMonitorWindowsPrivate
+{
+  gboolean initialized;
+  GError *init_error;
+  GMainContext *main_context;
+  GSource *route_change_source;
+  HANDLE handle;
+};
+
+#define g_network_monitor_windows_get_type _g_network_monitor_windows_get_type
+G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorWindows, g_network_monitor_windows, G_TYPE_NETWORK_MONITOR_BASE,
+                         G_ADD_PRIVATE (GNetworkMonitorWindows)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
+                                                g_network_monitor_windows_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_network_monitor_windows_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,
+                                                         "windows",
+                                                         20))
+
+static void
+g_network_monitor_windows_init (GNetworkMonitorWindows *win)
+{
+  win->priv = g_network_monitor_windows_get_instance_private (win);
+}
+
+static gboolean
+win_network_monitor_get_ip_info (IP_ADDRESS_PREFIX  prefix,
+                                 GSocketFamily     *family,
+                                 const guint8     **dest,
+                                 gsize             *len)
+{
+  switch (prefix.Prefix.si_family)
+    {
+      case AF_UNSPEC:
+        /* Fall-through: AF_UNSPEC deliveres both IPV4 and IPV6 infos, let`s stick with IPV4 here */
+      case AF_INET:
+        *family = G_SOCKET_FAMILY_IPV4;
+        *dest = (guint8 *) &prefix.Prefix.Ipv4.sin_addr;
+        *len = prefix.PrefixLength;
+        break;
+      case AF_INET6:
+        *family = G_SOCKET_FAMILY_IPV6;
+        *dest = (guint8 *) &prefix.Prefix.Ipv6.sin6_addr;
+        *len = prefix.PrefixLength;
+        break;
+      default:
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GInetAddressMask *
+get_network_mask (GSocketFamily  family,
+                  const guint8  *dest,
+                  gsize          len)
+{
+  GInetAddressMask *network;
+  GInetAddress *dest_addr;
+
+  if (dest != NULL)
+    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, len, NULL);
+  g_object_unref (dest_addr);
+
+  return network;
+}
+
+static gboolean
+win_network_monitor_process_table (GNetworkMonitorWindows  *win,
+                                   GError                 **error)
+{
+  DWORD ret = 0;
+  GPtrArray *networks;
+  gsize i;
+  MIB_IPFORWARD_TABLE2 *routes = NULL;
+  MIB_IPFORWARD_ROW2 *route;
+
+  ret = GetIpForwardTable2 (AF_UNSPEC, &routes);
+  if (ret != ERROR_SUCCESS)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "GetIpForwardTable2 () failed: %ld", ret);
+
+      return FALSE;
+    }
+
+  networks = g_ptr_array_new_full (routes->NumEntries, g_object_unref);
+  for (i = 0; i < routes->NumEntries; i++)
+    {
+      GInetAddressMask *network;
+      const guint8 *dest;
+      gsize len;
+      GSocketFamily family;
+
+      route = routes->Table + i;
+
+      if (!win_network_monitor_get_ip_info (route->DestinationPrefix, &family, &dest, &len))
+        continue;
+
+      network = get_network_mask (family, dest, len);
+      if (network == NULL)
+        continue;
+
+      g_ptr_array_add (networks, network);
+    }
+
+  g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (win),
+                                       (GInetAddressMask **) networks->pdata,
+                                       networks->len);
+
+  return TRUE;
+}
+
+static void
+add_network (GNetworkMonitorWindows *win,
+             GSocketFamily           family,
+             const guint8           *dest,
+             gsize                   dest_len)
+{
+  GInetAddressMask *network;
+
+  network = get_network_mask (family, dest, dest_len);
+  if (network != NULL)
+    {
+      g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (win), network);
+      g_object_unref (network);
+    }
+}
+
+static void
+remove_network (GNetworkMonitorWindows *win,
+                GSocketFamily           family,
+                const guint8           *dest,
+                gsize                   dest_len)
+{
+  GInetAddressMask *network;
+
+  network = get_network_mask (family, dest, dest_len);
+  if (network != NULL)
+    {
+      g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (win), network);
+      g_object_unref (network);
+    }
+}
+
+typedef struct {
+  PMIB_IPFORWARD_ROW2 route;
+  MIB_NOTIFICATION_TYPE type;
+  GNetworkMonitorWindows *win;
+} RouteData;
+
+static gboolean
+win_network_monitor_invoke_route_changed (gpointer user_data)
+{
+  GSocketFamily family;
+  RouteData *route_data = user_data;
+  const guint8 *dest;
+  gsize len;
+
+  switch (route_data->type)
+    {
+      case MibDeleteInstance:
+        if (!win_network_monitor_get_ip_info (route_data->route->DestinationPrefix, &family, &dest, &len))
+          break;
+
+        remove_network (route_data->win, family, dest, len);
+        break;
+      case MibAddInstance:
+        if (!win_network_monitor_get_ip_info (route_data->route->DestinationPrefix, &family, &dest, &len))
+            break;
+
+        add_network (route_data->win, family, dest, len);
+        break;
+      case MibInitialNotification:
+      default:
+        break;
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+static VOID WINAPI
+win_network_monitor_route_changed_cb (PVOID                 context,
+                                      PMIB_IPFORWARD_ROW2   route,
+                                      MIB_NOTIFICATION_TYPE type)
+{
+  GNetworkMonitorWindows *win = context;
+  RouteData *route_data;
+
+  route_data = g_new0 (RouteData, 1);
+  route_data->route = route;
+  route_data->type = type;
+  route_data->win = win;
+
+  win->priv->route_change_source = g_idle_source_new ();
+  g_source_set_priority (win->priv->route_change_source, G_PRIORITY_DEFAULT);
+  g_source_set_callback (win->priv->route_change_source,
+                         win_network_monitor_invoke_route_changed,
+                         route_data,
+                         g_free);
+
+  g_source_attach (win->priv->route_change_source, win->priv->main_context);
+}
+
+static gboolean
+g_network_monitor_windows_initable_init (GInitable     *initable,
+                                         GCancellable  *cancellable,
+                                         GError       **error)
+{
+  GNetworkMonitorWindows *win = G_NETWORK_MONITOR_WINDOWS (initable);
+  NTSTATUS status;
+  gboolean read;
+
+  if (!win->priv->initialized)
+    {
+      win->priv->main_context = g_main_context_ref_thread_default ();
+
+      /* Read current IP routing table. */
+      read = win_network_monitor_process_table (win, &win->priv->init_error);
+      if (read)
+        {
+          /* Register for IPv4 and IPv6 route updates. */
+          status = NotifyRouteChange2 (AF_UNSPEC, (PIPFORWARD_CHANGE_CALLBACK) 
win_network_monitor_route_changed_cb, win, FALSE, &win->priv->handle);
+          if (status != NO_ERROR)
+            g_set_error (&win->priv->init_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                         "NotifyRouteChange2() error: %ld", status);
+        }
+
+      win->priv->initialized = TRUE;
+    }
+
+  /* Forward the results. */
+  if (win->priv->init_error != NULL)
+    {
+      g_propagate_error (error, g_error_copy (win->priv->init_error));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+g_network_monitor_windows_finalize (GObject *object)
+{
+  GNetworkMonitorWindows *win = G_NETWORK_MONITOR_WINDOWS (object);
+
+  /* Cancel notification event */
+  if (win->priv->handle)
+    CancelMibChangeNotify2 (win->priv->handle);
+
+  g_clear_error (&win->priv->init_error);
+
+  if (win->priv->route_change_source != NULL)
+    {
+      g_source_destroy (win->priv->route_change_source);
+      g_source_unref (win->priv->route_change_source);
+    }
+
+  g_main_context_unref (win->priv->main_context);
+
+  G_OBJECT_CLASS (g_network_monitor_windows_parent_class)->finalize (object);
+}
+
+static void
+g_network_monitor_windows_class_init (GNetworkMonitorWindowsClass *win_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (win_class);
+
+  gobject_class->finalize = g_network_monitor_windows_finalize;
+}
+
+static void
+g_network_monitor_windows_iface_init (GNetworkMonitorInterface *monitor_iface)
+{
+}
+
+static void
+g_network_monitor_windows_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_network_monitor_windows_initable_init;
+}
diff --git a/gio/gnetworkmonitorwindows.h b/gio/gnetworkmonitorwindows.h
new file mode 100644
index 0000000..ea8185f
--- /dev/null
+++ b/gio/gnetworkmonitorwindows.h
@@ -0,0 +1,53 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2014-2018 Jan-Michael Brummer <jan brummer tabos org>
+ *
+ * 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_WINDOWS_H__
+#define __G_NETWORK_MONITOR_WINDOWS_H__
+
+#include "gnetworkmonitorbase.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NETWORK_MONITOR_WINDOWS         (_g_network_monitor_windows_get_type ())
+#define G_NETWORK_MONITOR_WINDOWS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
G_TYPE_NETWORK_MONITOR_WINDOWS, GNetworkMonitorWindows))
+#define G_NETWORK_MONITOR_WINDOWS_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NETWORK_MONITOR_WINDOWS, 
GNetworkMonitorWindowsClass))
+#define G_IS_NETWORK_MONITOR_WINDOWS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
G_TYPE_NETWORK_MONITOR_WINDOWS))
+#define G_IS_NETWORK_MONITOR_WINDOWS_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
G_TYPE_NETWORK_MONITOR_WINDOWS))
+#define G_NETWORK_MONITOR_WINDOWS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
G_TYPE_NETWORK_MONITOR_WINDOWS, GNetworkMonitorWindowsClass))
+
+typedef struct _GNetworkMonitorWindows        GNetworkMonitorWindows;
+typedef struct _GNetworkMonitorWindowsClass   GNetworkMonitorWindowsClass;
+typedef struct _GNetworkMonitorWindowsPrivate GNetworkMonitorWindowsPrivate;
+
+struct _GNetworkMonitorWindows {
+  GNetworkMonitorBase parent_instance;
+
+  GNetworkMonitorWindowsPrivate *priv;
+};
+
+struct _GNetworkMonitorWindowsClass {
+  GNetworkMonitorBaseClass parent_class;
+};
+
+GType _g_network_monitor_windows_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_NETWORK_MONITOR_WINDOWS_H__ */
diff --git a/gio/meson.build b/gio/meson.build
index 8316073..6d1e081 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -388,6 +388,8 @@ else
     'gwin32volumemonitor.c',
     'gwin32inputstream.c',
     'gwin32outputstream.c',
+    'gnetworkmonitorwindows.c',
+    'gnetworkmonitorwindows.h',
   )
 
   gio_win_rc = configure_file(


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