1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2010 Collabora, Ltd.
5 * SPDX-License-Identifier: LGPL-2.1-or-later
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.
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.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
24 #include "gproxyaddressenumerator.h"
28 #include "gasyncresult.h"
29 #include "ginetaddress.h"
32 #include "glib-private.h"
33 #include "gnetworkaddress.h"
34 #include "gnetworkingprivate.h"
36 #include "gproxyaddress.h"
37 #include "gproxyresolver.h"
39 #include "gresolver.h"
40 #include "gsocketaddress.h"
41 #include "gsocketaddressenumerator.h"
42 #include "gsocketconnectable.h"
45 * SECTION:gproxyaddressenumerator
46 * @short_description: Proxy wrapper enumerator for socket addresses
49 * #GProxyAddressEnumerator is a wrapper around #GSocketAddressEnumerator which
50 * takes the #GSocketAddress instances returned by the #GSocketAddressEnumerator
51 * and wraps them in #GProxyAddress instances, using the given
52 * #GProxyAddressEnumerator:proxy-resolver.
54 * This enumerator will be returned (for example, by
55 * g_socket_connectable_enumerate()) as appropriate when a proxy is configured;
56 * there should be no need to manually wrap a #GSocketAddressEnumerator instance
60 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
71 struct _GProxyAddressEnumeratorPrivate
73 /* Destination address */
74 GSocketConnectable *connectable;
81 /* Proxy enumeration */
82 GProxyResolver *proxy_resolver;
85 GSocketAddressEnumerator *addr_enum;
86 GSocketAddress *proxy_address;
87 const gchar *proxy_uri;
89 gchar *proxy_username;
90 gchar *proxy_password;
91 gboolean supports_hostname;
95 /* ever_enumerated is TRUE after we've returned a result for the first time
96 * via g_proxy_address_enumerator_next() or _next_async(). If FALSE, we have
97 * never returned yet, and should return an error if returning NULL because
98 * it does not make sense for a proxy resolver to return NULL except on error.
99 * (Whereas a DNS resolver would return NULL with no error to indicate "no
100 * results", a proxy resolver would want to return "direct://" instead, so
101 * NULL without error does not make sense for us.)
103 * But if ever_enumerated is TRUE, then we must not report any further errors
104 * (except for G_IO_ERROR_CANCELLED), because this is an API contract of
105 * GSocketAddressEnumerator.
107 gboolean ever_enumerated;
110 G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
113 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
116 g_clear_pointer (&priv->proxy_username, g_free);
117 g_clear_pointer (&priv->proxy_password, g_free);
119 g_uri_split_with_user (proxy, G_URI_FLAGS_HAS_PASSWORD, NULL,
120 &priv->proxy_username, &priv->proxy_password,
121 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
125 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
127 if (priv->proxy_address)
130 while (priv->addr_enum == NULL && *priv->next_proxy)
132 GSocketConnectable *connectable = NULL;
135 priv->proxy_uri = *priv->next_proxy++;
136 g_free (priv->proxy_type);
137 priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
139 if (priv->proxy_type == NULL)
142 /* Assumes hostnames are supported for unknown protocols */
143 priv->supports_hostname = TRUE;
144 proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
147 priv->supports_hostname = g_proxy_supports_hostname (proxy);
148 g_object_unref (proxy);
151 if (strcmp ("direct", priv->proxy_type) == 0)
153 if (priv->connectable)
154 connectable = g_object_ref (priv->connectable);
156 connectable = g_network_address_new (priv->dest_hostname,
161 GError *error = NULL;
164 default_port = GLIB_PRIVATE_CALL (g_uri_get_default_scheme_port) (priv->proxy_type);
165 if (default_port == -1)
168 connectable = g_network_address_parse_uri (priv->proxy_uri, default_port, &error);
171 g_warning ("Invalid proxy URI '%s': %s",
172 priv->proxy_uri, error->message);
173 g_error_free (error);
176 save_userinfo (priv, priv->proxy_uri);
181 priv->addr_enum = g_socket_connectable_enumerate (connectable);
182 g_object_unref (connectable);
187 static GSocketAddress *
188 g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
189 GCancellable *cancellable,
192 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
193 GSocketAddress *result = NULL;
194 GError *first_error = NULL;
196 if (!priv->ever_enumerated)
198 g_assert (priv->proxies == NULL);
199 priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
203 priv->next_proxy = priv->proxies;
205 if (priv->proxies == NULL)
207 priv->ever_enumerated = TRUE;
212 while (result == NULL && (*priv->next_proxy || priv->addr_enum))
214 gchar *dest_hostname;
215 gchar *dest_protocol;
216 GInetSocketAddress *inetsaddr;
217 GInetAddress *inetaddr;
220 next_enumerator (priv);
222 if (!priv->addr_enum)
225 if (priv->proxy_address == NULL)
227 priv->proxy_address = g_socket_address_enumerator_next (
230 first_error ? NULL : &first_error);
233 if (priv->proxy_address == NULL)
235 g_object_unref (priv->addr_enum);
236 priv->addr_enum = NULL;
240 g_resolver_free_addresses (priv->dest_ips);
241 priv->dest_ips = NULL;
247 if (strcmp ("direct", priv->proxy_type) == 0)
249 result = priv->proxy_address;
250 priv->proxy_address = NULL;
254 if (!priv->supports_hostname)
256 GInetAddress *dest_ip;
262 resolver = g_resolver_get_default();
263 priv->dest_ips = g_resolver_lookup_by_name (resolver,
266 first_error ? NULL : &first_error);
267 g_object_unref (resolver);
271 g_object_unref (priv->proxy_address);
272 priv->proxy_address = NULL;
277 if (!priv->next_dest_ip)
278 priv->next_dest_ip = priv->dest_ips;
280 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
281 dest_hostname = g_inet_address_to_string (dest_ip);
283 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
287 dest_hostname = g_strdup (priv->dest_hostname);
289 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
291 if (!G_IS_INET_SOCKET_ADDRESS (priv->proxy_address))
293 g_free (dest_hostname);
294 g_free (dest_protocol);
296 g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address), NULL);
298 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
299 inetaddr = g_inet_socket_address_get_address (inetsaddr);
300 port = g_inet_socket_address_get_port (inetsaddr);
302 result = g_object_new (G_TYPE_PROXY_ADDRESS,
305 "protocol", priv->proxy_type,
306 "destination-protocol", dest_protocol,
307 "destination-hostname", dest_hostname,
308 "destination-port", priv->dest_port,
309 "username", priv->proxy_username,
310 "password", priv->proxy_password,
311 "uri", priv->proxy_uri,
313 g_free (dest_hostname);
314 g_free (dest_protocol);
316 if (priv->supports_hostname || priv->next_dest_ip == NULL)
318 g_object_unref (priv->proxy_address);
319 priv->proxy_address = NULL;
323 if (result == NULL && first_error && (!priv->ever_enumerated || g_error_matches (first_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
324 g_propagate_error (error, first_error);
325 else if (first_error)
326 g_error_free (first_error);
328 if (result == NULL && error != NULL && *error == NULL && !priv->ever_enumerated)
329 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unspecified proxy lookup failure"));
331 priv->ever_enumerated = TRUE;
337 complete_async (GTask *task)
339 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
341 if (priv->last_error && (!priv->ever_enumerated || g_error_matches (priv->last_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
343 g_task_return_error (task, priv->last_error);
344 priv->last_error = NULL;
346 else if (!priv->ever_enumerated)
347 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unspecified proxy lookup failure"));
349 g_task_return_pointer (task, NULL, NULL);
351 priv->ever_enumerated = TRUE;
353 g_clear_error (&priv->last_error);
354 g_object_unref (task);
358 return_result (GTask *task)
360 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
361 GSocketAddress *result;
362 gboolean is_inet_socket_address;
364 if (strcmp ("direct", priv->proxy_type) == 0)
366 result = priv->proxy_address;
367 priv->proxy_address = NULL;
371 gchar *dest_hostname, *dest_protocol;
372 GInetSocketAddress *inetsaddr;
373 GInetAddress *inetaddr;
376 if (!priv->supports_hostname)
378 GInetAddress *dest_ip;
380 if (!priv->next_dest_ip)
381 priv->next_dest_ip = priv->dest_ips;
383 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
384 dest_hostname = g_inet_address_to_string (dest_ip);
386 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
390 dest_hostname = g_strdup (priv->dest_hostname);
392 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
394 is_inet_socket_address = G_IS_INET_SOCKET_ADDRESS (priv->proxy_address);
395 if (!is_inet_socket_address)
397 g_free (dest_hostname);
398 g_free (dest_protocol);
400 g_return_if_fail (is_inet_socket_address);
402 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
403 inetaddr = g_inet_socket_address_get_address (inetsaddr);
404 port = g_inet_socket_address_get_port (inetsaddr);
406 result = g_object_new (G_TYPE_PROXY_ADDRESS,
409 "protocol", priv->proxy_type,
410 "destination-protocol", dest_protocol,
411 "destination-hostname", dest_hostname,
412 "destination-port", priv->dest_port,
413 "username", priv->proxy_username,
414 "password", priv->proxy_password,
415 "uri", priv->proxy_uri,
417 g_free (dest_hostname);
418 g_free (dest_protocol);
420 if (priv->supports_hostname || priv->next_dest_ip == NULL)
422 g_object_unref (priv->proxy_address);
423 priv->proxy_address = NULL;
427 priv->ever_enumerated = TRUE;
428 g_task_return_pointer (task, result, g_object_unref);
429 g_object_unref (task);
432 static void address_enumerate_cb (GObject *object,
433 GAsyncResult *result,
437 next_proxy (GTask *task)
439 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
441 if (*priv->next_proxy)
443 g_object_unref (priv->addr_enum);
444 priv->addr_enum = NULL;
448 g_resolver_free_addresses (priv->dest_ips);
449 priv->dest_ips = NULL;
452 next_enumerator (priv);
456 g_socket_address_enumerator_next_async (priv->addr_enum,
457 g_task_get_cancellable (task),
458 address_enumerate_cb,
464 complete_async (task);
468 dest_hostname_lookup_cb (GObject *object,
469 GAsyncResult *result,
472 GTask *task = user_data;
473 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
475 g_clear_error (&priv->last_error);
476 priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
480 return_result (task);
483 g_clear_object (&priv->proxy_address);
489 address_enumerate_cb (GObject *object,
490 GAsyncResult *result,
493 GTask *task = user_data;
494 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
496 g_clear_error (&priv->last_error);
497 priv->proxy_address =
498 g_socket_address_enumerator_next_finish (priv->addr_enum,
501 if (priv->proxy_address)
503 if (!priv->supports_hostname && !priv->dest_ips)
506 resolver = g_resolver_get_default();
507 g_resolver_lookup_by_name_async (resolver,
509 g_task_get_cancellable (task),
510 dest_hostname_lookup_cb,
512 g_object_unref (resolver);
516 return_result (task);
523 proxy_lookup_cb (GObject *object,
524 GAsyncResult *result,
527 GTask *task = user_data;
528 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
530 g_clear_error (&priv->last_error);
531 priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
534 priv->next_proxy = priv->proxies;
536 if (priv->last_error)
538 complete_async (task);
543 next_enumerator (priv);
546 g_socket_address_enumerator_next_async (priv->addr_enum,
547 g_task_get_cancellable (task),
548 address_enumerate_cb,
554 complete_async (task);
558 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
559 GCancellable *cancellable,
560 GAsyncReadyCallback callback,
563 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
566 task = g_task_new (enumerator, cancellable, callback, user_data);
567 g_task_set_source_tag (task, g_proxy_address_enumerator_next_async);
568 g_task_set_task_data (task, priv, NULL);
570 if (priv->proxies == NULL)
572 g_proxy_resolver_lookup_async (priv->proxy_resolver,
582 if (priv->proxy_address)
584 return_result (task);
589 g_socket_address_enumerator_next_async (priv->addr_enum,
591 address_enumerate_cb,
597 complete_async (task);
600 static GSocketAddress *
601 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
602 GAsyncResult *result,
605 g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
607 return g_task_propagate_pointer (G_TASK (result), error);
611 g_proxy_address_enumerator_constructed (GObject *object)
613 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
614 GSocketConnectable *conn;
619 conn = g_network_address_parse_uri (priv->dest_uri, priv->default_port, NULL);
623 "hostname", &priv->dest_hostname,
626 priv->dest_port = port;
628 g_object_unref (conn);
631 g_warning ("Invalid URI '%s'", priv->dest_uri);
634 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->constructed (object);
638 g_proxy_address_enumerator_get_property (GObject *object,
643 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
647 g_value_set_string (value, priv->dest_uri);
650 case PROP_DEFAULT_PORT:
651 g_value_set_uint (value, priv->default_port);
654 case PROP_CONNECTABLE:
655 g_value_set_object (value, priv->connectable);
658 case PROP_PROXY_RESOLVER:
659 g_value_set_object (value, priv->proxy_resolver);
663 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
668 g_proxy_address_enumerator_set_property (GObject *object,
673 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
677 priv->dest_uri = g_value_dup_string (value);
680 case PROP_DEFAULT_PORT:
681 priv->default_port = g_value_get_uint (value);
684 case PROP_CONNECTABLE:
685 priv->connectable = g_value_dup_object (value);
688 case PROP_PROXY_RESOLVER:
689 if (priv->proxy_resolver)
690 g_object_unref (priv->proxy_resolver);
691 priv->proxy_resolver = g_value_get_object (value);
692 if (!priv->proxy_resolver)
693 priv->proxy_resolver = g_proxy_resolver_get_default ();
694 g_object_ref (priv->proxy_resolver);
698 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
703 g_proxy_address_enumerator_finalize (GObject *object)
705 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
707 if (priv->connectable)
708 g_object_unref (priv->connectable);
710 if (priv->proxy_resolver)
711 g_object_unref (priv->proxy_resolver);
713 g_free (priv->dest_uri);
714 g_free (priv->dest_hostname);
717 g_resolver_free_addresses (priv->dest_ips);
719 g_strfreev (priv->proxies);
722 g_object_unref (priv->addr_enum);
724 g_free (priv->proxy_type);
725 g_free (priv->proxy_username);
726 g_free (priv->proxy_password);
728 g_clear_error (&priv->last_error);
730 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
734 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
736 self->priv = g_proxy_address_enumerator_get_instance_private (self);
740 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
742 GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
743 GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
745 object_class->constructed = g_proxy_address_enumerator_constructed;
746 object_class->set_property = g_proxy_address_enumerator_set_property;
747 object_class->get_property = g_proxy_address_enumerator_get_property;
748 object_class->finalize = g_proxy_address_enumerator_finalize;
750 enumerator_class->next = g_proxy_address_enumerator_next;
751 enumerator_class->next_async = g_proxy_address_enumerator_next_async;
752 enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
754 g_object_class_install_property (object_class,
756 g_param_spec_string ("uri",
758 P_("The destination URI, use none:// for generic socket"),
761 G_PARAM_CONSTRUCT_ONLY |
762 G_PARAM_STATIC_STRINGS));
765 * GProxyAddressEnumerator:default-port:
767 * The default port to use if #GProxyAddressEnumerator:uri does not
772 g_object_class_install_property (object_class,
774 g_param_spec_uint ("default-port",
776 P_("The default port to use if uri does not specify one"),
779 G_PARAM_CONSTRUCT_ONLY |
780 G_PARAM_STATIC_STRINGS));
782 g_object_class_install_property (object_class,
784 g_param_spec_object ("connectable",
786 P_("The connectable being enumerated."),
787 G_TYPE_SOCKET_CONNECTABLE,
789 G_PARAM_CONSTRUCT_ONLY |
790 G_PARAM_STATIC_STRINGS));
793 * GProxyAddressEnumerator:proxy-resolver:
795 * The proxy resolver to use.
799 g_object_class_install_property (object_class,
801 g_param_spec_object ("proxy-resolver",
802 P_("Proxy resolver"),
803 P_("The proxy resolver to use."),
804 G_TYPE_PROXY_RESOLVER,
807 G_PARAM_STATIC_STRINGS));