[glib-networking] proxy/gnome: Fix up multiple ignore_hosts bugs, and add a test
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking] proxy/gnome: Fix up multiple ignore_hosts bugs, and add a test
- Date: Thu, 22 Sep 2011 12:54:38 +0000 (UTC)
commit 21c42ff99f1c90fd40beee6420f254052549c358
Author: Dan Winship <danw gnome org>
Date: Mon Sep 12 14:41:20 2011 -0400
proxy/gnome: Fix up multiple ignore_hosts bugs, and add a test
- ignore_hosts names are treated as suffixes/patterns, not
exact matches
- ignore_hosts names/IPs can specify ports
- IPv6 literals now work in ignore_hosts (they were supposed to work
before, but there was a parsing bug).
- IP/length patterns now work in all cases
https://bugzilla.gnome.org/show_bug.cgi?id=655581
.gitignore | 1 +
Makefile.am | 2 +
configure.ac | 1 +
proxy/gnome/gproxyresolvergnome.c | 233 +++++++++++++++++++++++--------------
proxy/tests/Makefile.am | 19 +++
proxy/tests/gnome.c | 155 ++++++++++++++++++++++++
6 files changed, 323 insertions(+), 88 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 4c98074..6e0c9d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,5 +32,6 @@ m4
proxy/libproxy/glib-pacrunner
proxy/libproxy/org.gtk.GLib.PACRunner.service
+proxy/tests/gnome
/tls/tests/tls
diff --git a/Makefile.am b/Makefile.am
index 41c7a60..0a55026 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,6 +13,8 @@ if HAVE_GNOME_PROXY
SUBDIRS += proxy/gnome
endif
+SUBDIRS += proxy/tests
+
if HAVE_GNUTLS
SUBDIRS += tls/gnutls
endif
diff --git a/configure.ac b/configure.ac
index a5df2c2..6d5a94c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -215,6 +215,7 @@ AC_CONFIG_FILES([Makefile
po/Makefile.in po/Makefile
proxy/libproxy/Makefile
proxy/gnome/Makefile
+ proxy/tests/Makefile
tls/gnutls/Makefile
tls/tests/Makefile
])
diff --git a/proxy/gnome/gproxyresolvergnome.c b/proxy/gnome/gproxyresolvergnome.c
index fadc111..923b8bb 100644
--- a/proxy/gnome/gproxyresolvergnome.c
+++ b/proxy/gnome/gproxyresolvergnome.c
@@ -34,7 +34,6 @@
#define GNOME_PROXY_USE_SAME_PROXY_KEY "use-same-proxy"
#define GNOME_PROXY_HTTP_CHILD_SCHEMA "http"
-#define GNOME_PROXY_HTTP_ENABLED_KEY "enabled"
#define GNOME_PROXY_HTTP_HOST_KEY "host"
#define GNOME_PROXY_HTTP_PORT_KEY "port"
#define GNOME_PROXY_HTTP_USE_AUTH_KEY "use-authentication"
@@ -57,8 +56,15 @@ typedef struct {
GSocketFamily family;
guint8 mask[16];
gint length;
+ gushort port;
} GProxyResolverGnomeIPMask;
+typedef struct {
+ gchar *name;
+ gint length;
+ gushort port;
+} GProxyResolverGnomeDomain;
+
struct _GProxyResolverGnome {
GObject parent_instance;
@@ -72,8 +78,9 @@ struct _GProxyResolverGnome {
GDesktopProxyMode mode;
gchar *autoconfig_url;
gboolean use_same_proxy;
- gchar **ignore_hosts;
+
GProxyResolverGnomeIPMask *ignore_ips;
+ GProxyResolverGnomeDomain *ignore_domains;
gchar *http_proxy, *https_proxy;
gchar *ftp_proxy, *socks_authority;
@@ -99,14 +106,21 @@ g_proxy_resolver_gnome_class_finalize (GProxyResolverGnomeClass *klass)
static void
free_settings (GProxyResolverGnome *resolver)
{
- g_free (resolver->autoconfig_url);
- g_strfreev (resolver->ignore_hosts);
+ int i;
+
g_free (resolver->ignore_ips);
+ if (resolver->ignore_domains)
+ {
+ for (i = 0; resolver->ignore_domains[i].name; i++)
+ g_free (resolver->ignore_domains[i].name);
+ g_free (resolver->ignore_domains);
+ }
g_free (resolver->http_proxy);
g_free (resolver->https_proxy);
g_free (resolver->ftp_proxy);
g_free (resolver->socks_authority);
+ g_free (resolver->autoconfig_url);
}
static void
@@ -196,6 +210,7 @@ g_proxy_resolver_gnome_init (GProxyResolverGnome *resolver)
static void
update_settings (GProxyResolverGnome *resolver)
{
+ gchar **ignore_hosts;
gchar *host;
guint port;
int i;
@@ -211,62 +226,120 @@ update_settings (GProxyResolverGnome *resolver)
resolver->use_same_proxy =
g_settings_get_boolean (resolver->proxy_settings, GNOME_PROXY_USE_SAME_PROXY_KEY);
- resolver->ignore_hosts =
+ ignore_hosts =
g_settings_get_strv (resolver->proxy_settings, GNOME_PROXY_IGNORE_HOSTS_KEY);
-
- if (resolver->ignore_hosts && resolver->ignore_hosts[0])
+ if (ignore_hosts && ignore_hosts[0])
{
GArray *ignore_ips;
- gchar *slash;
+ GArray *ignore_domains;
+ gchar *host, *tmp, *slash, *colon, *bracket;
GInetAddress *iaddr;
- GProxyResolverGnomeIPMask mask;
+ GProxyResolverGnomeIPMask ip;
+ GProxyResolverGnomeDomain domain;
+ gushort port;
+ gboolean is_ip;
ignore_ips = g_array_new (TRUE, FALSE, sizeof (GProxyResolverGnomeIPMask));
- for (i = 0; resolver->ignore_hosts[i]; i++)
+ ignore_domains = g_array_new (TRUE, FALSE, sizeof (GProxyResolverGnomeDomain));
+
+ for (i = 0; ignore_hosts[i]; i++)
{
- host = resolver->ignore_hosts[i];
+ host = g_strchomp (ignore_hosts[i]);
+ port = 0;
+ is_ip = FALSE;
+
+ if (*host == '[')
+ {
+ /* [IPv6]:port */
+ is_ip = TRUE;
+
+ host++;
+ bracket = strchr (host, ']');
+ if (!bracket || !bracket[1] || bracket[1] != ':')
+ goto bad;
+
+ port = strtoul (bracket + 2, &tmp, 10);
+ if (*tmp)
+ goto bad;
+
+ *bracket = '\0';
+ }
+ else
+ {
+ colon = strchr (host, ':');
+ if (colon && !strchr (colon + 1, ':'))
+ {
+ /* hostname:port or IPv4:port */
+ port = strtoul (colon + 1, &tmp, 10);
+ if (*tmp)
+ goto bad;
+ *colon = '\0';
+ }
+ }
+
slash = strchr (host, '/');
if (slash)
- host = g_strndup (host, slash - host);
+ {
+ /* IPv6/length or IPv4/length */
+ is_ip = TRUE;
+ *slash = '\0';
+ }
+
iaddr = g_inet_address_new_from_string (host);
+ if (!iaddr && is_ip)
+ goto bad;
+
if (iaddr)
{
int addrlen = g_inet_address_get_native_size (iaddr);
- memset (&mask, 0, sizeof (mask));
- mask.family = g_inet_address_get_family (iaddr);
- memcpy (mask.mask, g_inet_address_to_bytes (iaddr), addrlen);
+ memset (&ip, 0, sizeof (ip));
+ ip.family = g_inet_address_get_family (iaddr);
+ memcpy (ip.mask, g_inet_address_to_bytes (iaddr), addrlen);
+ ip.port = port;
if (slash)
{
- mask.length = atoi (slash + 1);
- if (mask.length > addrlen * 8)
- {
- g_warning("ignore_host '%s' has invalid mask length",
- resolver->ignore_hosts[i]);
- mask.length = addrlen;
- }
+ ip.length = strtoul (slash + 1, &tmp, 10);
+ if (*tmp || ip.length > addrlen * 8)
+ goto bad;
}
else
- mask.length = 0;
+ ip.length = addrlen * 8;
- g_array_append_val (ignore_ips, mask);
+ g_array_append_val (ignore_ips, ip);
g_object_unref (iaddr);
}
- if (slash)
- g_free (host);
- }
+ else
+ {
+ if (g_str_has_prefix (host, "*."))
+ host += 2;
+ else if (*host == '.')
+ host++;
+
+ memset (&domain, 0, sizeof (domain));
+ domain.name = g_strdup (host);
+ domain.length = strlen (domain.name);
+ domain.port = port;
+ g_array_append_val (ignore_domains, domain);
+ }
+ continue;
- if (ignore_ips->len)
- resolver->ignore_ips = (GProxyResolverGnomeIPMask *)g_array_free (ignore_ips, FALSE);
- else
- {
- g_array_free (ignore_ips, TRUE);
- resolver->ignore_ips = NULL;
+ bad:
+ g_warning ("Ignoring invalid ignore_hosts value '%s'", host);
}
+
+ resolver->ignore_ips = (GProxyResolverGnomeIPMask *)
+ g_array_free (ignore_ips, ignore_ips->len == 0);
+ resolver->ignore_domains = (GProxyResolverGnomeDomain *)
+ g_array_free (ignore_domains, ignore_domains->len == 0);
}
else
- resolver->ignore_ips = NULL;
+ {
+ resolver->ignore_ips = NULL;
+ resolver->ignore_domains = NULL;
+ }
+ g_strfreev (ignore_hosts);
host = g_settings_get_string (resolver->http_settings, GNOME_PROXY_HTTP_HOST_KEY);
port = g_settings_get_int (resolver->http_settings, GNOME_PROXY_HTTP_PORT_KEY);
@@ -346,39 +419,6 @@ g_proxy_resolver_gnome_is_supported (GProxyResolver *object)
}
static gboolean
-parse_uri (const gchar *uri,
- gchar **scheme,
- gchar **host)
-{
- const gchar *authority, *hoststart, *hostend, *at, *colon, *slash;
-
- colon = strchr (uri, ':');
- if (!colon || strncmp (colon, "://", 3) != 0)
- return FALSE;
-
- *scheme = g_strndup (uri, colon - uri);
-
- authority = colon + 3;
- colon = strchr (authority, ':');
- slash = strchr (authority, '/');
- if (colon && (!slash || colon < slash))
- hostend = colon;
- else if (slash)
- hostend = slash;
- else
- hostend = authority + strlen (authority);
-
- at = strchr (authority, '@');
- if (at && at < hostend)
- hoststart = at + 1;
- else
- hoststart = authority;
- *host = g_strndup (hoststart, hostend - hoststart);
-
- return TRUE;
-}
-
-static gboolean
masked_compare (const guint8 *mask,
const guint8 *addr,
int maskbits)
@@ -393,15 +433,20 @@ masked_compare (const guint8 *mask,
return FALSE;
bits = maskbits % 8;
+ if (bits == 0)
+ return TRUE;
+
return mask[bytes] == (addr[bytes] & (0xFF << (8 - bits)));
}
static gboolean
ignore_host (GProxyResolverGnome *resolver,
- const gchar *host)
+ const gchar *host,
+ gushort port)
{
+ gchar *ascii_host = NULL;
gboolean ignore = FALSE;
- gint i;
+ gint i, length, offset;
if (resolver->ignore_ips)
{
@@ -413,11 +458,13 @@ ignore_host (GProxyResolverGnome *resolver,
GSocketFamily family = g_inet_address_get_family (iaddr);
const guint8 *addr = g_inet_address_to_bytes (iaddr);
- for (i = 0; resolver->ignore_ips[i].length; i++)
+ for (i = 0; resolver->ignore_ips[i].family; i++)
{
- if (resolver->ignore_ips[i].family == family &&
- masked_compare (resolver->ignore_ips[i].mask, addr,
- resolver->ignore_ips[i].length))
+ GProxyResolverGnomeIPMask *ip = &resolver->ignore_ips[i];
+
+ if (ip->family == family &&
+ (ip->port == 0 || ip->port == port) &&
+ masked_compare (ip->mask, addr, ip->length))
{
ignore = TRUE;
break;
@@ -429,25 +476,28 @@ ignore_host (GProxyResolverGnome *resolver,
}
}
- if (resolver->ignore_hosts && resolver->ignore_hosts[0])
- {
- gchar *ascii_host = NULL;
-
- if (g_hostname_is_non_ascii (host))
- host = ascii_host = g_hostname_to_ascii (host);
+ if (g_hostname_is_non_ascii (host))
+ host = ascii_host = g_hostname_to_ascii (host);
+ length = strlen (host);
- for (i = 0; resolver->ignore_hosts[i]; i++)
+ if (resolver->ignore_domains)
+ {
+ for (i = 0; resolver->ignore_domains[i].length; i++)
{
- if (!g_ascii_strcasecmp (host, resolver->ignore_hosts[i]))
+ GProxyResolverGnomeDomain *domain = &resolver->ignore_domains[i];
+
+ offset = length - domain->length;
+ if ((domain->port == 0 || domain->port == port) &&
+ (offset == 0 || (offset > 0 && host[offset - 1] == '.')) &&
+ (g_ascii_strcasecmp (domain->name, host + offset) == 0))
{
ignore = TRUE;
break;
}
}
-
- g_free (ascii_host);
}
+ g_free (ascii_host);
return ignore;
}
@@ -458,9 +508,11 @@ g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
GError **error)
{
GProxyResolverGnome *resolver = G_PROXY_RESOLVER_GNOME (proxy_resolver);
- gchar *scheme = NULL, *host = NULL;
+ GSocketConnectable *addr;
+ const gchar *scheme = NULL, *host = NULL;
const gchar *proxy = "direct://";
gchar **proxies = NULL;
+ gushort port;
g_mutex_lock (resolver->lock);
if (resolver->need_update)
@@ -471,9 +523,14 @@ g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
goto done;
/* FIXME: use guri when it lands... */
- if (!parse_uri (uri, &scheme, &host))
+ addr = g_network_address_parse_uri (uri, 0, error);
+ if (!addr)
goto done;
- if (ignore_host (resolver, host))
+ scheme = g_network_address_get_scheme (G_NETWORK_ADDRESS (addr));
+ host = g_network_address_get_hostname (G_NETWORK_ADDRESS (addr));
+ port = g_network_address_get_port (G_NETWORK_ADDRESS (addr));
+
+ if (ignore_host (resolver, host, port))
goto done;
if (resolver->pacrunner)
@@ -521,8 +578,8 @@ g_proxy_resolver_gnome_lookup (GProxyResolver *proxy_resolver,
}
done:
- g_free (scheme);
- g_free (host);
+ if (addr)
+ g_object_unref (addr);
if (!proxies)
{
diff --git a/proxy/tests/Makefile.am b/proxy/tests/Makefile.am
new file mode 100644
index 0000000..a1e69bd
--- /dev/null
+++ b/proxy/tests/Makefile.am
@@ -0,0 +1,19 @@
+NULL =
+
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ $(GSETTINGS_DESKTOP_SCHEMAS_CFLAGS) \
+ -I$(top_srcdir)/proxy \
+ -DSRCDIR=\""$(srcdir)"\" \
+ -DTOP_BUILDDIR=\""$(top_builddir)"\"
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+LDADD = \
+ $(GLIB_LIBS)
+
+TEST_PROGS += \
+ gnome \
+ $(NULL)
diff --git a/proxy/tests/gnome.c b/proxy/tests/gnome.c
new file mode 100644
index 0000000..e87eec2
--- /dev/null
+++ b/proxy/tests/gnome.c
@@ -0,0 +1,155 @@
+/* GProxyResolverGnome tests
+ *
+ * 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 <gio/gio.h>
+#include <gdesktop-enums.h>
+
+#define GNOME_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy"
+#define GNOME_PROXY_MODE_KEY "mode"
+#define GNOME_PROXY_AUTOCONFIG_URL_KEY "autoconfig-url"
+#define GNOME_PROXY_IGNORE_HOSTS_KEY "ignore-hosts"
+#define GNOME_PROXY_USE_SAME_PROXY_KEY "use-same-proxy"
+
+#define GNOME_PROXY_HTTP_CHILD_SCHEMA "http"
+#define GNOME_PROXY_HTTP_HOST_KEY "host"
+#define GNOME_PROXY_HTTP_PORT_KEY "port"
+#define GNOME_PROXY_HTTP_USE_AUTH_KEY "use-authentication"
+#define GNOME_PROXY_HTTP_USER_KEY "authentication-user"
+#define GNOME_PROXY_HTTP_PASSWORD_KEY "authentication-password"
+
+#define GNOME_PROXY_HTTPS_CHILD_SCHEMA "https"
+#define GNOME_PROXY_HTTPS_HOST_KEY "host"
+#define GNOME_PROXY_HTTPS_PORT_KEY "port"
+
+#define GNOME_PROXY_FTP_CHILD_SCHEMA "ftp"
+#define GNOME_PROXY_FTP_HOST_KEY "host"
+#define GNOME_PROXY_FTP_PORT_KEY "port"
+
+#define GNOME_PROXY_SOCKS_CHILD_SCHEMA "socks"
+#define GNOME_PROXY_SOCKS_HOST_KEY "host"
+#define GNOME_PROXY_SOCKS_PORT_KEY "port"
+
+static const char *ignore_hosts[] = {
+ ".bbb.xx",
+ "*.ccc.xx",
+ "ddd.xx",
+ "*.eee.xx:8000",
+ "127.0.0.0/24",
+ "::1",
+ "fe80::/10"
+};
+static const int n_ignore_hosts = G_N_ELEMENTS (ignore_hosts);
+
+static const struct {
+ const char *uri;
+ const char *proxy;
+} ignore_tests[] = {
+ { "http://aaa.xx/", "http://localhost:8080" },
+ { "http://aaa.xx:8000/", "http://localhost:8080" },
+ { "http://www.aaa.xx/", "http://localhost:8080" },
+ { "http://www.aaa.xx:8000/", "http://localhost:8080" },
+ { "https://aaa.xx/", "http://localhost:8080" },
+ { "http://bbb.xx/", "direct://" },
+ { "http://www.bbb.xx/", "direct://" },
+ { "http://bbb.xx:8000/", "direct://" },
+ { "http://www.bbb.xx:8000/", "direct://" },
+ { "https://bbb.xx/", "direct://" },
+ { "http://nobbb.xx/", "http://localhost:8080" },
+ { "http://www.nobbb.xx/", "http://localhost:8080" },
+ { "http://nobbb.xx:8000/", "http://localhost:8080" },
+ { "http://www.nobbb.xx:8000/", "http://localhost:8080" },
+ { "https://nobbb.xx/", "http://localhost:8080" },
+ { "http://ccc.xx/", "direct://" },
+ { "http://www.ccc.xx/", "direct://" },
+ { "http://ccc.xx:8000/", "direct://" },
+ { "http://www.ccc.xx:8000/", "direct://" },
+ { "https://ccc.xx/", "direct://" },
+ { "http://ddd.xx/", "direct://" },
+ { "http://ddd.xx:8000/", "direct://" },
+ { "http://www.ddd.xx/", "direct://" },
+ { "http://www.ddd.xx:8000/", "direct://" },
+ { "https://ddd.xx/", "direct://" },
+ { "http://eee.xx/", "http://localhost:8080" },
+ { "http://eee.xx:8000/", "direct://" },
+ { "http://www.eee.xx/", "http://localhost:8080" },
+ { "http://www.eee.xx:8000/", "direct://" },
+ { "https://eee.xx/", "http://localhost:8080" },
+ { "http://1.2.3.4/", "http://localhost:8080" },
+ { "http://127.0.0.1/", "direct://" },
+ { "http://127.0.0.2/", "direct://" },
+ { "http://127.0.0.255/", "direct://" },
+ { "http://127.0.1.0/", "http://localhost:8080" },
+ { "http://[::1]/", "direct://" },
+ { "http://[::1]:80/", "direct://" },
+ { "http://[::1:1]/", "http://localhost:8080" },
+ { "http://[::1:1]:80/", "http://localhost:8080" },
+ { "http://[fe80::1]/", "direct://" },
+ { "http://[fe80::1]:80/", "direct://" },
+ { "http://[fec0::1]/", "http://localhost:8080" },
+ { "http://[fec0::1]:80/", "http://localhost:8080" }
+};
+static const int n_ignore_tests = G_N_ELEMENTS (ignore_tests);
+
+static void
+test_proxy_ignore (void)
+{
+ GSettings *settings, *http;
+ GProxyResolver *resolver;
+ GError *error = NULL;
+ char **proxies;
+ int i;
+
+ settings = g_settings_new (GNOME_PROXY_SETTINGS_SCHEMA);
+ g_settings_set_enum (settings, GNOME_PROXY_MODE_KEY, G_DESKTOP_PROXY_MODE_MANUAL);
+ g_settings_set (settings, GNOME_PROXY_IGNORE_HOSTS_KEY,
+ "@as", g_variant_new_strv (ignore_hosts, n_ignore_hosts));
+
+ http = g_settings_get_child (settings, GNOME_PROXY_HTTP_CHILD_SCHEMA);
+ g_settings_set_string (http, GNOME_PROXY_HTTP_HOST_KEY, "localhost");
+ g_settings_set_int (http, GNOME_PROXY_HTTP_PORT_KEY, 8080);
+
+ resolver = g_proxy_resolver_get_default ();
+
+ for (i = 0; i < n_ignore_tests; i++)
+ {
+ proxies = g_proxy_resolver_lookup (resolver, ignore_tests[i].uri,
+ NULL, &error);
+ g_assert_no_error (error);
+
+ g_assert_cmpstr (proxies[0], ==, ignore_tests[i].proxy);
+ g_strfreev (proxies);
+ }
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/proxy/gnome/.libs", TRUE);
+ g_setenv ("GIO_USE_PROXY_RESOLVER", "gnome", TRUE);
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+
+ g_test_add_func ("/proxy/gnome/ignore", test_proxy_ignore);
+
+ return g_test_run();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]