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