| /* GIO - GLib Input, Output and Streaming Library | 
 |  * | 
 |  * Copyright 2010, 2013 Red Hat, Inc. | 
 |  * | 
 |  * SPDX-License-Identifier: LGPL-2.1-or-later | 
 |  * | 
 |  * 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.1 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, see | 
 |  * <http://www.gnu.org/licenses/>. | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "gsimpleproxyresolver.h" | 
 | #include "ginetaddress.h" | 
 | #include "ginetaddressmask.h" | 
 | #include "gnetworkingprivate.h" | 
 | #include "gtask.h" | 
 |  | 
 | #include "glibintl.h" | 
 |  | 
 | /** | 
 |  * GSimpleProxyResolver: | 
 |  * | 
 |  * `GSimpleProxyResolver` is a simple [iface@Gio.ProxyResolver] implementation | 
 |  * that handles a single default proxy, multiple URI-scheme-specific | 
 |  * proxies, and a list of hosts that proxies should not be used for. | 
 |  * | 
 |  * `GSimpleProxyResolver` is never the default proxy resolver, but it | 
 |  * can be used as the base class for another proxy resolver | 
 |  * implementation, or it can be created and used manually, such as | 
 |  * with [method@Gio.SocketClient.set_proxy_resolver]. | 
 |  * | 
 |  * Since: 2.36 | 
 |  */ | 
 |  | 
 | typedef struct { | 
 |   gchar        *name; | 
 |   gsize          length; | 
 |   gushort       port; | 
 | } GSimpleProxyResolverDomain; | 
 |  | 
 | struct _GSimpleProxyResolverPrivate { | 
 |   gchar *default_proxy, **ignore_hosts; | 
 |   GHashTable *uri_proxies; | 
 |  | 
 |   GPtrArray *ignore_ips; | 
 |   GSimpleProxyResolverDomain *ignore_domains; | 
 | }; | 
 |  | 
 | static void g_simple_proxy_resolver_iface_init (GProxyResolverInterface *iface); | 
 |  | 
 | G_DEFINE_TYPE_WITH_CODE (GSimpleProxyResolver, g_simple_proxy_resolver, G_TYPE_OBJECT, | 
 |                          G_ADD_PRIVATE (GSimpleProxyResolver) | 
 |                          G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER, | 
 |                                                 g_simple_proxy_resolver_iface_init)) | 
 |  | 
 | enum | 
 | { | 
 |   PROP_0, | 
 |   PROP_DEFAULT_PROXY, | 
 |   PROP_IGNORE_HOSTS | 
 | }; | 
 |  | 
 | static void reparse_ignore_hosts (GSimpleProxyResolver *resolver); | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_finalize (GObject *object) | 
 | { | 
 |   GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); | 
 |   GSimpleProxyResolverPrivate *priv = resolver->priv; | 
 |  | 
 |   g_free (priv->default_proxy); | 
 |   g_hash_table_destroy (priv->uri_proxies); | 
 |  | 
 |   g_clear_pointer (&priv->ignore_hosts, g_strfreev); | 
 |   /* This will free ignore_ips and ignore_domains */ | 
 |   reparse_ignore_hosts (resolver); | 
 |  | 
 |   G_OBJECT_CLASS (g_simple_proxy_resolver_parent_class)->finalize (object); | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_init (GSimpleProxyResolver *resolver) | 
 | { | 
 |   resolver->priv = g_simple_proxy_resolver_get_instance_private (resolver); | 
 |   resolver->priv->uri_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, | 
 |                                                        g_free, g_free); | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_set_property (GObject      *object, | 
 |                                       guint         prop_id, | 
 |                                       const GValue *value, | 
 |                                       GParamSpec   *pspec) | 
 | { | 
 |   GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); | 
 |  | 
 |   switch (prop_id) | 
 |     { | 
 |       case PROP_DEFAULT_PROXY: | 
 |         g_simple_proxy_resolver_set_default_proxy (resolver, g_value_get_string (value)); | 
 | 	break; | 
 |  | 
 |       case PROP_IGNORE_HOSTS: | 
 |         g_simple_proxy_resolver_set_ignore_hosts (resolver, g_value_get_boxed (value)); | 
 | 	break; | 
 |  | 
 |       default: | 
 | 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_get_property (GObject    *object, | 
 |                                       guint       prop_id, | 
 |                                       GValue     *value, | 
 |                                       GParamSpec *pspec) | 
 | { | 
 |   GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); | 
 |  | 
 |   switch (prop_id) | 
 |     { | 
 |       case PROP_DEFAULT_PROXY: | 
 | 	g_value_set_string (value, resolver->priv->default_proxy); | 
 | 	break; | 
 |  | 
 |       case PROP_IGNORE_HOSTS: | 
 | 	g_value_set_boxed (value, resolver->priv->ignore_hosts); | 
 | 	break; | 
 |  | 
 |       default: | 
 | 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | reparse_ignore_hosts (GSimpleProxyResolver *resolver) | 
 | { | 
 |   GSimpleProxyResolverPrivate *priv = resolver->priv; | 
 |   GPtrArray *ignore_ips; | 
 |   GArray *ignore_domains; | 
 |   gchar *host, *tmp, *colon, *bracket; | 
 |   GInetAddress *iaddr; | 
 |   GInetAddressMask *mask; | 
 |   GSimpleProxyResolverDomain domain; | 
 |   gushort port; | 
 |   int i; | 
 |  | 
 |   if (priv->ignore_ips) | 
 |     g_ptr_array_free (priv->ignore_ips, TRUE); | 
 |   if (priv->ignore_domains) | 
 |     { | 
 |       for (i = 0; priv->ignore_domains[i].name; i++) | 
 | 	g_free (priv->ignore_domains[i].name); | 
 |       g_free (priv->ignore_domains); | 
 |     } | 
 |   priv->ignore_ips = NULL; | 
 |   priv->ignore_domains = NULL; | 
 |  | 
 |   if (!priv->ignore_hosts || !priv->ignore_hosts[0]) | 
 |     return; | 
 |  | 
 |   ignore_ips = g_ptr_array_new_with_free_func (g_object_unref); | 
 |   ignore_domains = g_array_new (TRUE, FALSE, sizeof (GSimpleProxyResolverDomain)); | 
 |  | 
 |   for (i = 0; priv->ignore_hosts[i]; i++) | 
 |     { | 
 |       host = g_strchomp (priv->ignore_hosts[i]); | 
 |  | 
 |       /* See if it's an IP address or IP/length mask */ | 
 |       mask = g_inet_address_mask_new_from_string (host, NULL); | 
 |       if (mask) | 
 |         { | 
 |           g_ptr_array_add (ignore_ips, mask); | 
 |           continue; | 
 |         } | 
 |  | 
 |       port = 0; | 
 |  | 
 |       if (*host == '[') | 
 |         { | 
 |           /* [IPv6]:port */ | 
 |           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'; | 
 |             } | 
 |         } | 
 |  | 
 |       iaddr = g_inet_address_new_from_string (host); | 
 |       if (iaddr) | 
 |         g_object_unref (iaddr); | 
 |       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; | 
 |  | 
 |     bad: | 
 |       g_warning ("Ignoring invalid ignore_hosts value '%s'", host); | 
 |     } | 
 |  | 
 |   if (ignore_ips->len) | 
 |     priv->ignore_ips = ignore_ips; | 
 |   else | 
 |     g_ptr_array_free (ignore_ips, TRUE); | 
 |  | 
 |   if (ignore_domains->len) | 
 |     priv->ignore_domains = (GSimpleProxyResolverDomain *)ignore_domains->data; | 
 |   g_array_free (ignore_domains, ignore_domains->len == 0); | 
 | } | 
 |  | 
 | static gboolean | 
 | ignore_host (GSimpleProxyResolver *resolver, | 
 | 	     const gchar          *host, | 
 | 	     gushort               port) | 
 | { | 
 |   GSimpleProxyResolverPrivate *priv = resolver->priv; | 
 |   gchar *ascii_host = NULL; | 
 |   gboolean ignore = FALSE; | 
 |   gsize offset, length; | 
 |   guint i; | 
 |  | 
 |   if (priv->ignore_ips) | 
 |     { | 
 |       GInetAddress *iaddr; | 
 |  | 
 |       iaddr = g_inet_address_new_from_string (host); | 
 |       if (iaddr) | 
 | 	{ | 
 | 	  for (i = 0; i < priv->ignore_ips->len; i++) | 
 | 	    { | 
 | 	      GInetAddressMask *mask = priv->ignore_ips->pdata[i]; | 
 |  | 
 | 	      if (g_inet_address_mask_matches (mask, iaddr)) | 
 | 		{ | 
 | 		  ignore = TRUE; | 
 | 		  break; | 
 | 		} | 
 | 	    } | 
 |  | 
 | 	  g_object_unref (iaddr); | 
 | 	  if (ignore) | 
 | 	    return TRUE; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (priv->ignore_domains) | 
 |     { | 
 |       length = 0; | 
 |       if (g_hostname_is_non_ascii (host)) | 
 |         host = ascii_host = g_hostname_to_ascii (host); | 
 |       if (host) | 
 |         length = strlen (host); | 
 |  | 
 |       for (i = 0; length > 0 && priv->ignore_domains[i].length; i++) | 
 | 	{ | 
 | 	  GSimpleProxyResolverDomain *domain = &priv->ignore_domains[i]; | 
 |  | 
 |           if (domain->length > length) | 
 |             continue; | 
 |  | 
 | 	  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); | 
 |     } | 
 |  | 
 |   return ignore; | 
 | } | 
 |  | 
 | static gchar ** | 
 | g_simple_proxy_resolver_lookup (GProxyResolver  *proxy_resolver, | 
 |                                 const gchar     *uri, | 
 |                                 GCancellable    *cancellable, | 
 |                                 GError         **error) | 
 | { | 
 |   GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (proxy_resolver); | 
 |   GSimpleProxyResolverPrivate *priv = resolver->priv; | 
 |   const gchar *proxy = NULL; | 
 |   gchar **proxies; | 
 |  | 
 |   if (priv->ignore_ips || priv->ignore_domains) | 
 |     { | 
 |       gchar *host = NULL; | 
 |       gint port; | 
 |  | 
 |       if (g_uri_split_network (uri, G_URI_FLAGS_NONE, NULL, | 
 |                                &host, &port, NULL) && | 
 |           ignore_host (resolver, host, port > 0 ? port : 0)) | 
 |         proxy = "direct://"; | 
 |  | 
 |       g_free (host); | 
 |     } | 
 |  | 
 |   if (!proxy && g_hash_table_size (priv->uri_proxies)) | 
 |     { | 
 |       gchar *scheme = g_ascii_strdown (uri, strcspn (uri, ":")); | 
 |  | 
 |       proxy = g_hash_table_lookup (priv->uri_proxies, scheme); | 
 |       g_free (scheme); | 
 |     } | 
 |  | 
 |   if (!proxy) | 
 |     proxy = priv->default_proxy; | 
 |   if (!proxy) | 
 |     proxy = "direct://"; | 
 |  | 
 |   if (!strncmp (proxy, "socks://", 8)) | 
 |     { | 
 |       proxies = g_new0 (gchar *, 4); | 
 |       proxies[0] = g_strdup_printf ("socks5://%s", proxy + 8); | 
 |       proxies[1] = g_strdup_printf ("socks4a://%s", proxy + 8); | 
 |       proxies[2] = g_strdup_printf ("socks4://%s", proxy + 8); | 
 |       proxies[3] = NULL; | 
 |     } | 
 |   else | 
 |     { | 
 |       proxies = g_new0 (gchar *, 2); | 
 |       proxies[0] = g_strdup (proxy); | 
 |     } | 
 |  | 
 |   return proxies; | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_lookup_async (GProxyResolver      *proxy_resolver, | 
 |                                       const gchar         *uri, | 
 |                                       GCancellable        *cancellable, | 
 |                                       GAsyncReadyCallback  callback, | 
 |                                       gpointer             user_data) | 
 | { | 
 |   GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (proxy_resolver); | 
 |   GTask *task; | 
 |   GError *error = NULL; | 
 |   char **proxies; | 
 |  | 
 |   task = g_task_new (resolver, cancellable, callback, user_data); | 
 |   g_task_set_source_tag (task, g_simple_proxy_resolver_lookup_async); | 
 |  | 
 |   proxies = g_simple_proxy_resolver_lookup (proxy_resolver, uri, | 
 |                                             cancellable, &error); | 
 |   if (proxies) | 
 |     g_task_return_pointer (task, proxies, (GDestroyNotify)g_strfreev); | 
 |   else | 
 |     g_task_return_error (task, error); | 
 |   g_object_unref (task); | 
 | } | 
 |  | 
 | static gchar ** | 
 | g_simple_proxy_resolver_lookup_finish (GProxyResolver  *resolver, | 
 |                                        GAsyncResult    *result, | 
 |                                        GError         **error) | 
 | { | 
 |   g_return_val_if_fail (g_task_is_valid (result, resolver), NULL); | 
 |  | 
 |   return g_task_propagate_pointer (G_TASK (result), error); | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_class_init (GSimpleProxyResolverClass *resolver_class) | 
 | { | 
 |   GObjectClass *object_class = G_OBJECT_CLASS (resolver_class); | 
 |  | 
 |   object_class->get_property = g_simple_proxy_resolver_get_property; | 
 |   object_class->set_property = g_simple_proxy_resolver_set_property; | 
 |   object_class->finalize = g_simple_proxy_resolver_finalize; | 
 |  | 
 |   /** | 
 |    * GSimpleProxyResolver:default-proxy: | 
 |    * | 
 |    * The default proxy URI that will be used for any URI that doesn't | 
 |    * match #GSimpleProxyResolver:ignore-hosts, and doesn't match any | 
 |    * of the schemes set with g_simple_proxy_resolver_set_uri_proxy(). | 
 |    * | 
 |    * Note that as a special case, if this URI starts with | 
 |    * "socks://", #GSimpleProxyResolver will treat it as referring | 
 |    * to all three of the socks5, socks4a, and socks4 proxy types. | 
 |    */ | 
 |   g_object_class_install_property (object_class, PROP_DEFAULT_PROXY, | 
 | 				   g_param_spec_string ("default-proxy", NULL, NULL, | 
 |                                                         NULL, | 
 |                                                         G_PARAM_READWRITE | | 
 |                                                         G_PARAM_STATIC_STRINGS)); | 
 |  | 
 |   /** | 
 |    * GSimpleProxyResolver:ignore-hosts: | 
 |    * | 
 |    * A list of hostnames and IP addresses that the resolver should | 
 |    * allow direct connections to. | 
 |    * | 
 |    * Entries can be in one of 4 formats: | 
 |    * | 
 |    * - A hostname, such as "example.com", ".example.com", or | 
 |    *   "*.example.com", any of which match "example.com" or | 
 |    *   any subdomain of it. | 
 |    * | 
 |    * - An IPv4 or IPv6 address, such as "192.168.1.1", | 
 |    *   which matches only that address. | 
 |    * | 
 |    * - A hostname or IP address followed by a port, such as | 
 |    *   "example.com:80", which matches whatever the hostname or IP | 
 |    *   address would match, but only for URLs with the (explicitly) | 
 |    *   indicated port. In the case of an IPv6 address, the address | 
 |    *   part must appear in brackets: "[::1]:443" | 
 |    * | 
 |    * - An IP address range, given by a base address and prefix length, | 
 |    *   such as "fe80::/10", which matches any address in that range. | 
 |    * | 
 |    * Note that when dealing with Unicode hostnames, the matching is | 
 |    * done against the ASCII form of the name. | 
 |    * | 
 |    * Also note that hostname exclusions apply only to connections made | 
 |    * to hosts identified by name, and IP address exclusions apply only | 
 |    * to connections made to hosts identified by address. That is, if | 
 |    * example.com has an address of 192.168.1.1, and the :ignore-hosts list | 
 |    * contains only "192.168.1.1", then a connection to "example.com" | 
 |    * (eg, via a #GNetworkAddress) will use the proxy, and a connection to | 
 |    * "192.168.1.1" (eg, via a #GInetSocketAddress) will not. | 
 |    * | 
 |    * These rules match the "ignore-hosts"/"noproxy" rules most | 
 |    * commonly used by other applications. | 
 |    */ | 
 |   g_object_class_install_property (object_class, PROP_IGNORE_HOSTS, | 
 | 				   g_param_spec_boxed ("ignore-hosts", NULL, NULL, | 
 |                                                        G_TYPE_STRV, | 
 |                                                        G_PARAM_READWRITE | | 
 |                                                        G_PARAM_STATIC_STRINGS)); | 
 |  | 
 | } | 
 |  | 
 | static void | 
 | g_simple_proxy_resolver_iface_init (GProxyResolverInterface *iface) | 
 | { | 
 |   iface->lookup = g_simple_proxy_resolver_lookup; | 
 |   iface->lookup_async = g_simple_proxy_resolver_lookup_async; | 
 |   iface->lookup_finish = g_simple_proxy_resolver_lookup_finish; | 
 | } | 
 |  | 
 | /** | 
 |  * g_simple_proxy_resolver_new: | 
 |  * @default_proxy: (nullable): the default proxy to use, eg | 
 |  *     "socks://192.168.1.1" | 
 |  * @ignore_hosts: (array zero-terminated=1) (nullable): an optional list of hosts/IP addresses | 
 |  *     to not use a proxy for. | 
 |  * | 
 |  * Creates a new #GSimpleProxyResolver. See | 
 |  * #GSimpleProxyResolver:default-proxy and | 
 |  * #GSimpleProxyResolver:ignore-hosts for more details on how the | 
 |  * arguments are interpreted. | 
 |  * | 
 |  * Returns: (transfer full): a new #GSimpleProxyResolver | 
 |  * | 
 |  * Since: 2.36 | 
 |  */ | 
 | GProxyResolver * | 
 | g_simple_proxy_resolver_new (const gchar  *default_proxy, | 
 |                              gchar       **ignore_hosts) | 
 | { | 
 |   return g_object_new (G_TYPE_SIMPLE_PROXY_RESOLVER, | 
 |                        "default-proxy", default_proxy, | 
 |                        "ignore-hosts", ignore_hosts, | 
 |                        NULL); | 
 | } | 
 |  | 
 | /** | 
 |  * g_simple_proxy_resolver_set_default_proxy: | 
 |  * @resolver: a #GSimpleProxyResolver | 
 |  * @default_proxy: (nullable): the default proxy to use | 
 |  * | 
 |  * Sets the default proxy on @resolver, to be used for any URIs that | 
 |  * don't match #GSimpleProxyResolver:ignore-hosts or a proxy set | 
 |  * via g_simple_proxy_resolver_set_uri_proxy(). | 
 |  * | 
 |  * If @default_proxy starts with "socks://", | 
 |  * #GSimpleProxyResolver will treat it as referring to all three of | 
 |  * the socks5, socks4a, and socks4 proxy types. | 
 |  * | 
 |  * Since: 2.36 | 
 |  */ | 
 | void | 
 | g_simple_proxy_resolver_set_default_proxy (GSimpleProxyResolver *resolver, | 
 |                                            const gchar          *default_proxy) | 
 | { | 
 |   g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); | 
 |   g_return_if_fail (default_proxy == NULL || g_uri_is_valid (default_proxy, G_URI_FLAGS_NONE, NULL)); | 
 |  | 
 |   g_free (resolver->priv->default_proxy); | 
 |   resolver->priv->default_proxy = g_strdup (default_proxy); | 
 |   g_object_notify (G_OBJECT (resolver), "default-proxy"); | 
 | } | 
 |  | 
 | /** | 
 |  * g_simple_proxy_resolver_set_ignore_hosts: | 
 |  * @resolver: a #GSimpleProxyResolver | 
 |  * @ignore_hosts: (array zero-terminated=1): %NULL-terminated list of hosts/IP addresses | 
 |  *     to not use a proxy for | 
 |  * | 
 |  * Sets the list of ignored hosts. | 
 |  * | 
 |  * See #GSimpleProxyResolver:ignore-hosts for more details on how the | 
 |  * @ignore_hosts argument is interpreted. | 
 |  * | 
 |  * Since: 2.36 | 
 |  */ | 
 | void | 
 | g_simple_proxy_resolver_set_ignore_hosts (GSimpleProxyResolver  *resolver, | 
 |                                           gchar                **ignore_hosts) | 
 | { | 
 |   g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); | 
 |  | 
 |   g_strfreev (resolver->priv->ignore_hosts); | 
 |   resolver->priv->ignore_hosts = g_strdupv (ignore_hosts); | 
 |   reparse_ignore_hosts (resolver); | 
 |   g_object_notify (G_OBJECT (resolver), "ignore-hosts"); | 
 | } | 
 |  | 
 | /** | 
 |  * g_simple_proxy_resolver_set_uri_proxy: | 
 |  * @resolver: a #GSimpleProxyResolver | 
 |  * @uri_scheme: the URI scheme to add a proxy for | 
 |  * @proxy: the proxy to use for @uri_scheme | 
 |  * | 
 |  * Adds a URI-scheme-specific proxy to @resolver; URIs whose scheme | 
 |  * matches @uri_scheme (and which don't match | 
 |  * #GSimpleProxyResolver:ignore-hosts) will be proxied via @proxy. | 
 |  * | 
 |  * As with #GSimpleProxyResolver:default-proxy, if @proxy starts with | 
 |  * "socks://", #GSimpleProxyResolver will treat it | 
 |  * as referring to all three of the socks5, socks4a, and socks4 proxy | 
 |  * types. | 
 |  * | 
 |  * Since: 2.36 | 
 |  */ | 
 | void | 
 | g_simple_proxy_resolver_set_uri_proxy (GSimpleProxyResolver *resolver, | 
 |                                        const gchar          *uri_scheme, | 
 |                                        const gchar          *proxy) | 
 | { | 
 |   g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); | 
 |  | 
 |   g_hash_table_replace (resolver->priv->uri_proxies, | 
 |                         g_ascii_strdown (uri_scheme, -1), | 
 |                         g_strdup (proxy)); | 
 | } |