(root)/
glib-2.79.0/
gio/
gwin32networkmonitor.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright 2014-2018 Jan-Michael Brummer <jan.brummer@tabos.org>
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, write to the
      19   * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
      20   * Boston, MA 02111-1307, USA.
      21   */
      22  
      23  #include "config.h"
      24  
      25  #include <errno.h>
      26  
      27  #ifdef HAVE_UNISTD_H
      28  #include <unistd.h>
      29  #endif
      30  
      31  #include <winsock2.h>
      32  #include <ws2tcpip.h>
      33  #include <iphlpapi.h>
      34  #include <stdio.h>
      35  
      36  #include "gwin32networkmonitor.h"
      37  #include "ginetaddress.h"
      38  #include "ginetaddressmask.h"
      39  #include "ginitable.h"
      40  #include "giomodule-priv.h"
      41  #include "glibintl.h"
      42  #include "glib/gstdio.h"
      43  #include "gnetworkingprivate.h"
      44  #include "gsocket.h"
      45  #include "gnetworkmonitor.h"
      46  #include "gioerror.h"
      47  
      48  static GInitableIface *initable_parent_iface;
      49  static void g_win32_network_monitor_iface_init (GNetworkMonitorInterface *iface);
      50  static void g_win32_network_monitor_initable_iface_init (GInitableIface *iface);
      51  
      52  struct _GWin32NetworkMonitorPrivate
      53  {
      54    gboolean initialized;
      55    GError *init_error;
      56    GMainContext *main_context;
      57    GSource *route_change_source;
      58    HANDLE handle;
      59  };
      60  
      61  #define g_win32_network_monitor_get_type _g_win32_network_monitor_get_type
      62  G_DEFINE_TYPE_WITH_CODE (GWin32NetworkMonitor, g_win32_network_monitor, G_TYPE_NETWORK_MONITOR_BASE,
      63                           G_ADD_PRIVATE (GWin32NetworkMonitor)
      64                           G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
      65                                                  g_win32_network_monitor_iface_init)
      66                           G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
      67                                                  g_win32_network_monitor_initable_iface_init)
      68                           _g_io_modules_ensure_extension_points_registered ();
      69                           g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
      70                                                           g_define_type_id,
      71                                                           "win32",
      72                                                           20))
      73  
      74  static void
      75  g_win32_network_monitor_init (GWin32NetworkMonitor *win)
      76  {
      77    win->priv = g_win32_network_monitor_get_instance_private (win);
      78  }
      79  
      80  static gboolean
      81  win_network_monitor_get_ip_info (const IP_ADDRESS_PREFIX  *prefix,
      82                                   GSocketFamily            *family,
      83                                   const guint8            **dest,
      84                                   gsize                    *len)
      85  {
      86    switch (prefix->Prefix.si_family)
      87      {
      88        case AF_UNSPEC:
      89          /* Fall-through: AF_UNSPEC deliveres both IPV4 and IPV6 infos, let`s stick with IPV4 here */
      90        case AF_INET:
      91          *family = G_SOCKET_FAMILY_IPV4;
      92          *dest = (guint8 *) &(prefix->Prefix.Ipv4.sin_addr);
      93          *len = prefix->PrefixLength;
      94          break;
      95        case AF_INET6:
      96          *family = G_SOCKET_FAMILY_IPV6;
      97          *dest = (guint8 *) &(prefix->Prefix.Ipv6.sin6_addr);
      98          *len = prefix->PrefixLength;
      99          break;
     100        default:
     101          return FALSE;
     102      }
     103  
     104    return TRUE;
     105  }
     106  
     107  static GInetAddressMask *
     108  get_network_mask (GSocketFamily  family,
     109                    const guint8  *dest,
     110                    gsize          len)
     111  {
     112    GInetAddressMask *network;
     113    GInetAddress *dest_addr;
     114  
     115    if (dest != NULL)
     116      dest_addr = g_inet_address_new_from_bytes (dest, family);
     117    else
     118      dest_addr = g_inet_address_new_any (family);
     119  
     120    network = g_inet_address_mask_new (dest_addr, len, NULL);
     121    g_object_unref (dest_addr);
     122  
     123    return network;
     124  }
     125  
     126  static gboolean
     127  win_network_monitor_process_table (GWin32NetworkMonitor  *win,
     128                                     GError                 **error)
     129  {
     130    DWORD ret = 0;
     131    GPtrArray *networks;
     132    gsize i;
     133    MIB_IPFORWARD_TABLE2 *routes = NULL;
     134    MIB_IPFORWARD_ROW2 *route;
     135  
     136    ret = GetIpForwardTable2 (AF_UNSPEC, &routes);
     137    if (ret != ERROR_SUCCESS)
     138      {
     139        g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
     140                     "GetIpForwardTable2 () failed: %ld", ret);
     141  
     142        return FALSE;
     143      }
     144  
     145    networks = g_ptr_array_new_full (routes->NumEntries, g_object_unref);
     146    for (i = 0; i < routes->NumEntries; i++)
     147      {
     148        GInetAddressMask *network;
     149        const guint8 *dest;
     150        gsize len;
     151        GSocketFamily family;
     152  
     153        route = routes->Table + i;
     154  
     155        if (!win_network_monitor_get_ip_info (&route->DestinationPrefix, &family, &dest, &len))
     156          continue;
     157  
     158        network = get_network_mask (family, dest, len);
     159        if (network == NULL)
     160          continue;
     161  
     162        g_ptr_array_add (networks, network);
     163      }
     164  
     165    g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (win),
     166                                         (GInetAddressMask **) networks->pdata,
     167                                         networks->len);
     168  
     169    return TRUE;
     170  }
     171  
     172  static void
     173  add_network (GWin32NetworkMonitor *win,
     174               GSocketFamily         family,
     175               const guint8         *dest,
     176               gsize                 dest_len)
     177  {
     178    GInetAddressMask *network;
     179  
     180    network = get_network_mask (family, dest, dest_len);
     181    if (network != NULL)
     182      {
     183        g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (win), network);
     184        g_object_unref (network);
     185      }
     186  }
     187  
     188  static void
     189  remove_network (GWin32NetworkMonitor *win,
     190                  GSocketFamily         family,
     191                  const guint8         *dest,
     192                  gsize                 dest_len)
     193  {
     194    GInetAddressMask *network;
     195  
     196    network = get_network_mask (family, dest, dest_len);
     197    if (network != NULL)
     198      {
     199        g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (win), network);
     200        g_object_unref (network);
     201      }
     202  }
     203  
     204  typedef struct {
     205    PMIB_IPFORWARD_ROW2 route;
     206    MIB_NOTIFICATION_TYPE type;
     207    GWin32NetworkMonitor *win;
     208  } RouteData;
     209  
     210  static gboolean
     211  win_network_monitor_invoke_route_changed (gpointer user_data)
     212  {
     213    GSocketFamily family;
     214    RouteData *route_data = user_data;
     215    const guint8 *dest;
     216    gsize len;
     217  
     218    switch (route_data->type)
     219      {
     220        case MibDeleteInstance:
     221          if (!win_network_monitor_get_ip_info (&route_data->route->DestinationPrefix, &family, &dest, &len))
     222            break;
     223  
     224          remove_network (route_data->win, family, dest, len);
     225          break;
     226        case MibAddInstance:
     227          if (!win_network_monitor_get_ip_info (&route_data->route->DestinationPrefix, &family, &dest, &len))
     228              break;
     229  
     230          add_network (route_data->win, family, dest, len);
     231          break;
     232        case MibInitialNotification:
     233        default:
     234          break;
     235      }
     236  
     237    return G_SOURCE_REMOVE;
     238  }
     239  
     240  static VOID WINAPI
     241  win_network_monitor_route_changed_cb (PVOID                 context,
     242                                        PMIB_IPFORWARD_ROW2   route,
     243                                        MIB_NOTIFICATION_TYPE type)
     244  {
     245    GWin32NetworkMonitor *win = context;
     246    RouteData *route_data;
     247  
     248    route_data = g_new0 (RouteData, 1);
     249    route_data->route = route;
     250    route_data->type = type;
     251    route_data->win = win;
     252  
     253    win->priv->route_change_source = g_idle_source_new ();
     254    g_source_set_priority (win->priv->route_change_source, G_PRIORITY_DEFAULT);
     255    g_source_set_callback (win->priv->route_change_source,
     256                           win_network_monitor_invoke_route_changed,
     257                           route_data,
     258                           g_free);
     259  
     260    g_source_attach (win->priv->route_change_source, win->priv->main_context);
     261  }
     262  
     263  static gboolean
     264  g_win32_network_monitor_initable_init (GInitable     *initable,
     265                                         GCancellable  *cancellable,
     266                                         GError       **error)
     267  {
     268    GWin32NetworkMonitor *win = G_WIN32_NETWORK_MONITOR (initable);
     269    NTSTATUS status;
     270    gboolean read;
     271  
     272    if (!win->priv->initialized)
     273      {
     274        win->priv->main_context = g_main_context_ref_thread_default ();
     275  
     276        /* Read current IP routing table. */
     277        read = win_network_monitor_process_table (win, &win->priv->init_error);
     278        if (read)
     279          {
     280            /* Register for IPv4 and IPv6 route updates. */
     281            status = NotifyRouteChange2 (AF_UNSPEC, (PIPFORWARD_CHANGE_CALLBACK) win_network_monitor_route_changed_cb, win, FALSE, &win->priv->handle);
     282            if (status != NO_ERROR)
     283              g_set_error (&win->priv->init_error, G_IO_ERROR, G_IO_ERROR_FAILED,
     284                           "NotifyRouteChange2() error: %ld", status);
     285          }
     286  
     287        win->priv->initialized = TRUE;
     288      }
     289  
     290    /* Forward the results. */
     291    if (win->priv->init_error != NULL)
     292      {
     293        g_propagate_error (error, g_error_copy (win->priv->init_error));
     294        return FALSE;
     295      }
     296  
     297    return initable_parent_iface->init (initable, cancellable, error);
     298  }
     299  
     300  static void
     301  g_win32_network_monitor_finalize (GObject *object)
     302  {
     303    GWin32NetworkMonitor *win = G_WIN32_NETWORK_MONITOR (object);
     304  
     305    /* Cancel notification event */
     306    if (win->priv->handle)
     307      CancelMibChangeNotify2 (win->priv->handle);
     308  
     309    g_clear_error (&win->priv->init_error);
     310  
     311    if (win->priv->route_change_source != NULL)
     312      {
     313        g_source_destroy (win->priv->route_change_source);
     314        g_source_unref (win->priv->route_change_source);
     315      }
     316  
     317    g_main_context_unref (win->priv->main_context);
     318  
     319    G_OBJECT_CLASS (g_win32_network_monitor_parent_class)->finalize (object);
     320  }
     321  
     322  static void
     323  g_win32_network_monitor_class_init (GWin32NetworkMonitorClass *win_class)
     324  {
     325    GObjectClass *gobject_class = G_OBJECT_CLASS (win_class);
     326  
     327    gobject_class->finalize = g_win32_network_monitor_finalize;
     328  }
     329  
     330  static void
     331  g_win32_network_monitor_iface_init (GNetworkMonitorInterface *monitor_iface)
     332  {
     333  }
     334  
     335  static void
     336  g_win32_network_monitor_initable_iface_init (GInitableIface *iface)
     337  {
     338    initable_parent_iface = g_type_interface_peek_parent (iface);
     339  
     340    iface->init = g_win32_network_monitor_initable_init;
     341  }