1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
3 | * Copyright 2010, 2013 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see |
17 | * <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | |
25 | #include "gsimpleproxyresolver.h" |
26 | #include "ginetaddress.h" |
27 | #include "ginetaddressmask.h" |
28 | #include "gnetworkingprivate.h" |
29 | #include "gtask.h" |
30 | |
31 | #include "glibintl.h" |
32 | |
33 | /** |
34 | * SECTION:gsimpleproxyresolver |
35 | * @short_description: Simple proxy resolver implementation |
36 | * @include: gio/gio.h |
37 | * @see_also: g_socket_client_set_proxy_resolver() |
38 | * |
39 | * #GSimpleProxyResolver is a simple #GProxyResolver implementation |
40 | * that handles a single default proxy, multiple URI-scheme-specific |
41 | * proxies, and a list of hosts that proxies should not be used for. |
42 | * |
43 | * #GSimpleProxyResolver is never the default proxy resolver, but it |
44 | * can be used as the base class for another proxy resolver |
45 | * implementation, or it can be created and used manually, such as |
46 | * with g_socket_client_set_proxy_resolver(). |
47 | * |
48 | * Since: 2.36 |
49 | */ |
50 | |
51 | typedef struct { |
52 | gchar *name; |
53 | gsize length; |
54 | gushort port; |
55 | } GSimpleProxyResolverDomain; |
56 | |
57 | struct _GSimpleProxyResolverPrivate { |
58 | gchar *default_proxy, **ignore_hosts; |
59 | GHashTable *uri_proxies; |
60 | |
61 | GPtrArray *ignore_ips; |
62 | GSimpleProxyResolverDomain *ignore_domains; |
63 | }; |
64 | |
65 | static void g_simple_proxy_resolver_iface_init (GProxyResolverInterface *iface); |
66 | |
67 | G_DEFINE_TYPE_WITH_CODE (GSimpleProxyResolver, g_simple_proxy_resolver, G_TYPE_OBJECT, |
68 | G_ADD_PRIVATE (GSimpleProxyResolver) |
69 | G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER, |
70 | g_simple_proxy_resolver_iface_init)) |
71 | |
72 | enum |
73 | { |
74 | PROP_0, |
75 | PROP_DEFAULT_PROXY, |
76 | PROP_IGNORE_HOSTS |
77 | }; |
78 | |
79 | static void reparse_ignore_hosts (GSimpleProxyResolver *resolver); |
80 | |
81 | static void |
82 | g_simple_proxy_resolver_finalize (GObject *object) |
83 | { |
84 | GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); |
85 | GSimpleProxyResolverPrivate *priv = resolver->priv; |
86 | |
87 | g_free (mem: priv->default_proxy); |
88 | g_hash_table_destroy (hash_table: priv->uri_proxies); |
89 | |
90 | g_clear_pointer (&priv->ignore_hosts, g_strfreev); |
91 | /* This will free ignore_ips and ignore_domains */ |
92 | reparse_ignore_hosts (resolver); |
93 | |
94 | G_OBJECT_CLASS (g_simple_proxy_resolver_parent_class)->finalize (object); |
95 | } |
96 | |
97 | static void |
98 | g_simple_proxy_resolver_init (GSimpleProxyResolver *resolver) |
99 | { |
100 | resolver->priv = g_simple_proxy_resolver_get_instance_private (self: resolver); |
101 | resolver->priv->uri_proxies = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, |
102 | key_destroy_func: g_free, value_destroy_func: g_free); |
103 | } |
104 | |
105 | static void |
106 | g_simple_proxy_resolver_set_property (GObject *object, |
107 | guint prop_id, |
108 | const GValue *value, |
109 | GParamSpec *pspec) |
110 | { |
111 | GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); |
112 | |
113 | switch (prop_id) |
114 | { |
115 | case PROP_DEFAULT_PROXY: |
116 | g_simple_proxy_resolver_set_default_proxy (resolver, default_proxy: g_value_get_string (value)); |
117 | break; |
118 | |
119 | case PROP_IGNORE_HOSTS: |
120 | g_simple_proxy_resolver_set_ignore_hosts (resolver, ignore_hosts: g_value_get_boxed (value)); |
121 | break; |
122 | |
123 | default: |
124 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
125 | } |
126 | } |
127 | |
128 | static void |
129 | g_simple_proxy_resolver_get_property (GObject *object, |
130 | guint prop_id, |
131 | GValue *value, |
132 | GParamSpec *pspec) |
133 | { |
134 | GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (object); |
135 | |
136 | switch (prop_id) |
137 | { |
138 | case PROP_DEFAULT_PROXY: |
139 | g_value_set_string (value, v_string: resolver->priv->default_proxy); |
140 | break; |
141 | |
142 | case PROP_IGNORE_HOSTS: |
143 | g_value_set_boxed (value, v_boxed: resolver->priv->ignore_hosts); |
144 | break; |
145 | |
146 | default: |
147 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
148 | } |
149 | } |
150 | |
151 | static void |
152 | reparse_ignore_hosts (GSimpleProxyResolver *resolver) |
153 | { |
154 | GSimpleProxyResolverPrivate *priv = resolver->priv; |
155 | GPtrArray *ignore_ips; |
156 | GArray *ignore_domains; |
157 | gchar *host, *tmp, *colon, *bracket; |
158 | GInetAddress *iaddr; |
159 | GInetAddressMask *mask; |
160 | GSimpleProxyResolverDomain domain; |
161 | gushort port; |
162 | int i; |
163 | |
164 | if (priv->ignore_ips) |
165 | g_ptr_array_free (array: priv->ignore_ips, TRUE); |
166 | if (priv->ignore_domains) |
167 | { |
168 | for (i = 0; priv->ignore_domains[i].name; i++) |
169 | g_free (mem: priv->ignore_domains[i].name); |
170 | g_free (mem: priv->ignore_domains); |
171 | } |
172 | priv->ignore_ips = NULL; |
173 | priv->ignore_domains = NULL; |
174 | |
175 | if (!priv->ignore_hosts || !priv->ignore_hosts[0]) |
176 | return; |
177 | |
178 | ignore_ips = g_ptr_array_new_with_free_func (element_free_func: g_object_unref); |
179 | ignore_domains = g_array_new (TRUE, FALSE, element_size: sizeof (GSimpleProxyResolverDomain)); |
180 | |
181 | for (i = 0; priv->ignore_hosts[i]; i++) |
182 | { |
183 | host = g_strchomp (string: priv->ignore_hosts[i]); |
184 | |
185 | /* See if it's an IP address or IP/length mask */ |
186 | mask = g_inet_address_mask_new_from_string (mask_string: host, NULL); |
187 | if (mask) |
188 | { |
189 | g_ptr_array_add (array: ignore_ips, data: mask); |
190 | continue; |
191 | } |
192 | |
193 | port = 0; |
194 | |
195 | if (*host == '[') |
196 | { |
197 | /* [IPv6]:port */ |
198 | host++; |
199 | bracket = strchr (s: host, c: ']'); |
200 | if (!bracket || !bracket[1] || bracket[1] != ':') |
201 | goto bad; |
202 | |
203 | port = strtoul (nptr: bracket + 2, endptr: &tmp, base: 10); |
204 | if (*tmp) |
205 | goto bad; |
206 | |
207 | *bracket = '\0'; |
208 | } |
209 | else |
210 | { |
211 | colon = strchr (s: host, c: ':'); |
212 | if (colon && !strchr (s: colon + 1, c: ':')) |
213 | { |
214 | /* hostname:port or IPv4:port */ |
215 | port = strtoul (nptr: colon + 1, endptr: &tmp, base: 10); |
216 | if (*tmp) |
217 | goto bad; |
218 | *colon = '\0'; |
219 | } |
220 | } |
221 | |
222 | iaddr = g_inet_address_new_from_string (string: host); |
223 | if (iaddr) |
224 | g_object_unref (object: iaddr); |
225 | else |
226 | { |
227 | if (g_str_has_prefix (str: host, prefix: "*." )) |
228 | host += 2; |
229 | else if (*host == '.') |
230 | host++; |
231 | } |
232 | |
233 | memset (s: &domain, c: 0, n: sizeof (domain)); |
234 | domain.name = g_strdup (str: host); |
235 | domain.length = strlen (s: domain.name); |
236 | domain.port = port; |
237 | g_array_append_val (ignore_domains, domain); |
238 | continue; |
239 | |
240 | bad: |
241 | g_warning ("Ignoring invalid ignore_hosts value '%s'" , host); |
242 | } |
243 | |
244 | if (ignore_ips->len) |
245 | priv->ignore_ips = ignore_ips; |
246 | else |
247 | g_ptr_array_free (array: ignore_ips, TRUE); |
248 | |
249 | if (ignore_domains->len) |
250 | priv->ignore_domains = (GSimpleProxyResolverDomain *)ignore_domains->data; |
251 | g_array_free (array: ignore_domains, free_segment: ignore_domains->len == 0); |
252 | } |
253 | |
254 | static gboolean |
255 | ignore_host (GSimpleProxyResolver *resolver, |
256 | const gchar *host, |
257 | gushort port) |
258 | { |
259 | GSimpleProxyResolverPrivate *priv = resolver->priv; |
260 | gchar *ascii_host = NULL; |
261 | gboolean ignore = FALSE; |
262 | gsize offset, length; |
263 | guint i; |
264 | |
265 | if (priv->ignore_ips) |
266 | { |
267 | GInetAddress *iaddr; |
268 | |
269 | iaddr = g_inet_address_new_from_string (string: host); |
270 | if (iaddr) |
271 | { |
272 | for (i = 0; i < priv->ignore_ips->len; i++) |
273 | { |
274 | GInetAddressMask *mask = priv->ignore_ips->pdata[i]; |
275 | |
276 | if (g_inet_address_mask_matches (mask, address: iaddr)) |
277 | { |
278 | ignore = TRUE; |
279 | break; |
280 | } |
281 | } |
282 | |
283 | g_object_unref (object: iaddr); |
284 | if (ignore) |
285 | return TRUE; |
286 | } |
287 | } |
288 | |
289 | if (priv->ignore_domains) |
290 | { |
291 | length = 0; |
292 | if (g_hostname_is_non_ascii (hostname: host)) |
293 | host = ascii_host = g_hostname_to_ascii (hostname: host); |
294 | if (host) |
295 | length = strlen (s: host); |
296 | |
297 | for (i = 0; length > 0 && priv->ignore_domains[i].length; i++) |
298 | { |
299 | GSimpleProxyResolverDomain *domain = &priv->ignore_domains[i]; |
300 | |
301 | if (domain->length > length) |
302 | continue; |
303 | |
304 | offset = length - domain->length; |
305 | if ((domain->port == 0 || domain->port == port) && |
306 | (offset == 0 || (offset > 0 && host[offset - 1] == '.')) && |
307 | (g_ascii_strcasecmp (s1: domain->name, s2: host + offset) == 0)) |
308 | { |
309 | ignore = TRUE; |
310 | break; |
311 | } |
312 | } |
313 | |
314 | g_free (mem: ascii_host); |
315 | } |
316 | |
317 | return ignore; |
318 | } |
319 | |
320 | static gchar ** |
321 | g_simple_proxy_resolver_lookup (GProxyResolver *proxy_resolver, |
322 | const gchar *uri, |
323 | GCancellable *cancellable, |
324 | GError **error) |
325 | { |
326 | GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (proxy_resolver); |
327 | GSimpleProxyResolverPrivate *priv = resolver->priv; |
328 | const gchar *proxy = NULL; |
329 | gchar **proxies; |
330 | |
331 | if (priv->ignore_ips || priv->ignore_domains) |
332 | { |
333 | gchar *host = NULL; |
334 | gint port; |
335 | |
336 | if (g_uri_split_network (uri_string: uri, flags: G_URI_FLAGS_NONE, NULL, |
337 | host: &host, port: &port, NULL) && |
338 | ignore_host (resolver, host, port: port > 0 ? port : 0)) |
339 | proxy = "direct://" ; |
340 | |
341 | g_free (mem: host); |
342 | } |
343 | |
344 | if (!proxy && g_hash_table_size (hash_table: priv->uri_proxies)) |
345 | { |
346 | gchar *scheme = g_ascii_strdown (str: uri, len: strcspn (s: uri, reject: ":" )); |
347 | |
348 | proxy = g_hash_table_lookup (hash_table: priv->uri_proxies, key: scheme); |
349 | g_free (mem: scheme); |
350 | } |
351 | |
352 | if (!proxy) |
353 | proxy = priv->default_proxy; |
354 | if (!proxy) |
355 | proxy = "direct://" ; |
356 | |
357 | if (!strncmp (s1: proxy, s2: "socks://" , n: 8)) |
358 | { |
359 | proxies = g_new0 (gchar *, 4); |
360 | proxies[0] = g_strdup_printf (format: "socks5://%s" , proxy + 8); |
361 | proxies[1] = g_strdup_printf (format: "socks4a://%s" , proxy + 8); |
362 | proxies[2] = g_strdup_printf (format: "socks4://%s" , proxy + 8); |
363 | proxies[3] = NULL; |
364 | } |
365 | else |
366 | { |
367 | proxies = g_new0 (gchar *, 2); |
368 | proxies[0] = g_strdup (str: proxy); |
369 | } |
370 | |
371 | return proxies; |
372 | } |
373 | |
374 | static void |
375 | g_simple_proxy_resolver_lookup_async (GProxyResolver *proxy_resolver, |
376 | const gchar *uri, |
377 | GCancellable *cancellable, |
378 | GAsyncReadyCallback callback, |
379 | gpointer user_data) |
380 | { |
381 | GSimpleProxyResolver *resolver = G_SIMPLE_PROXY_RESOLVER (proxy_resolver); |
382 | GTask *task; |
383 | GError *error = NULL; |
384 | char **proxies; |
385 | |
386 | task = g_task_new (source_object: resolver, cancellable, callback, callback_data: user_data); |
387 | g_task_set_source_tag (task, g_simple_proxy_resolver_lookup_async); |
388 | |
389 | proxies = g_simple_proxy_resolver_lookup (proxy_resolver, uri, |
390 | cancellable, error: &error); |
391 | if (proxies) |
392 | g_task_return_pointer (task, result: proxies, result_destroy: (GDestroyNotify)g_strfreev); |
393 | else |
394 | g_task_return_error (task, error); |
395 | g_object_unref (object: task); |
396 | } |
397 | |
398 | static gchar ** |
399 | g_simple_proxy_resolver_lookup_finish (GProxyResolver *resolver, |
400 | GAsyncResult *result, |
401 | GError **error) |
402 | { |
403 | g_return_val_if_fail (g_task_is_valid (result, resolver), NULL); |
404 | |
405 | return g_task_propagate_pointer (G_TASK (result), error); |
406 | } |
407 | |
408 | static void |
409 | g_simple_proxy_resolver_class_init (GSimpleProxyResolverClass *resolver_class) |
410 | { |
411 | GObjectClass *object_class = G_OBJECT_CLASS (resolver_class); |
412 | |
413 | object_class->get_property = g_simple_proxy_resolver_get_property; |
414 | object_class->set_property = g_simple_proxy_resolver_set_property; |
415 | object_class->finalize = g_simple_proxy_resolver_finalize; |
416 | |
417 | /** |
418 | * GSimpleProxyResolver:default-proxy: |
419 | * |
420 | * The default proxy URI that will be used for any URI that doesn't |
421 | * match #GSimpleProxyResolver:ignore-hosts, and doesn't match any |
422 | * of the schemes set with g_simple_proxy_resolver_set_uri_proxy(). |
423 | * |
424 | * Note that as a special case, if this URI starts with |
425 | * "socks://", #GSimpleProxyResolver will treat it as referring |
426 | * to all three of the socks5, socks4a, and socks4 proxy types. |
427 | */ |
428 | g_object_class_install_property (oclass: object_class, property_id: PROP_DEFAULT_PROXY, |
429 | pspec: g_param_spec_string (name: "default-proxy" , |
430 | P_("Default proxy" ), |
431 | P_("The default proxy URI" ), |
432 | NULL, |
433 | flags: G_PARAM_READWRITE | |
434 | G_PARAM_STATIC_STRINGS)); |
435 | |
436 | /** |
437 | * GSimpleProxyResolver:ignore-hosts: |
438 | * |
439 | * A list of hostnames and IP addresses that the resolver should |
440 | * allow direct connections to. |
441 | * |
442 | * Entries can be in one of 4 formats: |
443 | * |
444 | * - A hostname, such as "example.com", ".example.com", or |
445 | * "*.example.com", any of which match "example.com" or |
446 | * any subdomain of it. |
447 | * |
448 | * - An IPv4 or IPv6 address, such as "192.168.1.1", |
449 | * which matches only that address. |
450 | * |
451 | * - A hostname or IP address followed by a port, such as |
452 | * "example.com:80", which matches whatever the hostname or IP |
453 | * address would match, but only for URLs with the (explicitly) |
454 | * indicated port. In the case of an IPv6 address, the address |
455 | * part must appear in brackets: "[::1]:443" |
456 | * |
457 | * - An IP address range, given by a base address and prefix length, |
458 | * such as "fe80::/10", which matches any address in that range. |
459 | * |
460 | * Note that when dealing with Unicode hostnames, the matching is |
461 | * done against the ASCII form of the name. |
462 | * |
463 | * Also note that hostname exclusions apply only to connections made |
464 | * to hosts identified by name, and IP address exclusions apply only |
465 | * to connections made to hosts identified by address. That is, if |
466 | * example.com has an address of 192.168.1.1, and the :ignore-hosts list |
467 | * contains only "192.168.1.1", then a connection to "example.com" |
468 | * (eg, via a #GNetworkAddress) will use the proxy, and a connection to |
469 | * "192.168.1.1" (eg, via a #GInetSocketAddress) will not. |
470 | * |
471 | * These rules match the "ignore-hosts"/"noproxy" rules most |
472 | * commonly used by other applications. |
473 | */ |
474 | g_object_class_install_property (oclass: object_class, property_id: PROP_IGNORE_HOSTS, |
475 | pspec: g_param_spec_boxed (name: "ignore-hosts" , |
476 | P_("Ignore hosts" ), |
477 | P_("Hosts that will not use the proxy" ), |
478 | G_TYPE_STRV, |
479 | flags: G_PARAM_READWRITE | |
480 | G_PARAM_STATIC_STRINGS)); |
481 | |
482 | } |
483 | |
484 | static void |
485 | g_simple_proxy_resolver_iface_init (GProxyResolverInterface *iface) |
486 | { |
487 | iface->lookup = g_simple_proxy_resolver_lookup; |
488 | iface->lookup_async = g_simple_proxy_resolver_lookup_async; |
489 | iface->lookup_finish = g_simple_proxy_resolver_lookup_finish; |
490 | } |
491 | |
492 | /** |
493 | * g_simple_proxy_resolver_new: |
494 | * @default_proxy: (nullable): the default proxy to use, eg |
495 | * "socks://192.168.1.1" |
496 | * @ignore_hosts: (nullable): an optional list of hosts/IP addresses |
497 | * to not use a proxy for. |
498 | * |
499 | * Creates a new #GSimpleProxyResolver. See |
500 | * #GSimpleProxyResolver:default-proxy and |
501 | * #GSimpleProxyResolver:ignore-hosts for more details on how the |
502 | * arguments are interpreted. |
503 | * |
504 | * Returns: (transfer full) a new #GSimpleProxyResolver |
505 | * |
506 | * Since: 2.36 |
507 | */ |
508 | GProxyResolver * |
509 | g_simple_proxy_resolver_new (const gchar *default_proxy, |
510 | gchar **ignore_hosts) |
511 | { |
512 | return g_object_new (G_TYPE_SIMPLE_PROXY_RESOLVER, |
513 | first_property_name: "default-proxy" , default_proxy, |
514 | "ignore-hosts" , ignore_hosts, |
515 | NULL); |
516 | } |
517 | |
518 | /** |
519 | * g_simple_proxy_resolver_set_default_proxy: |
520 | * @resolver: a #GSimpleProxyResolver |
521 | * @default_proxy: the default proxy to use |
522 | * |
523 | * Sets the default proxy on @resolver, to be used for any URIs that |
524 | * don't match #GSimpleProxyResolver:ignore-hosts or a proxy set |
525 | * via g_simple_proxy_resolver_set_uri_proxy(). |
526 | * |
527 | * If @default_proxy starts with "socks://", |
528 | * #GSimpleProxyResolver will treat it as referring to all three of |
529 | * the socks5, socks4a, and socks4 proxy types. |
530 | * |
531 | * Since: 2.36 |
532 | */ |
533 | void |
534 | g_simple_proxy_resolver_set_default_proxy (GSimpleProxyResolver *resolver, |
535 | const gchar *default_proxy) |
536 | { |
537 | g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); |
538 | |
539 | g_free (mem: resolver->priv->default_proxy); |
540 | resolver->priv->default_proxy = g_strdup (str: default_proxy); |
541 | g_object_notify (G_OBJECT (resolver), property_name: "default-proxy" ); |
542 | } |
543 | |
544 | /** |
545 | * g_simple_proxy_resolver_set_ignore_hosts: |
546 | * @resolver: a #GSimpleProxyResolver |
547 | * @ignore_hosts: %NULL-terminated list of hosts/IP addresses |
548 | * to not use a proxy for |
549 | * |
550 | * Sets the list of ignored hosts. |
551 | * |
552 | * See #GSimpleProxyResolver:ignore-hosts for more details on how the |
553 | * @ignore_hosts argument is interpreted. |
554 | * |
555 | * Since: 2.36 |
556 | */ |
557 | void |
558 | g_simple_proxy_resolver_set_ignore_hosts (GSimpleProxyResolver *resolver, |
559 | gchar **ignore_hosts) |
560 | { |
561 | g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); |
562 | |
563 | g_strfreev (str_array: resolver->priv->ignore_hosts); |
564 | resolver->priv->ignore_hosts = g_strdupv (str_array: ignore_hosts); |
565 | reparse_ignore_hosts (resolver); |
566 | g_object_notify (G_OBJECT (resolver), property_name: "ignore-hosts" ); |
567 | } |
568 | |
569 | /** |
570 | * g_simple_proxy_resolver_set_uri_proxy: |
571 | * @resolver: a #GSimpleProxyResolver |
572 | * @uri_scheme: the URI scheme to add a proxy for |
573 | * @proxy: the proxy to use for @uri_scheme |
574 | * |
575 | * Adds a URI-scheme-specific proxy to @resolver; URIs whose scheme |
576 | * matches @uri_scheme (and which don't match |
577 | * #GSimpleProxyResolver:ignore-hosts) will be proxied via @proxy. |
578 | * |
579 | * As with #GSimpleProxyResolver:default-proxy, if @proxy starts with |
580 | * "socks://", #GSimpleProxyResolver will treat it |
581 | * as referring to all three of the socks5, socks4a, and socks4 proxy |
582 | * types. |
583 | * |
584 | * Since: 2.36 |
585 | */ |
586 | void |
587 | g_simple_proxy_resolver_set_uri_proxy (GSimpleProxyResolver *resolver, |
588 | const gchar *uri_scheme, |
589 | const gchar *proxy) |
590 | { |
591 | g_return_if_fail (G_IS_SIMPLE_PROXY_RESOLVER (resolver)); |
592 | |
593 | g_hash_table_replace (hash_table: resolver->priv->uri_proxies, |
594 | key: g_ascii_strdown (str: uri_scheme, len: -1), |
595 | value: g_strdup (str: proxy)); |
596 | } |
597 | |