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 |
78 | g_network_monitor_base_init (GNetworkMonitorBase *monitor) |
79 | { |
80 | monitor->priv = g_network_monitor_base_get_instance_private (self: monitor); |
81 | monitor->priv->networks = g_hash_table_new_full (hash_func: inet_address_mask_hash, |
82 | key_equal_func: inet_address_mask_equal, |
83 | key_destroy_func: g_object_unref, NULL); |
84 | monitor->priv->context = g_main_context_get_thread_default (); |
85 | if (monitor->priv->context) |
86 | g_main_context_ref (context: monitor->priv->context); |
87 | |
88 | monitor->priv->initializing = TRUE; |
89 | } |
90 | |
91 | static void |
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 (mask_string: "0.0.0.0/0" , NULL); |
104 | g_network_monitor_base_add_network (monitor, network: mask); |
105 | g_object_unref (object: mask); |
106 | |
107 | mask = g_inet_address_mask_new_from_string (mask_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, network: mask); |
114 | g_object_unref (object: mask); |
115 | } |
116 | } |
117 | } |
118 | |
119 | static void |
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, v_boolean: 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 | v_enum: 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 |
153 | g_network_monitor_base_finalize (GObject *object) |
154 | { |
155 | GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object); |
156 | |
157 | g_hash_table_unref (hash_table: monitor->priv->networks); |
158 | if (monitor->priv->network_changed_source) |
159 | { |
160 | g_source_destroy (source: monitor->priv->network_changed_source); |
161 | g_source_unref (source: monitor->priv->network_changed_source); |
162 | } |
163 | if (monitor->priv->context) |
164 | g_main_context_unref (context: monitor->priv->context); |
165 | |
166 | G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object); |
167 | } |
168 | |
169 | static void |
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 (oclass: gobject_class, property_id: PROP_NETWORK_AVAILABLE, name: "network-available" ); |
179 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_NETWORK_METERED, name: "network-metered" ); |
180 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_CONNECTIVITY, name: "connectivity" ); |
181 | } |
182 | |
183 | static gboolean |
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: &iter, hash_table: base->priv->networks); |
196 | while (g_hash_table_iter_next (iter: &iter, key: &key, NULL)) |
197 | { |
198 | GInetAddressMask *mask = key; |
199 | if (g_inet_address_mask_matches (mask, address: iaddr)) |
200 | return TRUE; |
201 | } |
202 | |
203 | return FALSE; |
204 | } |
205 | |
206 | static gboolean |
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 (hash_table: base->priv->networks) == 0) |
217 | { |
218 | g_set_error_literal (err: error, G_IO_ERROR, code: 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 (object: 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 (object: enumerator); |
236 | g_object_unref (object: addr); |
237 | return TRUE; |
238 | } |
239 | |
240 | while (addr) |
241 | { |
242 | if (g_network_monitor_base_can_reach_sockaddr (base, sockaddr: addr)) |
243 | { |
244 | g_object_unref (object: addr); |
245 | g_object_unref (object: enumerator); |
246 | return TRUE; |
247 | } |
248 | |
249 | g_object_unref (object: addr); |
250 | addr = g_socket_address_enumerator_next (enumerator, cancellable, error); |
251 | } |
252 | g_object_unref (object: enumerator); |
253 | |
254 | if (error && !*error) |
255 | { |
256 | g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_HOST_UNREACHABLE, |
257 | _("Host unreachable" )); |
258 | } |
259 | return FALSE; |
260 | } |
261 | |
262 | static void |
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: &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 (object: task); |
281 | return; |
282 | } |
283 | else |
284 | { |
285 | /* Resolved all addresses, none matched */ |
286 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_HOST_UNREACHABLE, |
287 | _("Host unreachable" )); |
288 | g_object_unref (object: task); |
289 | return; |
290 | } |
291 | } |
292 | |
293 | if (g_network_monitor_base_can_reach_sockaddr (base, sockaddr: addr)) |
294 | { |
295 | g_object_unref (object: addr); |
296 | g_task_return_boolean (task, TRUE); |
297 | g_object_unref (object: task); |
298 | return; |
299 | } |
300 | g_object_unref (object: addr); |
301 | |
302 | g_socket_address_enumerator_next_async (enumerator, |
303 | cancellable: g_task_get_cancellable (task), |
304 | callback: can_reach_async_got_address, user_data: task); |
305 | } |
306 | |
307 | static void |
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 (source_object: monitor, cancellable, callback, callback_data: 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, code: G_IO_ERROR_NETWORK_UNREACHABLE, |
323 | _("Network unreachable" )); |
324 | g_object_unref (object: task); |
325 | return; |
326 | } |
327 | |
328 | enumerator = g_socket_connectable_proxy_enumerate (connectable); |
329 | g_socket_address_enumerator_next_async (enumerator, cancellable, |
330 | callback: can_reach_async_got_address, user_data: task); |
331 | g_object_unref (object: enumerator); |
332 | } |
333 | |
334 | static gboolean |
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 |
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 (name: "network-changed" , G_TYPE_NETWORK_MONITOR); |
352 | } |
353 | |
354 | static gboolean |
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 |
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 |
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 (address: addr); |
380 | gsize bytes_length = g_inet_address_get_native_size (address: 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 |
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: mask_a, mask2: mask_b); |
424 | } |
425 | |
426 | static gboolean |
427 | emit_network_changed (gpointer user_data) |
428 | { |
429 | GNetworkMonitorBase *monitor = user_data; |
430 | gboolean is_available; |
431 | |
432 | if (g_source_is_destroyed (source: 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), property_name: "network-available" ); |
443 | } |
444 | |
445 | g_signal_emit (instance: monitor, signal_id: network_changed_signal, detail: 0, is_available); |
446 | |
447 | g_source_unref (source: monitor->priv->network_changed_source); |
448 | monitor->priv->network_changed_source = NULL; |
449 | |
450 | g_object_unref (object: monitor); |
451 | return FALSE; |
452 | } |
453 | |
454 | static void |
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, func: emit_network_changed, data: monitor, NULL); |
470 | g_source_set_name (source, name: "[gio] emit_network_changed" ); |
471 | g_source_attach (source, context: 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 |
496 | g_network_monitor_base_add_network (GNetworkMonitorBase *monitor, |
497 | GInetAddressMask *network) |
498 | { |
499 | if (!g_hash_table_add (hash_table: monitor->priv->networks, g_object_ref (network))) |
500 | return; |
501 | |
502 | if (g_inet_address_mask_get_length (mask: network) == 0) |
503 | { |
504 | switch (g_inet_address_mask_get_family (mask: 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 (address: g_inet_address_mask_get_address (mask: 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 |
537 | g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor, |
538 | GInetAddressMask *network) |
539 | { |
540 | if (!g_hash_table_remove (hash_table: monitor->priv->networks, key: network)) |
541 | return; |
542 | |
543 | if (g_inet_address_mask_get_length (mask: network) == 0) |
544 | { |
545 | switch (g_inet_address_mask_get_family (mask: 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 |
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 (hash_table: 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, network: networks[i]); |
583 | } |
584 | |