f363e02378c38c0e396f9a38cfa71342a67116d7
[platform/upstream/glib.git] / gio / gnetworkmonitorbase.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gnetworkmonitorbase.h"
24 #include "ginetaddress.h"
25 #include "ginetaddressmask.h"
26 #include "ginetsocketaddress.h"
27 #include "ginitable.h"
28 #include "gioerror.h"
29 #include "giomodule-priv.h"
30 #include "gnetworkmonitor.h"
31 #include "gsocketaddressenumerator.h"
32 #include "gsocketconnectable.h"
33 #include "gtask.h"
34 #include "glibintl.h"
35
36 static void g_network_monitor_base_iface_init (GNetworkMonitorInterface *iface);
37 static void g_network_monitor_base_initable_iface_init (GInitableIface *iface);
38
39 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
40                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
41                                                 g_network_monitor_base_initable_iface_init)
42                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
43                                                 g_network_monitor_base_iface_init)
44                          _g_io_modules_ensure_extension_points_registered ();
45                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
46                                                          g_define_type_id,
47                                                          "base",
48                                                          0))
49
50 enum
51 {
52   PROP_0,
53
54   PROP_NETWORK_AVAILABLE
55 };
56
57 struct _GNetworkMonitorBasePrivate
58 {
59   GPtrArray    *networks;
60   gboolean      have_ipv4_default_route;
61   gboolean      have_ipv6_default_route;
62   gboolean      is_available;
63
64   GMainContext *context;
65   GSource      *network_changed_source;
66   gboolean      initializing;
67 };
68
69 static guint network_changed_signal = 0;
70
71 static void queue_network_changed (GNetworkMonitorBase *monitor);
72
73 static void
74 g_network_monitor_base_init (GNetworkMonitorBase *monitor)
75 {
76   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
77                                                G_TYPE_NETWORK_MONITOR_BASE,
78                                                GNetworkMonitorBasePrivate);
79
80   monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
81   monitor->priv->context = g_main_context_get_thread_default ();
82   if (monitor->priv->context)
83     g_main_context_ref (monitor->priv->context);
84
85   monitor->priv->initializing = TRUE;
86   queue_network_changed (monitor);
87 }
88
89 static void
90 g_network_monitor_base_constructed (GObject *object)
91 {
92   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
93
94   if (G_OBJECT_TYPE (monitor) == G_TYPE_NETWORK_MONITOR_BASE)
95     {
96       GInetAddressMask *mask;
97
98       /* We're the dumb base class, not a smarter subclass. So just
99        * assume that the network is available.
100        */
101       mask = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
102       g_network_monitor_base_add_network (monitor, mask);
103       g_object_unref (mask);
104
105       mask = g_inet_address_mask_new_from_string ("::/0", NULL);
106       g_network_monitor_base_add_network (monitor, mask);
107       g_object_unref (mask);
108     }
109 }
110
111 static void
112 g_network_monitor_base_get_property (GObject    *object,
113                                      guint       prop_id,
114                                      GValue     *value,
115                                      GParamSpec *pspec)
116 {
117   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
118
119   switch (prop_id)
120     {
121       case PROP_NETWORK_AVAILABLE:
122         g_value_set_boolean (value, monitor->priv->is_available);
123         break;
124
125       default:
126         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127     }
128
129 }
130
131 static void
132 g_network_monitor_base_finalize (GObject *object)
133 {
134   GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
135
136   g_ptr_array_free (monitor->priv->networks, TRUE);
137   if (monitor->priv->network_changed_source)
138     {
139       g_source_destroy (monitor->priv->network_changed_source);
140       g_source_unref (monitor->priv->network_changed_source);
141     }
142   if (monitor->priv->context)
143     g_main_context_unref (monitor->priv->context);
144
145   G_OBJECT_CLASS (g_network_monitor_base_parent_class)->finalize (object);
146 }
147
148 static void
149 g_network_monitor_base_class_init (GNetworkMonitorBaseClass *monitor_class)
150 {
151   GObjectClass *gobject_class = G_OBJECT_CLASS (monitor_class);
152
153   g_type_class_add_private (monitor_class, sizeof (GNetworkMonitorBasePrivate));
154
155   gobject_class->constructed  = g_network_monitor_base_constructed;
156   gobject_class->get_property = g_network_monitor_base_get_property;
157   gobject_class->finalize     = g_network_monitor_base_finalize;
158
159   g_object_class_override_property (gobject_class, PROP_NETWORK_AVAILABLE, "network-available");
160 }
161
162 static gboolean
163 g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
164                                            GSocketAddress *sockaddr)
165 {
166   GInetAddress *iaddr;
167   int i;
168
169   if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
170     return FALSE;
171
172   iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
173   for (i = 0; i < base->priv->networks->len; i++)
174     {
175       if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
176         return TRUE;
177     }
178
179   return FALSE;
180 }
181
182 static gboolean
183 g_network_monitor_base_can_reach (GNetworkMonitor      *monitor,
184                                   GSocketConnectable   *connectable,
185                                   GCancellable         *cancellable,
186                                   GError              **error)
187 {
188   GNetworkMonitorBase *base = G_NETWORK_MONITOR_BASE (monitor);
189   GSocketAddressEnumerator *enumerator;
190   GSocketAddress *addr;
191
192   if (base->priv->networks->len == 0)
193     {
194       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
195                            _("Network unreachable"));
196       return FALSE;
197     }
198
199   enumerator = g_socket_connectable_proxy_enumerate (connectable);
200   addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
201   if (!addr)
202     {
203       /* Either the user cancelled, or DNS resolution failed */
204       g_object_unref (enumerator);
205       return FALSE;
206     }
207
208   if (base->priv->have_ipv4_default_route &&
209       base->priv->have_ipv6_default_route)
210     {
211       g_object_unref (enumerator);
212       g_object_unref (addr);
213       return TRUE;
214     }
215
216   while (addr)
217     {
218       if (g_network_monitor_base_can_reach_sockaddr (base, addr))
219         {
220           g_object_unref (addr);
221           g_object_unref (enumerator);
222           return TRUE;
223         }
224
225       g_object_unref (addr);
226       addr = g_socket_address_enumerator_next (enumerator, cancellable, error);
227     }
228   g_object_unref (enumerator);
229
230   if (error && !*error)
231     {
232       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
233                            _("Host unreachable"));
234     }
235   return FALSE;
236 }
237
238 static void
239 can_reach_async_got_address (GObject      *object,
240                              GAsyncResult *result,
241                              gpointer      user_data)
242 {
243   GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (object);
244   GTask *task = user_data;
245   GNetworkMonitorBase *base = g_task_get_source_object (task);
246   GSocketAddress *addr;
247   GError *error = NULL;
248
249   addr = g_socket_address_enumerator_next_finish (enumerator, result, &error);
250   if (!addr)
251     {
252       if (error)
253         {
254           /* Either the user cancelled, or DNS resolution failed */
255           g_task_return_error (task, error);
256           g_object_unref (task);
257           return;
258         }
259       else
260         {
261           /* Resolved all addresses, none matched */
262           g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
263                                    _("Host unreachable"));
264           g_object_unref (task);
265           return;
266         }
267     }
268
269   if (g_network_monitor_base_can_reach_sockaddr (base, addr))
270     {
271       g_object_unref (addr);
272       g_task_return_boolean (task, TRUE);
273       g_object_unref (task);
274       return;
275     }
276   g_object_unref (addr);
277
278   g_socket_address_enumerator_next_async (enumerator,
279                                           g_task_get_cancellable (task),
280                                           can_reach_async_got_address, task);
281 }
282
283 static void
284 g_network_monitor_base_can_reach_async (GNetworkMonitor     *monitor,
285                                         GSocketConnectable  *connectable,
286                                         GCancellable        *cancellable,
287                                         GAsyncReadyCallback  callback,
288                                         gpointer             user_data)
289 {
290   GTask *task;
291   GSocketAddressEnumerator *enumerator;
292
293   task = g_task_new (monitor, cancellable, callback, user_data);
294
295   if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
296     {
297       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
298                                _("Network unreachable"));
299       g_object_unref (task);
300       return;
301     }
302
303   enumerator = g_socket_connectable_proxy_enumerate (connectable);
304   g_socket_address_enumerator_next_async (enumerator, cancellable,
305                                           can_reach_async_got_address, task);
306   g_object_unref (enumerator);
307 }
308
309 static gboolean
310 g_network_monitor_base_can_reach_finish (GNetworkMonitor  *monitor,
311                                          GAsyncResult     *result,
312                                          GError          **error)
313 {
314   g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
315
316   return g_task_propagate_boolean (G_TASK (result), error);
317 }
318
319 static void
320 g_network_monitor_base_iface_init (GNetworkMonitorInterface *monitor_iface)
321 {
322   monitor_iface->can_reach = g_network_monitor_base_can_reach;
323   monitor_iface->can_reach_async = g_network_monitor_base_can_reach_async;
324   monitor_iface->can_reach_finish = g_network_monitor_base_can_reach_finish;
325
326   network_changed_signal = g_signal_lookup ("network-changed", G_TYPE_NETWORK_MONITOR);
327 }
328
329 static gboolean
330 g_network_monitor_base_initable_init (GInitable     *initable,
331                                       GCancellable  *cancellable,
332                                       GError       **error)
333 {
334   return TRUE;
335 }
336
337 static void
338 g_network_monitor_base_initable_iface_init (GInitableIface *iface)
339 {
340   iface->init = g_network_monitor_base_initable_init;
341 }
342
343 static gboolean
344 emit_network_changed (gpointer user_data)
345 {
346   GNetworkMonitorBase *monitor = user_data;
347   gboolean is_available;
348
349   g_object_ref (monitor);
350
351   if (monitor->priv->initializing)
352     monitor->priv->initializing = FALSE;
353   else
354     {
355       is_available = (monitor->priv->have_ipv4_default_route ||
356                       monitor->priv->have_ipv6_default_route);
357       if (monitor->priv->is_available != is_available)
358         {
359           monitor->priv->is_available = is_available;
360           g_object_notify (G_OBJECT (monitor), "network-available");
361         }
362
363       g_signal_emit (monitor, network_changed_signal, 0, is_available);
364     }
365
366   g_source_unref (monitor->priv->network_changed_source);
367   monitor->priv->network_changed_source = NULL;
368
369   g_object_unref (monitor);
370   return FALSE;
371 }
372
373 static void
374 queue_network_changed (GNetworkMonitorBase *monitor)
375 {
376   if (!monitor->priv->network_changed_source)
377     {
378       GSource *source;
379
380       source = g_idle_source_new ();
381       /* Use G_PRIORITY_HIGH_IDLE priority so that multiple
382        * network-change-related notifications coming in at
383        * G_PRIORITY_DEFAULT will get coalesced into one signal
384        * emission.
385        */
386       g_source_set_priority (source, G_PRIORITY_HIGH_IDLE);
387       g_source_set_callback (source, emit_network_changed, monitor, NULL);
388       g_source_attach (source, monitor->priv->context);
389       monitor->priv->network_changed_source = source;
390     }
391
392   /* Normally we wait to update is_available until we emit the signal,
393    * to keep things consistent. But when we're first creating the
394    * object, we want it to be correct right away.
395    */
396   if (monitor->priv->initializing)
397     {
398       monitor->priv->is_available = (monitor->priv->have_ipv4_default_route ||
399                                      monitor->priv->have_ipv6_default_route);
400     }
401 }
402
403 /**
404  * g_network_monitor_base_add_network:
405  * @monitor: the #GNetworkMonitorBase
406  * @network: a #GInetAddressMask
407  *
408  * Adds @network to @monitor's list of available networks.
409  *
410  * Since: 2.32
411  */
412 void
413 g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
414                                     GInetAddressMask    *network)
415 {
416   int i;
417
418   for (i = 0; i < monitor->priv->networks->len; i++)
419     {
420       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
421         return;
422     }
423
424   g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
425   if (g_inet_address_mask_get_length (network) == 0)
426     {
427       switch (g_inet_address_mask_get_family (network))
428         {
429         case G_SOCKET_FAMILY_IPV4:
430           monitor->priv->have_ipv4_default_route = TRUE;
431           break;
432         case G_SOCKET_FAMILY_IPV6:
433           monitor->priv->have_ipv6_default_route = TRUE;
434           break;
435         default:
436           break;
437         }
438     }
439
440   /* Don't emit network-changed when multicast-link-local routing
441    * changes. This rather arbitrary decision is mostly because it
442    * seems to change quite often...
443    */
444   if (g_inet_address_get_is_mc_link_local (g_inet_address_mask_get_address (network)))
445     return;
446
447   queue_network_changed (monitor);
448 }
449
450 /**
451  * g_network_monitor_base_remove_network:
452  * @monitor: the #GNetworkMonitorBase
453  * @network: a #GInetAddressMask
454  *
455  * Removes @network from @monitor's list of available networks.
456  *
457  * Since: 2.32
458  */
459 void
460 g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
461                                        GInetAddressMask    *network)
462 {
463   int i;
464
465   for (i = 0; i < monitor->priv->networks->len; i++)
466     {
467       if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
468         {
469           g_ptr_array_remove_index_fast (monitor->priv->networks, i);
470
471           if (g_inet_address_mask_get_length (network) == 0)
472             {
473               switch (g_inet_address_mask_get_family (network))
474                 {
475                 case G_SOCKET_FAMILY_IPV4:
476                   monitor->priv->have_ipv4_default_route = FALSE;
477                   break;
478                 case G_SOCKET_FAMILY_IPV6:
479                   monitor->priv->have_ipv6_default_route = FALSE;
480                   break;
481                 default:
482                   break;
483                 }
484             }
485
486           queue_network_changed (monitor);
487           return;
488         }
489     }
490 }
491
492 /**
493  * g_network_monitor_base_set_networks:
494  * @monitor: the #GNetworkMonitorBase
495  * @networks: (array length=length): an array of #GInetAddressMask
496  * @length: length of @networks
497  *
498  * Drops @monitor's current list of available networks and replaces
499  * it with @networks.
500  */
501 void
502 g_network_monitor_base_set_networks (GNetworkMonitorBase  *monitor,
503                                      GInetAddressMask    **networks,
504                                      gint                  length)
505 {
506   int i;
507
508   g_ptr_array_set_size (monitor->priv->networks, 0);
509   monitor->priv->have_ipv4_default_route = FALSE;
510   monitor->priv->have_ipv6_default_route = FALSE;
511
512   for (i = 0; i < length; i++)
513     g_network_monitor_base_add_network (monitor, networks[i]);
514 }