1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2011 Red Hat, Inc.
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 of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
23 #include "gnetworkmonitorbase.h"
24 #include "ginetaddress.h"
25 #include "ginetaddressmask.h"
26 #include "ginetsocketaddress.h"
27 #include "ginitable.h"
29 #include "giomodule-priv.h"
30 #include "gnetworkmonitor.h"
31 #include "gsocketaddressenumerator.h"
32 #include "gsocketconnectable.h"
35 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface
*iface
);
36 static void g_network_monitor_base_initable_iface_init (GInitableIface
*iface
);
38 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase
, g_network_monitor_base
, G_TYPE_OBJECT
,
39 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE
,
40 g_network_monitor_base_initable_iface_init
)
41 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR
,
42 g_network_monitor_base_iface_init
)
43 _g_io_modules_ensure_extension_points_registered ();
44 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME
,
53 PROP_NETWORK_AVAILABLE
56 struct _GNetworkMonitorBasePrivate
59 gboolean have_ipv4_default_route
;
60 gboolean have_ipv6_default_route
;
61 gboolean is_available
;
63 GMainContext
*context
;
64 GSource
*network_changed_source
;
65 gboolean initializing
;
68 static guint network_changed_signal
= 0;
70 static void queue_network_changed (GNetworkMonitorBase
*monitor
);
73 g_network_monitor_base_init (GNetworkMonitorBase
*monitor
)
75 monitor
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (monitor
,
76 G_TYPE_NETWORK_MONITOR_BASE
,
77 GNetworkMonitorBasePrivate
);
79 monitor
->priv
->networks
= g_ptr_array_new_with_free_func (g_object_unref
);
80 monitor
->priv
->context
= g_main_context_get_thread_default ();
81 if (monitor
->priv
->context
)
82 g_main_context_ref (monitor
->priv
->context
);
84 monitor
->priv
->initializing
= TRUE
;
85 queue_network_changed (monitor
);
89 g_network_monitor_base_constructed (GObject
*object
)
91 GNetworkMonitorBase
*monitor
= G_NETWORK_MONITOR_BASE (object
);
93 if (G_OBJECT_TYPE (monitor
) == G_TYPE_NETWORK_MONITOR_BASE
)
95 GInetAddressMask
*mask
;
97 /* We're the dumb base class, not a smarter subclass. So just
98 * assume that the network is available.
100 mask
= g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL
);
101 g_network_monitor_base_add_network (monitor
, mask
);
102 g_object_unref (mask
);
104 mask
= g_inet_address_mask_new_from_string ("::/0", NULL
);
105 g_network_monitor_base_add_network (monitor
, mask
);
106 g_object_unref (mask
);
111 g_network_monitor_base_get_property (GObject
*object
,
116 GNetworkMonitorBase
*monitor
= G_NETWORK_MONITOR_BASE (object
);
120 case PROP_NETWORK_AVAILABLE
:
121 g_value_set_boolean (value
, monitor
->priv
->is_available
);
125 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
131 g_network_monitor_base_finalize (GObject
*object
)
133 GNetworkMonitorBase
*monitor
= G_NETWORK_MONITOR_BASE (object
);
135 g_ptr_array_free (monitor
->priv
->networks
, TRUE
);
136 if (monitor
->priv
->network_changed_source
)
138 g_source_destroy (monitor
->priv
->network_changed_source
);
139 g_source_unref (monitor
->priv
->network_changed_source
);
141 if (monitor
->priv
->context
)
142 g_main_context_unref (monitor
->priv
->context
);
144 G_OBJECT_CLASS (g_network_monitor_base_parent_class
)->finalize (object
);
148 g_network_monitor_base_class_init (GNetworkMonitorBaseClass
*monitor_class
)
150 GObjectClass
*gobject_class
= G_OBJECT_CLASS (monitor_class
);
152 g_type_class_add_private (monitor_class
, sizeof (GNetworkMonitorBasePrivate
));
154 gobject_class
->constructed
= g_network_monitor_base_constructed
;
155 gobject_class
->get_property
= g_network_monitor_base_get_property
;
156 gobject_class
->finalize
= g_network_monitor_base_finalize
;
158 g_object_class_override_property (gobject_class
, PROP_NETWORK_AVAILABLE
, "network-available");
162 g_network_monitor_base_can_reach (GNetworkMonitor
*monitor
,
163 GSocketConnectable
*connectable
,
164 GCancellable
*cancellable
,
167 GNetworkMonitorBasePrivate
*priv
= G_NETWORK_MONITOR_BASE (monitor
)->priv
;
168 GSocketAddressEnumerator
*enumerator
;
169 GSocketAddress
*addr
;
171 if (priv
->have_ipv4_default_route
&&
172 priv
->have_ipv6_default_route
)
175 if (priv
->networks
->len
== 0)
177 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_NETWORK_UNREACHABLE
,
178 _("Network unreachable"));
182 enumerator
= g_socket_connectable_proxy_enumerate (connectable
);
183 addr
= g_socket_address_enumerator_next (enumerator
, cancellable
, error
);
186 /* Either the user cancelled, or DNS resolution failed */
187 g_object_unref (enumerator
);
193 if (G_IS_INET_SOCKET_ADDRESS (addr
))
198 iaddr
= g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr
));
199 for (i
= 0; i
< priv
->networks
->len
; i
++)
201 if (g_inet_address_mask_matches (priv
->networks
->pdata
[i
], iaddr
))
203 g_object_unref (addr
);
204 g_object_unref (enumerator
);
210 g_object_unref (addr
);
211 addr
= g_socket_address_enumerator_next (enumerator
, cancellable
, error
);
213 g_object_unref (enumerator
);
215 if (error
&& !*error
)
217 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_HOST_UNREACHABLE
,
218 _("Host unreachable"));
224 g_network_monitor_base_iface_init (GNetworkMonitorInterface
*monitor_iface
)
226 monitor_iface
->can_reach
= g_network_monitor_base_can_reach
;
228 network_changed_signal
= g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR
);
232 g_network_monitor_base_initable_init (GInitable
*initable
,
233 GCancellable
*cancellable
,
240 g_network_monitor_base_initable_iface_init (GInitableIface
*iface
)
242 iface
->init
= g_network_monitor_base_initable_init
;
246 emit_network_changed (gpointer user_data
)
248 GNetworkMonitorBase
*monitor
= user_data
;
249 gboolean is_available
;
251 g_object_ref (monitor
);
253 if (monitor
->priv
->initializing
)
254 monitor
->priv
->initializing
= FALSE
;
257 is_available
= (monitor
->priv
->have_ipv4_default_route
||
258 monitor
->priv
->have_ipv6_default_route
);
259 if (monitor
->priv
->is_available
!= is_available
)
261 monitor
->priv
->is_available
= is_available
;
262 g_object_notify (G_OBJECT (monitor
), "network-available");
265 g_signal_emit (monitor
, network_changed_signal
, 0, is_available
);
268 g_source_unref (monitor
->priv
->network_changed_source
);
269 monitor
->priv
->network_changed_source
= NULL
;
271 g_object_unref (monitor
);
276 queue_network_changed (GNetworkMonitorBase
*monitor
)
278 if (!monitor
->priv
->network_changed_source
)
282 source
= g_idle_source_new ();
283 /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
284 * network-change-related notifications coming in at
285 * G_PRIORITY_DEFAULT will get coalesced into one signal
288 g_source_set_priority (source
, G_PRIORITY_HIGH_IDLE
);
289 g_source_set_callback (source
, emit_network_changed
, monitor
, NULL
);
290 g_source_attach (source
, monitor
->priv
->context
);
291 monitor
->priv
->network_changed_source
= source
;
294 /* Normally we wait to update is_available until we emit the signal,
295 * to keep things consistent. But when we're first creating the
296 * object, we want it to be correct right away.
298 if (monitor
->priv
->initializing
)
300 monitor
->priv
->is_available
= (monitor
->priv
->have_ipv4_default_route
||
301 monitor
->priv
->have_ipv6_default_route
);
306 * g_network_monitor_base_add_network:
307 * @monitor: the #GNetworkMonitorBase
308 * @network: a #GInetAddressMask
310 * Adds @network to @monitor's list of available networks.
313 g_network_monitor_base_add_network (GNetworkMonitorBase
*monitor
,
314 GInetAddressMask
*network
)
318 for (i
= 0; i
< monitor
->priv
->networks
->len
; i
++)
320 if (g_inet_address_mask_equal (monitor
->priv
->networks
->pdata
[i
], network
))
324 g_ptr_array_add (monitor
->priv
->networks
, g_object_ref (network
));
325 if (g_inet_address_mask_get_length (network
) == 0)
327 switch (g_inet_address_mask_get_family (network
))
329 case G_SOCKET_FAMILY_IPV4
:
330 monitor
->priv
->have_ipv4_default_route
= TRUE
;
332 case G_SOCKET_FAMILY_IPV6
:
333 monitor
->priv
->have_ipv6_default_route
= TRUE
;
340 /* Don't emit network-changed when multicast-link-local routing
341 * changes. This rather arbitrary decision is mostly because it
342 * seems to change quite often...
344 if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network
)))
347 queue_network_changed (monitor
);
351 * g_network_monitor_base_remove_network:
352 * @monitor: the #GNetworkMonitorBase
353 * @network: a #GInetAddressMask
355 * Removes @network from @monitor's list of available networks.
358 g_network_monitor_base_remove_network (GNetworkMonitorBase
*monitor
,
359 GInetAddressMask
*network
)
363 for (i
= 0; i
< monitor
->priv
->networks
->len
; i
++)
365 if (g_inet_address_mask_equal (monitor
->priv
->networks
->pdata
[i
], network
))
367 g_ptr_array_remove_index_fast (monitor
->priv
->networks
, i
);
369 if (g_inet_address_mask_get_length (network
) == 0)
371 switch (g_inet_address_mask_get_family (network
))
373 case G_SOCKET_FAMILY_IPV4
:
374 monitor
->priv
->have_ipv4_default_route
= FALSE
;
376 case G_SOCKET_FAMILY_IPV6
:
377 monitor
->priv
->have_ipv6_default_route
= FALSE
;
384 queue_network_changed (monitor
);
391 * g_network_monitor_base_set_networks:
392 * @monitor: the #GNetworkMonitorBase
393 * @networks: (array length=length): an array of #GInetAddressMask
394 * @length: length of @networks
396 * Drops @monitor's current list of available networks and replaces
400 g_network_monitor_base_set_networks (GNetworkMonitorBase
*monitor
,
401 GInetAddressMask
**networks
,
406 g_ptr_array_set_size (monitor
->priv
->networks
, 0);
407 monitor
->priv
->have_ipv4_default_route
= FALSE
;
408 monitor
->priv
->have_ipv6_default_route
= FALSE
;
410 for (i
= 0; i
< length
; i
++)
411 g_network_monitor_base_add_network (monitor
, networks
[i
]);