#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"
GSocketFamily family;
guint8 mask[16];
gint length;
+ gushort port;
} GProxyResolverGnomeIPMask;
+typedef struct {
+ gchar *name;
+ gint length;
+ gushort port;
+} GProxyResolverGnomeDomain;
+
struct _GProxyResolverGnome {
GObject parent_instance;
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;
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
static void
update_settings (GProxyResolverGnome *resolver)
{
+ gchar **ignore_hosts;
gchar *host;
guint port;
int i;
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);
}
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)
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)
{
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;
}
}
- 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;
}
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)
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)
}
done:
- g_free (scheme);
- g_free (host);
+ if (addr)
+ g_object_unref (addr);
if (!proxies)
{
--- /dev/null
+/* 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();
+}