From eb9755dc9c765cd0381f8b6d897e6ff4f7582a0a Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Sat, 1 Oct 2011 08:31:54 -0400 Subject: [PATCH] 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(+) create mode 100644 gio/ginetaddressmask.c create mode 100644 gio/ginetaddressmask.h 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 @@ Lowlevel network support + 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
+ginetaddressmask +GInetAddressMask +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 + +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 + +g_inet_address_mask_get_type +
+ +
gsocketaddress GSocketAddress 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 + +#include +#include + +#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 can be included directly." +#endif + +#ifndef __G_INET_ADDRESS_MASK_H__ +#define __G_INET_ADDRESS_MASK_H__ + +#include + +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 #include #include +#include #include #include #include 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; -- 2.7.4