Coverage Report

Created: 2025-07-23 08:13

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