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