1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2011 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "gnetworkmonitorbase.h"
22 #include "ginetaddress.h"
23 #include "ginetaddressmask.h"
24 #include "ginetsocketaddress.h"
25 #include "ginitable.h"
26 #include "gioerror.h"
27 #include "giomodule-priv.h"
28 #include "gnetworkmonitor.h"
29 #include "gsocketaddressenumerator.h"
30 #include "gsocketconnectable.h"
31 #include "gtask.h"
32 #include "glibintl.h"
33 
34 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
35 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
36 
37 enum
38 {
39   PROP_0,
40 
41   PROP_NETWORK_AVAILABLE,
42   PROP_NETWORK_METERED,
43   PROP_CONNECTIVITY
44 };
45 
46 struct _GNetworkMonitorBasePrivate
47 {
48   GHashTable   *networks  /* (element-type GInetAddressMask) (owned) */;
49   gboolean      have_ipv4_default_route;
50   gboolean      have_ipv6_default_route;
51   gboolean      is_available;
52 
53   GMainContext *context;
54   GSource      *network_changed_source;
55   gboolean      initializing;
56 };
57 
58 static guint network_changed_signal = 0;
59 
60 static void queue_network_changed (GNetworkMonitorBase *monitor);
61 static guint inet_address_mask_hash (gconstpointer key);
62 static gboolean inet_address_mask_equal (gconstpointer a,
63                                          gconstpointer b);
64 
65 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
66                          G_ADD_PRIVATE (GNetworkMonitorBase)
67                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
68                                                 g_network_monitor_base_initable_iface_init)
69                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
70                                                 g_network_monitor_base_iface_init)
71                          _g_io_modules_ensure_extension_points_registered ();
72                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
73                                                          g_define_type_id,
74                                                          "base",
75                                                          0))
76 
77 static void
g_network_monitor_base_init(GNetworkMonitorBase * monitor)78 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
79 {
80   monitor->priv = g_network_monitor_base_get_instance_private (monitor);
81   monitor->priv->networks = g_hash_table_new_full (inet_address_mask_hash,
82                                                    inet_address_mask_equal,
83                                                    g_object_unref, NULL);
84   monitor->priv->context = g_main_context_get_thread_default ();
85   if (monitor->priv->context)
86     g_main_context_ref (monitor->priv->context);
87 
88   monitor->priv->initializing = TRUE;
89 }
90 
91 static void
g_network_monitor_base_constructed(GObject * object)92 g_network_monitor_base_constructed (GObject *object)
93 {
94   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
95 
96   if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
97     {
98       GInetAddressMask *mask;
99 
100       /* We're the dumb base class, not a smarter subclass. So just
101        * assume that the network is available.
102        */
103       mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
104       g_network_monitor_base_add_network (monitor, mask);
105       g_object_unref (mask);
106 
107       mask = g_inet_address_mask_new_from_string ("::/0", NULL);
108       if (mask)
109         {
110           /* On some environments (for example Windows without IPv6 support
111            * enabled) the string "::/0" can't be processed and causes
112            * g_inet_address_mask_new_from_string to return NULL */
113           g_network_monitor_base_add_network (monitor, mask);
114           g_object_unref (mask);
115         }
116     }
117 }
118 
119 static void
g_network_monitor_base_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)120 g_network_monitor_base_get_property (GObject    *object,
121                                      guint       prop_id,
122                                      GValue     *value,
123                                      GParamSpec *pspec)
124 {
125   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
126 
127   switch (prop_id)
128     {
129     case PROP_NETWORK_AVAILABLE:
130       g_value_set_boolean (value, monitor->priv->is_available);
131       break;
132 
133     case PROP_NETWORK_METERED:
134       /* Default to FALSE in the unknown case. */
135       g_value_set_boolean (value, FALSE);
136       break;
137 
138     case PROP_CONNECTIVITY:
139       g_value_set_enum (value,
140                         monitor->priv->is_available ?
141                         G_NETWORK_CONNECTIVITY_FULL :
142                         G_NETWORK_CONNECTIVITY_LOCAL);
143       break;
144 
145     default:
146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147       break;
148     }
149 
150 }
151 
152 static void
g_network_monitor_base_finalize(GObject * object)153 g_network_monitor_base_finalize (GObject *object)
154 {
155   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
156 
157   g_hash_table_unref (monitor->priv->networks);
158   if (monitor->priv->network_changed_source)
159     {
160       g_source_destroy (monitor->priv->network_changed_source);
161       g_source_unref (monitor->priv->network_changed_source);
162     }
163   if (monitor->priv->context)
164     g_main_context_unref (monitor->priv->context);
165 
166   G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
167 }
168 
169 static void
g_network_monitor_base_class_init(GNetworkMonitorBaseClass * monitor_class)170 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
171 {
172   GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
173 
174   gobject_class->constructed  = g_network_monitor_base_constructed;
175   gobject_class->get_property = g_network_monitor_base_get_property;
176   gobject_class->finalize     = g_network_monitor_base_finalize;
177 
178   g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
179   g_object_class_override_property (gobject_class, PROP_NETWORK_METERED, "network-metered");
180   g_object_class_override_property (gobject_class, PROP_CONNECTIVITY, "connectivity");
181 }
182 
183 static gboolean
g_network_monitor_base_can_reach_sockaddr(GNetworkMonitorBase * base,GSocketAddress * sockaddr)184 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
185                                            GSocketAddress *sockaddr)
186 {
187   GInetAddress *iaddr;
188   GHashTableIter iter;
189   gpointer key;
190 
191   if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
192     return FALSE;
193 
194   iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
195   g_hash_table_iter_init (&iter, base->priv->networks);
196   while (g_hash_table_iter_next (&iter, &key, NULL))
197     {
198       GInetAddressMask *mask = key;
199       if (g_inet_address_mask_matches (mask, iaddr))
200         return TRUE;
201     }
202 
203   return FALSE;
204 }
205 
206 static gboolean
g_network_monitor_base_can_reach(GNetworkMonitor * monitor,GSocketConnectable * connectable,GCancellable * cancellable,GError ** error)207 g_network_monitor_base_can_reach (GNetworkMonitor      *monitor,
208                                   GSocketConnectable   *connectable,
209                                   GCancellable         *cancellable,
210                                   GError              **error)
211 {
212   GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
213   GSocketAddressEnumerator *enumerator;
214   GSocketAddress *addr;
215 
216   if (g_hash_table_size (base->priv->networks) == 0)
217     {
218       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
219                            _("Network unreachable"));
220       return FALSE;
221     }
222 
223   enumerator = g_socket_connectable_proxy_enumerate (connectable);
224   addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
225   if (!addr)
226     {
227       /* Either the user cancelled, or DNS resolution failed */
228       g_object_unref (enumerator);
229       return FALSE;
230     }
231 
232   if (base->priv->have_ipv4_default_route &&
233       base->priv->have_ipv6_default_route)
234     {
235       g_object_unref (enumerator);
236       g_object_unref (addr);
237       return TRUE;
238     }
239 
240   while (addr)
241     {
242       if (g_network_monitor_base_can_reach_sockaddr (base, addr))
243         {
244           g_object_unref (addr);
245           g_object_unref (enumerator);
246           return TRUE;
247         }
248 
249       g_object_unref (addr);
250       addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
251     }
252   g_object_unref (enumerator);
253 
254   if (error && !*error)
255     {
256       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
257                            _("Host unreachable"));
258     }
259   return FALSE;
260 }
261 
262 static void
can_reach_async_got_address(GObject * object,GAsyncResult * result,gpointer user_data)263 can_reach_async_got_address (GObject      *object,
264                              GAsyncResult *result,
265                              gpointer      user_data)
266 {
267   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
268   GTask *task = user_data;
269   GNetworkMonitorBase *base = g_task_get_source_object (task);
270   GSocketAddress *addr;
271   GError *error = NULL;
272 
273   addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
274   if (!addr)
275     {
276       if (error)
277         {
278           /* Either the user cancelled, or DNS resolution failed */
279           g_task_return_error (task, error);
280           g_object_unref (task);
281           return;
282         }
283       else
284         {
285           /* Resolved all addresses, none matched */
286           g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
287                                    _("Host unreachable"));
288           g_object_unref (task);
289           return;
290         }
291     }
292 
293   if (g_network_monitor_base_can_reach_sockaddr (base, addr))
294     {
295       g_object_unref (addr);
296       g_task_return_boolean (task, TRUE);
297       g_object_unref (task);
298       return;
299     }
300   g_object_unref (addr);
301 
302   g_socket_address_enumerator_next_async (enumerator,
303                                           g_task_get_cancellable (task),
304                                           can_reach_async_got_address, task);
305 }
306 
307 static void
g_network_monitor_base_can_reach_async(GNetworkMonitor * monitor,GSocketConnectable * connectable,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)308 g_network_monitor_base_can_reach_async (GNetworkMonitor     *monitor,
309                                         GSocketConnectable  *connectable,
310                                         GCancellable        *cancellable,
311                                         GAsyncReadyCallback  callback,
312                                         gpointer             user_data)
313 {
314   GTask *task;
315   GSocketAddressEnumerator *enumerator;
316 
317   task = g_task_new (monitor, cancellable, callback, user_data);
318   g_task_set_source_tag (task, g_network_monitor_base_can_reach_async);
319 
320   if (g_hash_table_size (G_NETWORK_MONITOR_BASE (monitor)->priv->networks) == 0)
321     {
322       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
323                                _("Network unreachable"));
324       g_object_unref (task);
325       return;
326     }
327 
328   enumerator = g_socket_connectable_proxy_enumerate (connectable);
329   g_socket_address_enumerator_next_async (enumerator, cancellable,
330                                           can_reach_async_got_address, task);
331   g_object_unref (enumerator);
332 }
333 
334 static gboolean
g_network_monitor_base_can_reach_finish(GNetworkMonitor * monitor,GAsyncResult * result,GError ** error)335 g_network_monitor_base_can_reach_finish (GNetworkMonitor  *monitor,
336                                          GAsyncResult     *result,
337                                          GError          **error)
338 {
339   g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
340 
341   return g_task_propagate_boolean (G_TASK (result), error);
342 }
343 
344 static void
g_network_monitor_base_iface_init(GNetworkMonitorInterface * monitor_iface)345 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
346 {
347   monitor_iface->can_reach = g_network_monitor_base_can_reach;
348   monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
349   monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
350 
351   network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
352 }
353 
354 static gboolean
g_network_monitor_base_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)355 g_network_monitor_base_initable_init (GInitable     *initable,
356                                       GCancellable  *cancellable,
357                                       GError       **error)
358 {
359   GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (initable);
360 
361   base->priv->initializing = FALSE;
362 
363   return TRUE;
364 }
365 
366 static void
g_network_monitor_base_initable_iface_init(GInitableIface * iface)367 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
368 {
369   iface->init = g_network_monitor_base_initable_init;
370 }
371 
372 static guint
inet_address_mask_hash(gconstpointer key)373 inet_address_mask_hash (gconstpointer key)
374 {
375   GInetAddressMask *mask = G_INET_ADDRESS_MASK (key);
376   guint addr_hash;
377   guint mask_length = g_inet_address_mask_get_length (mask);
378   GInetAddress *addr = g_inet_address_mask_get_address (mask);
379   const guint8 *bytes = g_inet_address_to_bytes (addr);
380   gsize bytes_length = g_inet_address_get_native_size (addr);
381 
382   union
383     {
384       const guint8 *bytes;
385       guint32 *hash32;
386       guint64 *hash64;
387     } integerifier;
388 
389   /* If we can fit the entire address into the hash key, do it. Don’t worry
390    * about endianness; the address should always be in network endianness. */
391   if (bytes_length == sizeof (guint32))
392     {
393       integerifier.bytes = bytes;
394       addr_hash = *integerifier.hash32;
395     }
396   else if (bytes_length == sizeof (guint64))
397     {
398       integerifier.bytes = bytes;
399       addr_hash = *integerifier.hash64;
400     }
401   else
402     {
403       gsize i;
404 
405       /* Otherwise, fall back to adding the bytes together. We do this, rather
406        * than XORing them, as routes often have repeated tuples which would
407        * cancel out under XOR. */
408       addr_hash = 0;
409       for (i = 0; i < bytes_length; i++)
410         addr_hash += bytes[i];
411     }
412 
413   return addr_hash + mask_length;;
414 }
415 
416 static gboolean
inet_address_mask_equal(gconstpointer a,gconstpointer b)417 inet_address_mask_equal (gconstpointer a,
418                          gconstpointer b)
419 {
420   GInetAddressMask *mask_a = G_INET_ADDRESS_MASK (a);
421   GInetAddressMask *mask_b = G_INET_ADDRESS_MASK (b);
422 
423   return g_inet_address_mask_equal (mask_a, mask_b);
424 }
425 
426 static gboolean
emit_network_changed(gpointer user_data)427 emit_network_changed (gpointer user_data)
428 {
429   GNetworkMonitorBase *monitor = user_data;
430   gboolean is_available;
431 
432   if (g_source_is_destroyed (g_main_current_source ()))
433     return FALSE;
434 
435   g_object_ref (monitor);
436 
437   is_available = (monitor->priv->have_ipv4_default_route ||
438                   monitor->priv->have_ipv6_default_route);
439   if (monitor->priv->is_available != is_available)
440     {
441       monitor->priv->is_available = is_available;
442       g_object_notify (G_OBJECT (monitor), "network-available");
443     }
444 
445   g_signal_emit (monitor, network_changed_signal, 0, is_available);
446 
447   g_source_unref (monitor->priv->network_changed_source);
448   monitor->priv->network_changed_source = NULL;
449 
450   g_object_unref (monitor);
451   return FALSE;
452 }
453 
454 static void
queue_network_changed(GNetworkMonitorBase * monitor)455 queue_network_changed (GNetworkMonitorBase *monitor)
456 {
457   if (!monitor->priv->network_changed_source &&
458       !monitor->priv->initializing)
459     {
460       GSource *source;
461 
462       source = g_idle_source_new ();
463       /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
464        * network-change-related notifications coming in at
465        * G_PRIORITY_DEFAULT will get coalesced into one signal
466        * emission.
467        */
468       g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
469       g_source_set_callback (source, emit_network_changed, monitor, NULL);
470       g_source_set_static_name (source, "[gio] emit_network_changed");
471       g_source_attach (source, monitor->priv->context);
472       monitor->priv->network_changed_source = source;
473     }
474 
475   /* Normally we wait to update is_available until we emit the signal,
476    * to keep things consistent. But when we're first creating the
477    * object, we want it to be correct right away.
478    */
479   if (monitor->priv->initializing)
480     {
481       monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
482                                      monitor->priv->have_ipv6_default_route);
483     }
484 }
485 
486 /**
487  * g_network_monitor_base_add_network:
488  * @monitor: the #GNetworkMonitorBase
489  * @network: (transfer none): a #GInetAddressMask
490  *
491  * Adds @network to @monitor's list of available networks.
492  *
493  * Since: 2.32
494  */
495 void
g_network_monitor_base_add_network(GNetworkMonitorBase * monitor,GInetAddressMask * network)496 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
497                                     GInetAddressMask    *network)
498 {
499   if (!g_hash_table_add (monitor->priv->networks, g_object_ref (network)))
500     return;
501 
502   if (g_inet_address_mask_get_length (network) == 0)
503     {
504       switch (g_inet_address_mask_get_family (network))
505         {
506         case G_SOCKET_FAMILY_IPV4:
507           monitor->priv->have_ipv4_default_route = TRUE;
508           break;
509         case G_SOCKET_FAMILY_IPV6:
510           monitor->priv->have_ipv6_default_route = TRUE;
511           break;
512         default:
513           break;
514         }
515     }
516 
517   /* Don't emit network-changed when multicast-link-local routing
518    * changes. This rather arbitrary decision is mostly because it
519    * seems to change quite often...
520    */
521   if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
522     return;
523 
524   queue_network_changed (monitor);
525 }
526 
527 /**
528  * g_network_monitor_base_remove_network:
529  * @monitor: the #GNetworkMonitorBase
530  * @network: a #GInetAddressMask
531  *
532  * Removes @network from @monitor's list of available networks.
533  *
534  * Since: 2.32
535  */
536 void
g_network_monitor_base_remove_network(GNetworkMonitorBase * monitor,GInetAddressMask * network)537 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
538                                        GInetAddressMask    *network)
539 {
540   if (!g_hash_table_remove (monitor->priv->networks, network))
541     return;
542 
543   if (g_inet_address_mask_get_length (network) == 0)
544     {
545       switch (g_inet_address_mask_get_family (network))
546         {
547         case G_SOCKET_FAMILY_IPV4:
548           monitor->priv->have_ipv4_default_route = FALSE;
549           break;
550         case G_SOCKET_FAMILY_IPV6:
551           monitor->priv->have_ipv6_default_route = FALSE;
552           break;
553         default:
554           break;
555         }
556     }
557 
558   queue_network_changed (monitor);
559 }
560 
561 /**
562  * g_network_monitor_base_set_networks:
563  * @monitor: the #GNetworkMonitorBase
564  * @networks: (array length=length): an array of #GInetAddressMask
565  * @length: length of @networks
566  *
567  * Drops @monitor's current list of available networks and replaces
568  * it with @networks.
569  */
570 void
g_network_monitor_base_set_networks(GNetworkMonitorBase * monitor,GInetAddressMask ** networks,gint length)571 g_network_monitor_base_set_networks (GNetworkMonitorBase  *monitor,
572                                      GInetAddressMask    **networks,
573                                      gint                  length)
574 {
575   int i;
576 
577   g_hash_table_remove_all (monitor->priv->networks);
578   monitor->priv->have_ipv4_default_route = FALSE;
579   monitor->priv->have_ipv6_default_route = FALSE;
580 
581   for (i = 0; i < length; i++)
582     g_network_monitor_base_add_network (monitor, networks[i]);
583 }
584