[glib] GInetAddressMask: new type for internet address range matching



commit eb9755dc9c765cd0381f8b6d897e6ff4f7582a0a
Author: Dan Winship <danw gnome org>
Date:   Sat Oct 1 08:31:54 2011 -0400

    GInetAddressMask: new type for internet address range matching
    
    Eg, for matching a GInetAddress to a range like "10.0.0.0/8" or
    "fe80::/10"
    
    https://bugzilla.gnome.org/show_bug.cgi?id=620932

 docs/reference/gio/gio-docs.xml     |    1 +
 docs/reference/gio/gio-sections.txt |   25 ++
 gio/Makefile.am                     |    2 +
 gio/ginetaddressmask.c              |  463 +++++++++++++++++++++++++++++++++++
 gio/ginetaddressmask.h              |   78 ++++++
 gio/gio.h                           |    1 +
 gio/gio.symbols                     |    9 +
 gio/giotypes.h                      |    1 +
 8 files changed, 580 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index 496602d..fd408aa 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -107,6 +107,7 @@
       <title>Lowlevel network support</title>
       <xi:include href="xml/gsocket.xml"/>
       <xi:include href="xml/ginetaddress.xml"/>
+      <xi:include href="xml/ginetaddressmask.xml"/>
       <xi:include href="xml/gsocketaddress.xml"/>
       <xi:include href="xml/ginetsocketaddress.xml"/>
       <xi:include href="xml/gunixsocketaddress.xml"/>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 49ac6b1..33287c0 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1538,6 +1538,31 @@ g_inet_address_get_type
 </SECTION>
 
 <SECTION>
+<FILE>ginetaddressmask</FILE>
+<TITLE>GInetAddressMask</TITLE>
+GInetAddressMask
+g_inet_address_mask_new
+g_inet_address_mask_new_from_string
+g_inet_address_mask_to_string
+g_inet_address_mask_get_family
+g_inet_address_mask_get_address
+g_inet_address_mask_get_length
+g_inet_address_mask_matches
+g_inet_address_mask_equal
+<SUBSECTION Standard>
+GInetAddressMaskClass
+GInetAddressMaskPrivate
+G_INET_ADDRESS_MASK
+G_INET_ADDRESS_MASK_CLASS
+G_INET_ADDRESS_MASK_GET_CLASS
+G_IS_INET_ADDRESS_MASK
+G_IS_INET_ADDRESS_MASK_CLASS
+G_TYPE_INET_ADDRESS_MASK
+<SUBSECTION Private>
+g_inet_address_mask_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gsocketaddress</FILE>
 <TITLE>GSocketAddress</TITLE>
 GSocketAddress
diff --git a/gio/Makefile.am b/gio/Makefile.am
index b715e3c..249cc7b 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -313,6 +313,7 @@ libgio_2_0_la_SOURCES =		\
 	gfilteroutputstream.c 	\
 	gicon.c 		\
 	ginetaddress.c		\
+	ginetaddressmask.c	\
 	ginetsocketaddress.c	\
 	ginitable.c		\
 	ginputstream.c 		\
@@ -479,6 +480,7 @@ gio_headers =			\
 	gfilteroutputstream.h 	\
 	gicon.h 		\
 	ginetaddress.h		\
+	ginetaddressmask.h	\
 	ginetsocketaddress.h	\
 	ginputstream.h 		\
 	ginitable.h		\
diff --git a/gio/ginetaddressmask.c b/gio/ginetaddressmask.c
new file mode 100644
index 0000000..5794a56
--- /dev/null
+++ b/gio/ginetaddressmask.c
@@ -0,0 +1,463 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ginetaddressmask.h"
+#include "ginetaddress.h"
+#include "ginitable.h"
+#include "gioerror.h"
+#include "gioenumtypes.h"
+#include "glibintl.h"
+
+/**
+ * SECTION:ginetaddressmask
+ * @short_description: An IPv4/IPv6 address mask
+ *
+ * #GInetAddressMask represents a range of IPv4 or IPv6 addresses
+ * described by a base address and a length indicating how many bits
+ * of the base address are relevant for matching purposes. These are
+ * often given in string form. Eg, "10.0.0.0/8", or "fe80::/10".
+ */
+
+/**
+ * GInetAddressMask:
+ *
+ * A combination of an IPv4 or IPv6 base address and a length,
+ * representing a range of IP addresses.
+ *
+ * Since: 2.32
+ */
+
+static void     g_inet_address_mask_initable_iface_init (GInitableIface  *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GInetAddressMask, g_inet_address_mask, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+						g_inet_address_mask_initable_iface_init));
+
+struct _GInetAddressMaskPrivate
+{
+  GInetAddress *addr;
+  guint         length;
+};
+
+enum
+{
+  PROP_0,
+  PROP_FAMILY,
+  PROP_ADDRESS,
+  PROP_LENGTH
+};
+
+static void
+g_inet_address_mask_set_property (GObject      *object,
+				  guint         prop_id,
+				  const GValue *value,
+				  GParamSpec   *pspec)
+{
+  GInetAddressMask *mask = G_INET_ADDRESS_MASK (object);
+
+  switch (prop_id)
+    {
+    case PROP_ADDRESS:
+      if (mask->priv->addr)
+	g_object_unref (mask->priv->addr);
+      mask->priv->addr = g_value_dup_object (value);
+      break;
+
+    case PROP_LENGTH:
+      mask->priv->length = g_value_get_uint (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_inet_address_mask_get_property (GObject    *object,
+				  guint       prop_id,
+				  GValue     *value,
+				  GParamSpec *pspec)
+{
+  GInetAddressMask *mask = G_INET_ADDRESS_MASK (object);
+
+  switch (prop_id)
+    {
+    case PROP_FAMILY:
+      g_value_set_enum (value, g_inet_address_get_family (mask->priv->addr));
+      break;
+
+    case PROP_ADDRESS:
+      g_value_set_object (value, mask->priv->addr);
+      break;
+
+    case PROP_LENGTH:
+      g_value_set_uint (value, mask->priv->length);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_inet_address_mask_class_init (GInetAddressMaskClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GInetAddressMaskPrivate));
+
+  gobject_class->set_property = g_inet_address_mask_set_property;
+  gobject_class->get_property = g_inet_address_mask_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_FAMILY,
+                                   g_param_spec_enum ("family",
+						      P_("Address family"),
+						      P_("The address family (IPv4 or IPv6)"),
+						      G_TYPE_SOCKET_FAMILY,
+						      G_SOCKET_FAMILY_INVALID,
+						      G_PARAM_READABLE |
+                                                      G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_ADDRESS,
+                                   g_param_spec_object ("address",
+							P_("Address"),
+							P_("The base address"),
+							G_TYPE_INET_ADDRESS,
+							G_PARAM_READWRITE |
+							G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_LENGTH,
+                                   g_param_spec_uint ("length",
+						      P_("Length"),
+						      P_("The prefix length"),
+						      0, 128, 0,
+						      G_PARAM_READWRITE |
+						      G_PARAM_STATIC_STRINGS));
+}
+
+static gboolean
+g_inet_address_mask_initable_init (GInitable     *initable,
+				   GCancellable  *cancellable,
+				   GError       **error)
+{
+  GInetAddressMask *mask = G_INET_ADDRESS_MASK (initable);
+  guint addrlen, nbytes, nbits;
+  const guint8 *bytes;
+  gboolean ok;
+
+  if (!mask->priv->addr)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+			   _("No address specified"));
+      return FALSE;
+    }
+
+  addrlen = g_inet_address_get_native_size (mask->priv->addr);
+  if (mask->priv->length > addrlen * 8)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+		   _("Length %u is too long for address"),
+		   mask->priv->length);
+      return FALSE;
+    }
+
+  /* Make sure all the bits after @length are 0 */
+  bytes = g_inet_address_to_bytes (mask->priv->addr);
+  ok = TRUE;
+
+  nbytes = mask->priv->length / 8;
+  bytes += nbytes;
+  addrlen -= nbytes;
+
+  nbits = mask->priv->length % 8;
+  if (nbits)
+    {
+      if (bytes[0] & (0xFF >> nbits))
+	ok = FALSE;
+      bytes++;
+      addrlen--;
+    }
+
+  while (addrlen)
+    {
+      if (bytes[0])
+	ok = FALSE;
+      bytes++;
+      addrlen--;
+    }
+
+  if (!ok)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+			   _("Address has bits set beyond prefix length"));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+g_inet_address_mask_initable_iface_init (GInitableIface  *iface)
+{
+  iface->init = g_inet_address_mask_initable_init;
+}
+
+static void
+g_inet_address_mask_init (GInetAddressMask *mask)
+{
+  mask->priv = G_TYPE_INSTANCE_GET_PRIVATE (mask,
+					    G_TYPE_INET_ADDRESS_MASK,
+					    GInetAddressMaskPrivate);
+}
+
+/**
+ * g_inet_address_mask_new:
+ * @addr: a #GInetAddress
+ * @length: number of bits of @addr to use
+ * @error: return location for #GError, or %NULL
+ *
+ * Creates a new #GInetAddressMask representing all addresses whose
+ * first @length bits match @addr.
+ *
+ * Returns: a new #GInetAddressMask, or %NULL on error
+ *
+ * Since: 2.32
+ */
+GInetAddressMask *
+g_inet_address_mask_new (GInetAddress  *addr,
+			 guint          length,
+			 GError       **error)
+{
+  return g_initable_new (G_TYPE_INET_ADDRESS_MASK, NULL, error,
+			 "address", addr,
+			 "length", length,
+			 NULL);
+}
+
+/**
+ * g_inet_address_mask_new_from_string:
+ * @mask_string: an IP address or address/length string
+ * @error: return location for #GError, or %NULL
+ *
+ * Parses @mask_string as an IP address and (optional) length, and
+ * creates a new #GInetAddressMask. The length, if present, is
+ * delimited by a "/". If it is not present, then the length is
+ * assumed to be the full length of the address.
+ *
+ * Returns: a new #GInetAddressMask corresponding to @string, or %NULL
+ * on error.
+ *
+ * Since: 2.32
+ */
+GInetAddressMask *
+g_inet_address_mask_new_from_string (const gchar  *mask_string,
+				     GError      **error)
+{
+  GInetAddressMask *mask;
+  GInetAddress *addr;
+  gchar *slash;
+  guint length;
+
+  slash = strchr (mask_string, '/');
+  if (slash)
+    {
+      gchar *address, *end;
+
+      length = strtoul (slash + 1, &end, 10);
+      if (*end || !*(slash + 1))
+	{
+	parse_error:
+	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+		       _("Could not parse '%s' as IP address mask"),
+		       mask_string);
+	  return NULL;
+	}
+
+      address = g_strndup (mask_string, slash - mask_string);
+      addr = g_inet_address_new_from_string (address);
+      g_free (address);
+
+      if (!addr)
+	goto parse_error;
+    }
+  else
+    {
+      addr = g_inet_address_new_from_string (mask_string);
+      if (!addr)
+	goto parse_error;
+
+      length = g_inet_address_get_native_size (addr) * 8;
+    }
+
+  mask = g_inet_address_mask_new (addr, length, error);
+  g_object_unref (addr);
+
+  return mask;
+}
+
+/**
+ * g_inet_address_mask_to_string:
+ * @mask: a #GInetAddressMask
+ *
+ * Converts @mask back to its corresponding string form.
+ *
+ * Return value: a string corresponding to @mask.
+ *
+ * Since: 2.32
+ */
+gchar *
+g_inet_address_mask_to_string (GInetAddressMask *mask)
+{
+  gchar *addr_string, *mask_string;
+
+  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), NULL);
+
+  addr_string = g_inet_address_to_string (mask->priv->addr);
+
+  if (mask->priv->length == (g_inet_address_get_native_size (mask->priv->addr) * 8))
+    return addr_string;
+
+  mask_string = g_strdup_printf ("%s/%u", addr_string, mask->priv->length);
+  g_free (addr_string);
+
+  return mask_string;
+}
+
+/**
+ * g_inet_address_mask_get_family:
+ * @mask: a #GInetAddressMask
+ *
+ * Gets the #GSocketFamily of @mask's address
+ *
+ * Return value: the #GSocketFamily of @mask's address
+ *
+ * Since: 2.32
+ */
+GSocketFamily
+g_inet_address_mask_get_family (GInetAddressMask *mask)
+{
+  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), G_SOCKET_FAMILY_INVALID);
+
+  return g_inet_address_get_family (mask->priv->addr);
+}
+
+/**
+ * g_inet_address_mask_get_address:
+ * @mask: a #GInetAddressMask
+ *
+ * Gets @mask's base address
+ *
+ * Return value: (transfer none): @mask's base address
+ *
+ * Since: 2.32
+ */
+GInetAddress *
+g_inet_address_mask_get_address (GInetAddressMask *mask)
+{
+  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), NULL);
+
+  return mask->priv->addr;
+}
+
+/**
+ * g_inet_address_mask_get_length:
+ * @mask: a #GInetAddressMask
+ *
+ * Gets @mask's length
+ *
+ * Return value: @mask's length
+ *
+ * Since: 2.32
+ */
+guint
+g_inet_address_mask_get_length (GInetAddressMask *mask)
+{
+  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), 0);
+
+  return mask->priv->length;
+}
+
+/**
+ * g_inet_address_mask_matches:
+ * @mask: a #GInetAddressMask
+ * @address: a #GInetAddress
+ *
+ * Tests if @address falls within the range described by @mask.
+ *
+ * Return value: whether @address falls within the range described by
+ * @mask.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_inet_address_mask_matches (GInetAddressMask *mask,
+			     GInetAddress     *address)
+{
+  const guint8 *maskbytes, *addrbytes;
+  int nbytes, nbits;
+
+  g_return_val_if_fail (G_IS_INET_ADDRESS_MASK (mask), FALSE);
+  g_return_val_if_fail (G_IS_INET_ADDRESS (address), FALSE);
+
+  if (g_inet_address_get_family (mask->priv->addr) !=
+      g_inet_address_get_family (address))
+    return FALSE;
+
+  if (mask->priv->length == 0)
+    return TRUE;
+
+  maskbytes = g_inet_address_to_bytes (mask->priv->addr);
+  addrbytes = g_inet_address_to_bytes (address);
+
+  nbytes = mask->priv->length / 8;
+  if (nbytes != 0 && memcmp (maskbytes, addrbytes, nbytes) != 0)
+    return FALSE;
+
+  nbits = mask->priv->length % 8;
+  if (nbits == 0)
+    return TRUE;
+
+  return maskbytes[nbytes] == (addrbytes[nbytes] & (0xFF << (8 - nbits)));
+}
+
+
+/**
+ * g_inet_address_mask_equal:
+ * @mask: a #GInetAddressMask
+ * @mask2: another #GInetAddressMask
+ *
+ * Tests if @mask and @mask2 are the same mask.
+ *
+ * Return value: whether @mask and @mask2 are the same mask
+ *
+ * Since: 2.32
+ */
+gboolean
+g_inet_address_mask_equal (GInetAddressMask  *mask,
+			   GInetAddressMask  *mask2)
+{
+  return ((mask->priv->length == mask2->priv->length) &&
+	  g_inet_address_equal (mask->priv->addr, mask2->priv->addr));
+}
diff --git a/gio/ginetaddressmask.h b/gio/ginetaddressmask.h
new file mode 100644
index 0000000..17d00ca
--- /dev/null
+++ b/gio/ginetaddressmask.h
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_INET_ADDRESS_MASK_H__
+#define __G_INET_ADDRESS_MASK_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_INET_ADDRESS_MASK         (g_inet_address_mask_get_type ())
+#define G_INET_ADDRESS_MASK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INET_ADDRESS_MASK, GInetAddressMask))
+#define G_INET_ADDRESS_MASK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_INET_ADDRESS_MASK, GInetAddressMaskClass))
+#define G_IS_INET_ADDRESS_MASK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INET_ADDRESS_MASK))
+#define G_IS_INET_ADDRESS_MASK_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_INET_ADDRESS_MASK))
+#define G_INET_ADDRESS_MASK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_INET_ADDRESS_MASK, GInetAddressMaskClass))
+
+typedef struct _GInetAddressMaskClass   GInetAddressMaskClass;
+typedef struct _GInetAddressMaskPrivate GInetAddressMaskPrivate;
+
+struct _GInetAddressMask
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  GInetAddressMaskPrivate *priv;
+};
+
+struct _GInetAddressMaskClass
+{
+  GObjectClass parent_class;
+
+};
+
+GType g_inet_address_mask_get_type (void) G_GNUC_CONST;
+
+GInetAddressMask *g_inet_address_mask_new             (GInetAddress      *addr,
+						       guint              length,
+						       GError           **error);
+
+GInetAddressMask *g_inet_address_mask_new_from_string (const gchar       *mask_string,
+						       GError           **error);
+gchar            *g_inet_address_mask_to_string       (GInetAddressMask  *mask);
+
+GSocketFamily     g_inet_address_mask_get_family      (GInetAddressMask  *mask);
+GInetAddress     *g_inet_address_mask_get_address     (GInetAddressMask  *mask);
+guint             g_inet_address_mask_get_length      (GInetAddressMask  *mask);
+
+gboolean          g_inet_address_mask_matches         (GInetAddressMask  *mask,
+						       GInetAddress      *address);
+gboolean          g_inet_address_mask_equal           (GInetAddressMask  *mask,
+						       GInetAddressMask  *mask2);
+
+G_END_DECLS
+
+#endif /* __G_INET_ADDRESS_MASK_H__ */
+
diff --git a/gio/gio.h b/gio/gio.h
index f0bc550..ab65b1b 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -75,6 +75,7 @@
 #include <gio/gfilteroutputstream.h>
 #include <gio/gicon.h>
 #include <gio/ginetaddress.h>
+#include <gio/ginetaddressmask.h>
 #include <gio/ginetsocketaddress.h>
 #include <gio/ginitable.h>
 #include <gio/ginputstream.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index a96b827..9ac949f 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -827,6 +827,15 @@ g_inet_address_to_bytes
 g_inet_address_get_native_size
 g_inet_address_to_string
 g_inet_address_equal
+g_inet_address_mask_new
+g_inet_address_mask_new_from_string
+g_inet_address_mask_to_string
+g_inet_address_mask_get_family
+g_inet_address_mask_get_address
+g_inet_address_mask_get_length
+g_inet_address_mask_get_type
+g_inet_address_mask_matches
+g_inet_address_mask_equal
 g_inet_socket_address_get_address
 g_inet_socket_address_get_port
 g_inet_socket_address_get_type
diff --git a/gio/giotypes.h b/gio/giotypes.h
index d37bcef..6081322 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -96,6 +96,7 @@ typedef struct _GFilenameCompleter            GFilenameCompleter;
 
 typedef struct _GIcon                         GIcon; /* Dummy typedef */
 typedef struct _GInetAddress                  GInetAddress;
+typedef struct _GInetAddressMask              GInetAddressMask;
 typedef struct _GInetSocketAddress            GInetSocketAddress;
 typedef struct _GInputStream                  GInputStream;
 typedef struct _GInitable                     GInitable;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]