Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/ginetaddressmask.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 <stdlib.h>
24
#include <string.h>
25
26
#include "ginetaddressmask.h"
27
#include "ginetaddress.h"
28
#include "ginitable.h"
29
#include "gioerror.h"
30
#include "gioenumtypes.h"
31
#include "glibintl.h"
32
33
/**
34
 * GInetAddressMask:
35
 *
36
 * `GInetAddressMask` represents a range of IPv4 or IPv6 addresses
37
 * described by a base address and a length indicating how many bits
38
 * of the base address are relevant for matching purposes. These are
39
 * often given in string form. For example, `10.0.0.0/8`, or `fe80::/10`.
40
 *
41
 * Since: 2.32
42
 */
43
44
struct _GInetAddressMaskPrivate
45
{
46
  GInetAddress *addr;
47
  guint         length;
48
};
49
50
static void     g_inet_address_mask_initable_iface_init (GInitableIface  *iface);
51
52
G_DEFINE_TYPE_WITH_CODE (GInetAddressMask, g_inet_address_mask, G_TYPE_OBJECT,
53
                         G_ADD_PRIVATE (GInetAddressMask)
54
       G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
55
            g_inet_address_mask_initable_iface_init))
56
57
enum
58
{
59
  PROP_0,
60
  PROP_FAMILY,
61
  PROP_ADDRESS,
62
  PROP_LENGTH
63
};
64
65
static void
66
g_inet_address_mask_set_property (GObject      *object,
67
          guint         prop_id,
68
          const GValue *value,
69
          GParamSpec   *pspec)
70
0
{
71
0
  GInetAddressMask *mask = G_INET_ADDRESS_MASK (object);
72
73
0
  switch (prop_id)
74
0
    {
75
0
    case PROP_ADDRESS:
76
0
      if (mask->priv->addr)
77
0
  g_object_unref (mask->priv->addr);
78
0
      mask->priv->addr = g_value_dup_object (value);
79
0
      break;
80
81
0
    case PROP_LENGTH:
82
0
      mask->priv->length = g_value_get_uint (value);
83
0
      break;
84
85
0
    default:
86
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
87
0
      break;
88
0
    }
89
90
0
}
91
92
static void
93
g_inet_address_mask_get_property (GObject    *object,
94
          guint       prop_id,
95
          GValue     *value,
96
          GParamSpec *pspec)
97
0
{
98
0
  GInetAddressMask *mask = G_INET_ADDRESS_MASK (object);
99
100
0
  switch (prop_id)
101
0
    {
102
0
    case PROP_FAMILY:
103
0
      g_value_set_enum (value, g_inet_address_get_family (mask->priv->addr));
104
0
      break;
105
106
0
    case PROP_ADDRESS:
107
0
      g_value_set_object (value, mask->priv->addr);
108
0
      break;
109
110
0
    case PROP_LENGTH:
111
0
      g_value_set_uint (value, mask->priv->length);
112
0
      break;
113
114
0
    default:
115
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116
0
    }
117
0
}
118
119
static void
120
g_inet_address_mask_dispose (GObject *object)
121
0
{
122
0
  GInetAddressMask *mask = G_INET_ADDRESS_MASK (object);
123
124
0
  g_clear_object (&mask->priv->addr);
125
126
0
  G_OBJECT_CLASS (g_inet_address_mask_parent_class)->dispose (object);
127
0
}
128
129
static void
130
g_inet_address_mask_class_init (GInetAddressMaskClass *klass)
131
0
{
132
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
133
134
0
  gobject_class->set_property = g_inet_address_mask_set_property;
135
0
  gobject_class->get_property = g_inet_address_mask_get_property;
136
0
  gobject_class->dispose = g_inet_address_mask_dispose;
137
138
  /**
139
   * GInetAddressMask:family:
140
   *
141
   * The address family (IPv4 or IPv6).
142
   *
143
   * Since: 2.32
144
   */
145
0
  g_object_class_install_property (gobject_class, PROP_FAMILY,
146
0
                                   g_param_spec_enum ("family", NULL, NULL,
147
0
                  G_TYPE_SOCKET_FAMILY,
148
0
                  G_SOCKET_FAMILY_INVALID,
149
0
                  G_PARAM_READABLE |
150
0
                                                      G_PARAM_STATIC_STRINGS));
151
152
  /**
153
   * GInetAddressMask:address:
154
   *
155
   * The base address.
156
   *
157
   * Since: 2.32
158
   */
159
0
  g_object_class_install_property (gobject_class, PROP_ADDRESS,
160
0
                                   g_param_spec_object ("address", NULL, NULL,
161
0
              G_TYPE_INET_ADDRESS,
162
0
              G_PARAM_READWRITE |
163
0
              G_PARAM_STATIC_STRINGS));
164
165
  /**
166
   * GInetAddressMask:length:
167
   *
168
   * The prefix length, in bytes.
169
   *
170
   * Since: 2.32
171
   */
172
0
  g_object_class_install_property (gobject_class, PROP_LENGTH,
173
0
                                   g_param_spec_uint ("length", NULL, NULL,
174
0
                  0, 128, 0,
175
0
                  G_PARAM_READWRITE |
176
0
                  G_PARAM_STATIC_STRINGS));
177
0
}
178
179
static gboolean
180
g_inet_address_mask_initable_init (GInitable     *initable,
181
           GCancellable  *cancellable,
182
           GError       **error)
183
0
{
184
0
  GInetAddressMask *mask = G_INET_ADDRESS_MASK (initable);
185
0
  guint addrlen, nbytes, nbits;
186
0
  const guint8 *bytes;
187
0
  gboolean ok;
188
189
0
  if (!mask->priv->addr)
190
0
    {
191
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
192
0
         _("No address specified"));
193
0
      return FALSE;
194
0
    }
195
196
0
  addrlen = g_inet_address_get_native_size (mask->priv->addr);
197
0
  if (mask->priv->length > addrlen * 8)
198
0
    {
199
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
200
0
       _("Length %u is too long for address"),
201
0
       mask->priv->length);
202
0
      return FALSE;
203
0
    }
204
205
  /* Make sure all the bits after @length are 0 */
206
0
  bytes = g_inet_address_to_bytes (mask->priv->addr);
207
0
  ok = TRUE;
208
209
0
  nbytes = mask->priv->length / 8;
210
0
  bytes += nbytes;
211
0
  addrlen -= nbytes;
212
213
0
  nbits = mask->priv->length % 8;
214
0
  if (nbits)
215
0
    {
216
0
      if (bytes[0] & (0xFF >> nbits))
217
0
  ok = FALSE;
218
0
      bytes++;
219
0
      addrlen--;
220
0
    }
221
222
0
  while (addrlen)
223
0
    {
224
0
      if (bytes[0])
225
0
  ok = FALSE;
226
0
      bytes++;
227
0
      addrlen--;
228
0
    }
229
230
0
  if (!ok)
231
0
    {
232
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
233
0
         _("Address has bits set beyond prefix length"));
234
0
      return FALSE;
235
0
    }
236
237
0
  return TRUE;
238
0
}
239
240
static void
241
g_inet_address_mask_initable_iface_init (GInitableIface  *iface)
242
0
{
243
0
  iface->init = g_inet_address_mask_initable_init;
244
0
}
245
246
static void
247
g_inet_address_mask_init (GInetAddressMask *mask)
248
0
{
249
0
  mask->priv = g_inet_address_mask_get_instance_private (mask);
250
0
}
251
252
/**
253
 * g_inet_address_mask_new:
254
 * @addr: a #GInetAddress
255
 * @length: number of bits of @addr to use
256
 * @error: return location for #GError, or %NULL
257
 *
258
 * Creates a new #GInetAddressMask representing all addresses whose
259
 * first @length bits match @addr.
260
 *
261
 * Returns: a new #GInetAddressMask, or %NULL on error
262
 *
263
 * Since: 2.32
264
 */
265
GInetAddressMask *
266
g_inet_address_mask_new (GInetAddress  *addr,
267
       guint          length,
268
       GError       **error)
269
0
{
270
0
  return g_initable_new (G_TYPE_INET_ADDRESS_MASK, NULL, error,
271
0
       "address", addr,
272
0
       "length", length,
273
0
       NULL);
274
0
}
275
276
/**
277
 * g_inet_address_mask_new_from_string:
278
 * @mask_string: an IP address or address/length string
279
 * @error: return location for #GError, or %NULL
280
 *
281
 * Parses @mask_string as an IP address and (optional) length, and
282
 * creates a new #GInetAddressMask. The length, if present, is
283
 * delimited by a "/". If it is not present, then the length is
284
 * assumed to be the full length of the address.
285
 *
286
 * Returns: a new #GInetAddressMask corresponding to @string, or %NULL
287
 * on error.
288
 *
289
 * Since: 2.32
290
 */
291
GInetAddressMask *
292
g_inet_address_mask_new_from_string (const gchar  *mask_string,
293
             GError      **error)
294
0
{
295
0
  GInetAddressMask *mask;
296
0
  GInetAddress *addr;
297
0
  gchar *slash;
298
0
  guint length;
299
300
0
  slash = strchr (mask_string, '/');
301
0
  if (slash)
302
0
    {
303
0
      gchar *address, *end;
304
305
0
      length = strtoul (slash + 1, &end, 10);
306
0
      if (*end || !*(slash + 1))
307
0
  {
308
0
  parse_error:
309
0
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
310
0
           _("Could not parse ā€œ%sā€ as IP address mask"),
311
0
           mask_string);
312
0
    return NULL;
313
0
  }
314
315
0
      address = g_strndup (mask_string, slash - mask_string);
316
0
      addr = g_inet_address_new_from_string (address);
317
0
      g_free (address);
318
319
0
      if (!addr)
320
0
  goto parse_error;
321
0
    }
322
0
  else
323
0
    {
324
0
      addr = g_inet_address_new_from_string (mask_string);
325
0
      if (!addr)
326
0
  goto parse_error;
327
328
0
      length = g_inet_address_get_native_size (addr) * 8;
329
0
    }
330
331
0
  mask = g_inet_address_mask_new (addr, length, error);
332
0
  g_object_unref (addr);
333
334
0
  return mask;
335
0
}
336
337
/**
338
 * g_inet_address_mask_to_string:
339
 * @mask: a #GInetAddressMask
340
 *
341
 * Converts @mask back to its corresponding string form.
342
 *
343
 * Returns: a string corresponding to @mask.
344
 *
345
 * Since: 2.32
346
 */
347
gchar *
348
g_inet_address_mask_to_string (GInetAddressMask *mask)
349
0
{
350
0
  gchar *addr_string, *mask_string;
351
352
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), NULL);
353
354
0
  addr_string = g_inet_address_to_string (mask->priv->addr);
355
356
0
  if (mask->priv->length == (g_inet_address_get_native_size (mask->priv->addr) * 8))
357
0
    return addr_string;
358
359
0
  mask_string = g_strdup_printf ("%s/%u", addr_string, mask->priv->length);
360
0
  g_free (addr_string);
361
362
0
  return mask_string;
363
0
}
364
365
/**
366
 * g_inet_address_mask_get_family:
367
 * @mask: a #GInetAddressMask
368
 *
369
 * Gets the #GSocketFamily of @mask's address
370
 *
371
 * Returns: the #GSocketFamily of @mask's address
372
 *
373
 * Since: 2.32
374
 */
375
GSocketFamily
376
g_inet_address_mask_get_family (GInetAddressMask *mask)
377
0
{
378
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), G_SOCKET_FAMILY_INVALID);
379
380
0
  return g_inet_address_get_family (mask->priv->addr);
381
0
}
382
383
/**
384
 * g_inet_address_mask_get_address:
385
 * @mask: a #GInetAddressMask
386
 *
387
 * Gets @mask's base address
388
 *
389
 * Returns: (transfer none): @mask's base address
390
 *
391
 * Since: 2.32
392
 */
393
GInetAddress *
394
g_inet_address_mask_get_address (GInetAddressMask *mask)
395
0
{
396
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), NULL);
397
398
0
  return mask->priv->addr;
399
0
}
400
401
/**
402
 * g_inet_address_mask_get_length:
403
 * @mask: a #GInetAddressMask
404
 *
405
 * Gets @mask's length
406
 *
407
 * Returns: @mask's length
408
 *
409
 * Since: 2.32
410
 */
411
guint
412
g_inet_address_mask_get_length (GInetAddressMask *mask)
413
0
{
414
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), 0);
415
416
0
  return mask->priv->length;
417
0
}
418
419
/**
420
 * g_inet_address_mask_matches:
421
 * @mask: a #GInetAddressMask
422
 * @address: a #GInetAddress
423
 *
424
 * Tests if @address falls within the range described by @mask.
425
 *
426
 * Returns: whether @address falls within the range described by
427
 * @mask.
428
 *
429
 * Since: 2.32
430
 */
431
gboolean
432
g_inet_address_mask_matches (GInetAddressMask *mask,
433
           GInetAddress     *address)
434
0
{
435
0
  const guint8 *maskbytes, *addrbytes;
436
0
  int nbytes, nbits;
437
438
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), FALSE);
439
0
  g_return_val_if_fail (G_IS_INET_ADDRESS (address), FALSE);
440
441
0
  if (g_inet_address_get_family (mask->priv->addr) !=
442
0
      g_inet_address_get_family (address))
443
0
    return FALSE;
444
445
0
  if (mask->priv->length == 0)
446
0
    return TRUE;
447
448
0
  maskbytes = g_inet_address_to_bytes (mask->priv->addr);
449
0
  addrbytes = g_inet_address_to_bytes (address);
450
451
0
  nbytes = mask->priv->length / 8;
452
0
  if (nbytes != 0 && memcmp (maskbytes, addrbytes, nbytes) != 0)
453
0
    return FALSE;
454
455
0
  nbits = mask->priv->length % 8;
456
0
  if (nbits == 0)
457
0
    return TRUE;
458
459
0
  return maskbytes[nbytes] == (addrbytes[nbytes] & (0xFF << (8 - nbits)));
460
0
}
461
462
463
/**
464
 * g_inet_address_mask_equal:
465
 * @mask: a #GInetAddressMask
466
 * @mask2: another #GInetAddressMask
467
 *
468
 * Tests if @mask and @mask2 are the same mask.
469
 *
470
 * Returns: whether @mask and @mask2 are the same mask
471
 *
472
 * Since: 2.32
473
 */
474
gboolean
475
g_inet_address_mask_equal (GInetAddressMask  *mask,
476
         GInetAddressMask  *mask2)
477
0
{
478
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), FALSE);
479
0
  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask2), FALSE);
480
481
0
  return ((mask->priv->length == mask2->priv->length) &&
482
0
    g_inet_address_equal (mask->priv->addr, mask2->priv->addr));
483
0
}