1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2010 Collabora, Ltd.
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 <http://www.gnu.org/licenses/>.
19 *
20 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
21 */
22
23 #include "config.h"
24 #include "gproxyaddressenumerator.h"
25
26 #include <string.h>
27
28 #include "gasyncresult.h"
29 #include "ginetaddress.h"
30 #include "gioerror.h"
31 #include "glibintl.h"
32 #include "glib-private.h"
33 #include "gnetworkaddress.h"
34 #include "gnetworkingprivate.h"
35 #include "gproxy.h"
36 #include "gproxyaddress.h"
37 #include "gproxyresolver.h"
38 #include "gtask.h"
39 #include "gresolver.h"
40 #include "gsocketaddress.h"
41 #include "gsocketaddressenumerator.h"
42 #include "gsocketconnectable.h"
43
44 /**
45 * GProxyAddressEnumerator:
46 *
47 * `GProxyAddressEnumerator` is a wrapper around
48 * [class@Gio.SocketAddressEnumerator] which takes the [class@Gio.SocketAddress]
49 * instances returned by the [class@Gio.SocketAddressEnumerator]
50 * and wraps them in [class@Gio.ProxyAddress] instances, using the given
51 * [property@Gio.ProxyAddressEnumerator:proxy-resolver].
52 *
53 * This enumerator will be returned (for example, by
54 * [method@Gio.SocketConnectable.enumerate]) as appropriate when a proxy is
55 * configured; there should be no need to manually wrap a
56 * [class@Gio.SocketAddressEnumerator] instance with one.
57 */
58
59 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
60
61 enum
62 {
63 PROP_0,
64 PROP_URI,
65 PROP_DEFAULT_PORT,
66 PROP_CONNECTABLE,
67 PROP_PROXY_RESOLVER
68 };
69
70 struct _GProxyAddressEnumeratorPrivate
71 {
72 /* Destination address */
73 GSocketConnectable *connectable;
74 gchar *dest_uri;
75 guint16 default_port;
76 gchar *dest_hostname;
77 guint16 dest_port;
78 GList *dest_ips;
79
80 /* Proxy enumeration */
81 GProxyResolver *proxy_resolver;
82 gchar **proxies;
83 gchar **next_proxy;
84 GSocketAddressEnumerator *addr_enum;
85 GSocketAddress *proxy_address;
86 const gchar *proxy_uri;
87 gchar *proxy_type;
88 gchar *proxy_username;
89 gchar *proxy_password;
90 gboolean supports_hostname;
91 GList *next_dest_ip;
92 GError *last_error;
93
94 /* ever_enumerated is TRUE after we've returned a result for the first time
95 * via g_proxy_address_enumerator_next() or _next_async(). If FALSE, we have
96 * never returned yet, and should return an error if returning NULL because
97 * it does not make sense for a proxy resolver to return NULL except on error.
98 * (Whereas a DNS resolver would return NULL with no error to indicate "no
99 * results", a proxy resolver would want to return "direct://" instead, so
100 * NULL without error does not make sense for us.)
101 *
102 * But if ever_enumerated is TRUE, then we must not report any further errors
103 * (except for G_IO_ERROR_CANCELLED), because this is an API contract of
104 * GSocketAddressEnumerator.
105 */
106 gboolean ever_enumerated;
107 };
108
109 G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
110
111 static void
112 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
113 const gchar *proxy)
114 {
115 g_clear_pointer (&priv->proxy_username, g_free);
116 g_clear_pointer (&priv->proxy_password, g_free);
117
118 g_uri_split_with_user (proxy, G_URI_FLAGS_HAS_PASSWORD, NULL,
119 &priv->proxy_username, &priv->proxy_password,
120 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
121 }
122
123 static void
124 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
125 {
126 if (priv->proxy_address)
127 return;
128
129 while (priv->addr_enum == NULL && *priv->next_proxy)
130 {
131 GSocketConnectable *connectable = NULL;
132 GProxy *proxy;
133
134 priv->proxy_uri = *priv->next_proxy++;
135 g_free (priv->proxy_type);
136 priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
137
138 if (priv->proxy_type == NULL)
139 continue;
140
141 /* Assumes hostnames are supported for unknown protocols */
142 priv->supports_hostname = TRUE;
143 proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
144 if (proxy)
145 {
146 priv->supports_hostname = g_proxy_supports_hostname (proxy);
147 g_object_unref (proxy);
148 }
149
150 if (strcmp ("direct", priv->proxy_type) == 0)
151 {
152 if (priv->connectable)
153 connectable = g_object_ref (priv->connectable);
154 else
155 connectable = g_network_address_new (priv->dest_hostname,
156 priv->dest_port);
157 }
158 else
159 {
160 GError *error = NULL;
161 int default_port;
162
163 default_port = GLIB_PRIVATE_CALL (g_uri_get_default_scheme_port) (priv->proxy_type);
164 if (default_port == -1)
165 default_port = 0;
166
167 connectable = g_network_address_parse_uri (priv->proxy_uri, default_port, &error);
168 if (error)
169 {
170 g_warning ("Invalid proxy URI '%s': %s",
171 priv->proxy_uri, error->message);
172 g_error_free (error);
173 }
174
175 save_userinfo (priv, priv->proxy_uri);
176 }
177
178 if (connectable)
179 {
180 priv->addr_enum = g_socket_connectable_enumerate (connectable);
181 g_object_unref (connectable);
182 }
183 }
184 }
185
186 static GSocketAddress *
187 g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
188 GCancellable *cancellable,
189 GError **error)
190 {
191 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
192 GSocketAddress *result = NULL;
193 GError *first_error = NULL;
194
195 if (!priv->ever_enumerated)
196 {
197 g_assert (priv->proxies == NULL);
198 priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
199 priv->dest_uri,
200 cancellable,
201 error);
202 priv->next_proxy = priv->proxies;
203
204 if (priv->proxies == NULL)
205 {
206 priv->ever_enumerated = TRUE;
207 return NULL;
208 }
209 }
210
211 while (result == NULL && (*priv->next_proxy || priv->addr_enum))
212 {
213 gchar *dest_hostname;
214 gchar *dest_protocol;
215 GInetSocketAddress *inetsaddr;
216 GInetAddress *inetaddr;
217 guint16 port;
218
219 next_enumerator (priv);
220
221 if (!priv->addr_enum)
222 continue;
223
224 if (priv->proxy_address == NULL)
225 {
226 priv->proxy_address = g_socket_address_enumerator_next (
227 priv->addr_enum,
228 cancellable,
229 first_error ? NULL : &first_error);
230 }
231
232 if (priv->proxy_address == NULL)
233 {
234 g_object_unref (priv->addr_enum);
235 priv->addr_enum = NULL;
236
237 if (priv->dest_ips)
238 {
239 g_resolver_free_addresses (priv->dest_ips);
240 priv->dest_ips = NULL;
241 }
242
243 continue;
244 }
245
246 if (strcmp ("direct", priv->proxy_type) == 0)
247 {
248 result = priv->proxy_address;
249 priv->proxy_address = NULL;
250 continue;
251 }
252
253 if (!priv->supports_hostname)
254 {
255 GInetAddress *dest_ip;
256
257 if (!priv->dest_ips)
258 {
259 GResolver *resolver;
260
261 resolver = g_resolver_get_default();
262 priv->dest_ips = g_resolver_lookup_by_name (resolver,
263 priv->dest_hostname,
264 cancellable,
265 first_error ? NULL : &first_error);
266 g_object_unref (resolver);
267
268 if (!priv->dest_ips)
269 {
270 g_object_unref (priv->proxy_address);
271 priv->proxy_address = NULL;
272 continue;
273 }
274 }
275
276 if (!priv->next_dest_ip)
277 priv->next_dest_ip = priv->dest_ips;
278
279 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
280 dest_hostname = g_inet_address_to_string (dest_ip);
281
282 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
283 }
284 else
285 {
286 dest_hostname = g_strdup (priv->dest_hostname);
287 }
288 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
289
290 if (!G_IS_INET_SOCKET_ADDRESS (priv->proxy_address))
291 {
292 g_free (dest_hostname);
293 g_free (dest_protocol);
294 }
295 g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address), NULL);
296
297 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
298 inetaddr = g_inet_socket_address_get_address (inetsaddr);
299 port = g_inet_socket_address_get_port (inetsaddr);
300
301 result = g_object_new (G_TYPE_PROXY_ADDRESS,
302 "address", inetaddr,
303 "port", port,
304 "protocol", priv->proxy_type,
305 "destination-protocol", dest_protocol,
306 "destination-hostname", dest_hostname,
307 "destination-port", priv->dest_port,
308 "username", priv->proxy_username,
309 "password", priv->proxy_password,
310 "uri", priv->proxy_uri,
311 NULL);
312 g_free (dest_hostname);
313 g_free (dest_protocol);
314
315 if (priv->supports_hostname || priv->next_dest_ip == NULL)
316 {
317 g_object_unref (priv->proxy_address);
318 priv->proxy_address = NULL;
319 }
320 }
321
322 if (result == NULL && first_error && (!priv->ever_enumerated || g_error_matches (first_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
323 g_propagate_error (error, first_error);
324 else if (first_error)
325 g_error_free (first_error);
326
327 if (result == NULL && error != NULL && *error == NULL && !priv->ever_enumerated)
328 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unspecified proxy lookup failure"));
329
330 priv->ever_enumerated = TRUE;
331
332 return result;
333 }
334
335 static void
336 complete_async (GTask *task)
337 {
338 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
339
340 if (priv->last_error && (!priv->ever_enumerated || g_error_matches (priv->last_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
341 {
342 g_task_return_error (task, priv->last_error);
343 priv->last_error = NULL;
344 }
345 else if (!priv->ever_enumerated)
346 {
347 g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_FAILED,
348 _("Unspecified proxy lookup failure"));
349 }
350 else
351 {
352 g_task_return_pointer (task, NULL, NULL);
353 }
354
355 priv->ever_enumerated = TRUE;
356
357 g_clear_error (&priv->last_error);
358 g_object_unref (task);
359 }
360
361 static void
362 return_result (GTask *task)
363 {
364 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
365 GSocketAddress *result;
366 gboolean is_inet_socket_address;
367
368 if (strcmp ("direct", priv->proxy_type) == 0)
369 {
370 result = priv->proxy_address;
371 priv->proxy_address = NULL;
372 }
373 else
374 {
375 gchar *dest_hostname, *dest_protocol;
376 GInetSocketAddress *inetsaddr;
377 GInetAddress *inetaddr;
378 guint16 port;
379
380 if (!priv->supports_hostname)
381 {
382 GInetAddress *dest_ip;
383
384 if (!priv->next_dest_ip)
385 priv->next_dest_ip = priv->dest_ips;
386
387 dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
388 dest_hostname = g_inet_address_to_string (dest_ip);
389
390 priv->next_dest_ip = g_list_next (priv->next_dest_ip);
391 }
392 else
393 {
394 dest_hostname = g_strdup (priv->dest_hostname);
395 }
396 dest_protocol = g_uri_parse_scheme (priv->dest_uri);
397
398 is_inet_socket_address = G_IS_INET_SOCKET_ADDRESS (priv->proxy_address);
399 if (!is_inet_socket_address)
400 {
401 g_free (dest_hostname);
402 g_free (dest_protocol);
403 }
404 g_return_if_fail (is_inet_socket_address);
405
406 inetsaddr = G_INET_SOCKET_ADDRESS (priv->proxy_address);
407 inetaddr = g_inet_socket_address_get_address (inetsaddr);
408 port = g_inet_socket_address_get_port (inetsaddr);
409
410 result = g_object_new (G_TYPE_PROXY_ADDRESS,
411 "address", inetaddr,
412 "port", port,
413 "protocol", priv->proxy_type,
414 "destination-protocol", dest_protocol,
415 "destination-hostname", dest_hostname,
416 "destination-port", priv->dest_port,
417 "username", priv->proxy_username,
418 "password", priv->proxy_password,
419 "uri", priv->proxy_uri,
420 NULL);
421 g_free (dest_hostname);
422 g_free (dest_protocol);
423
424 if (priv->supports_hostname || priv->next_dest_ip == NULL)
425 {
426 g_object_unref (priv->proxy_address);
427 priv->proxy_address = NULL;
428 }
429 }
430
431 priv->ever_enumerated = TRUE;
432 g_task_return_pointer (task, result, g_object_unref);
433 g_object_unref (task);
434 }
435
436 static void address_enumerate_cb (GObject *object,
437 GAsyncResult *result,
438 gpointer user_data);
439
440 static void
441 next_proxy (GTask *task)
442 {
443 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
444
445 if (*priv->next_proxy)
446 {
447 g_object_unref (priv->addr_enum);
448 priv->addr_enum = NULL;
449
450 if (priv->dest_ips)
451 {
452 g_resolver_free_addresses (priv->dest_ips);
453 priv->dest_ips = NULL;
454 }
455
456 next_enumerator (priv);
457
458 if (priv->addr_enum)
459 {
460 g_socket_address_enumerator_next_async (priv->addr_enum,
461 g_task_get_cancellable (task),
462 address_enumerate_cb,
463 task);
464 return;
465 }
466 }
467
468 complete_async (task);
469 }
470
471 static void
472 dest_hostname_lookup_cb (GObject *object,
473 GAsyncResult *result,
474 gpointer user_data)
475 {
476 GTask *task = user_data;
477 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
478
479 g_clear_error (&priv->last_error);
480 priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
481 result,
482 &priv->last_error);
483 if (priv->dest_ips)
484 return_result (task);
485 else
486 {
487 g_clear_object (&priv->proxy_address);
488 next_proxy (task);
489 }
490 }
491
492 static void
493 address_enumerate_cb (GObject *object,
494 GAsyncResult *result,
495 gpointer user_data)
496 {
497 GTask *task = user_data;
498 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
499
500 g_clear_error (&priv->last_error);
501 priv->proxy_address =
502 g_socket_address_enumerator_next_finish (priv->addr_enum,
503 result,
504 &priv->last_error);
505 if (priv->proxy_address)
506 {
507 if (!priv->supports_hostname && !priv->dest_ips)
508 {
509 GResolver *resolver;
510 resolver = g_resolver_get_default();
511 g_resolver_lookup_by_name_async (resolver,
512 priv->dest_hostname,
513 g_task_get_cancellable (task),
514 dest_hostname_lookup_cb,
515 task);
516 g_object_unref (resolver);
517 return;
518 }
519
520 return_result (task);
521 }
522 else
523 next_proxy (task);
524 }
525
526 static void
527 proxy_lookup_cb (GObject *object,
528 GAsyncResult *result,
529 gpointer user_data)
530 {
531 GTask *task = user_data;
532 GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
533
534 g_clear_error (&priv->last_error);
535 priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
536 result,
537 &priv->last_error);
538 priv->next_proxy = priv->proxies;
539
540 if (priv->last_error)
541 {
542 complete_async (task);
543 return;
544 }
545 else
546 {
547 next_enumerator (priv);
548 if (priv->addr_enum)
549 {
550 g_socket_address_enumerator_next_async (priv->addr_enum,
551 g_task_get_cancellable (task),
552 address_enumerate_cb,
553 task);
554 return;
555 }
556 }
557
558 complete_async (task);
559 }
560
561 static void
562 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
563 GCancellable *cancellable,
564 GAsyncReadyCallback callback,
565 gpointer user_data)
566 {
567 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
568 GTask *task;
569
570 task = g_task_new (enumerator, cancellable, callback, user_data);
571 g_task_set_source_tag (task, g_proxy_address_enumerator_next_async);
572 g_task_set_task_data (task, priv, NULL);
573
574 if (priv->proxies == NULL)
575 {
576 g_proxy_resolver_lookup_async (priv->proxy_resolver,
577 priv->dest_uri,
578 cancellable,
579 proxy_lookup_cb,
580 task);
581 return;
582 }
583
584 if (priv->addr_enum)
585 {
586 if (priv->proxy_address)
587 {
588 return_result (task);
589 return;
590 }
591 else
592 {
593 g_socket_address_enumerator_next_async (priv->addr_enum,
594 cancellable,
595 address_enumerate_cb,
596 task);
597 return;
598 }
599 }
600
601 complete_async (task);
602 }
603
604 static GSocketAddress *
605 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
606 GAsyncResult *result,
607 GError **error)
608 {
609 g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
610
611 return g_task_propagate_pointer (G_TASK (result), error);
612 }
613
614 static void
615 g_proxy_address_enumerator_constructed (GObject *object)
616 {
617 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
618 GSocketConnectable *conn;
619 guint port;
620
621 if (priv->dest_uri)
622 {
623 conn = g_network_address_parse_uri (priv->dest_uri, priv->default_port, NULL);
624 if (conn)
625 {
626 g_object_get (conn,
627 "hostname", &priv->dest_hostname,
628 "port", &port,
629 NULL);
630 priv->dest_port = port;
631
632 g_object_unref (conn);
633 }
634 else
635 g_warning ("Invalid URI '%s'", priv->dest_uri);
636 }
637
638 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->constructed (object);
639 }
640
641 static void
642 g_proxy_address_enumerator_get_property (GObject *object,
643 guint property_id,
644 GValue *value,
645 GParamSpec *pspec)
646 {
647 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
648 switch (property_id)
649 {
650 case PROP_URI:
651 g_value_set_string (value, priv->dest_uri);
652 break;
653
654 case PROP_DEFAULT_PORT:
655 g_value_set_uint (value, priv->default_port);
656 break;
657
658 case PROP_CONNECTABLE:
659 g_value_set_object (value, priv->connectable);
660 break;
661
662 case PROP_PROXY_RESOLVER:
663 g_value_set_object (value, priv->proxy_resolver);
664 break;
665
666 default:
667 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
668 }
669 }
670
671 static void
672 g_proxy_address_enumerator_set_property (GObject *object,
673 guint property_id,
674 const GValue *value,
675 GParamSpec *pspec)
676 {
677 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
678 switch (property_id)
679 {
680 case PROP_URI:
681 priv->dest_uri = g_value_dup_string (value);
682 break;
683
684 case PROP_DEFAULT_PORT:
685 priv->default_port = g_value_get_uint (value);
686 break;
687
688 case PROP_CONNECTABLE:
689 priv->connectable = g_value_dup_object (value);
690 break;
691
692 case PROP_PROXY_RESOLVER:
693 if (priv->proxy_resolver)
694 g_object_unref (priv->proxy_resolver);
695 priv->proxy_resolver = g_value_get_object (value);
696 if (!priv->proxy_resolver)
697 priv->proxy_resolver = g_proxy_resolver_get_default ();
698 g_object_ref (priv->proxy_resolver);
699 break;
700
701 default:
702 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
703 }
704 }
705
706 static void
707 g_proxy_address_enumerator_finalize (GObject *object)
708 {
709 GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
710
711 if (priv->connectable)
712 g_object_unref (priv->connectable);
713
714 if (priv->proxy_resolver)
715 g_object_unref (priv->proxy_resolver);
716
717 g_free (priv->dest_uri);
718 g_free (priv->dest_hostname);
719
720 if (priv->dest_ips)
721 g_resolver_free_addresses (priv->dest_ips);
722
723 g_strfreev (priv->proxies);
724
725 if (priv->addr_enum)
726 g_object_unref (priv->addr_enum);
727
728 g_free (priv->proxy_type);
729 g_free (priv->proxy_username);
730 g_free (priv->proxy_password);
731
732 g_clear_error (&priv->last_error);
733
734 G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
735 }
736
737 static void
738 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
739 {
740 self->priv = g_proxy_address_enumerator_get_instance_private (self);
741 }
742
743 static void
744 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
745 {
746 GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
747 GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
748
749 object_class->constructed = g_proxy_address_enumerator_constructed;
750 object_class->set_property = g_proxy_address_enumerator_set_property;
751 object_class->get_property = g_proxy_address_enumerator_get_property;
752 object_class->finalize = g_proxy_address_enumerator_finalize;
753
754 enumerator_class->next = g_proxy_address_enumerator_next;
755 enumerator_class->next_async = g_proxy_address_enumerator_next_async;
756 enumerator_class->next_finish = g_proxy_address_enumerator_next_finish;
757
758 /**
759 * GProxyAddressEnumerator:uri:
760 *
761 * The destination URI. Use `none://` for a generic socket.
762 */
763 g_object_class_install_property (object_class,
764 PROP_URI,
765 g_param_spec_string ("uri", NULL, NULL,
766 NULL,
767 G_PARAM_READWRITE |
768 G_PARAM_CONSTRUCT_ONLY |
769 G_PARAM_STATIC_STRINGS));
770
771 /**
772 * GProxyAddressEnumerator:default-port:
773 *
774 * The default port to use if #GProxyAddressEnumerator:uri does not
775 * specify one.
776 *
777 * Since: 2.38
778 */
779 g_object_class_install_property (object_class,
780 PROP_DEFAULT_PORT,
781 g_param_spec_uint ("default-port", NULL, NULL,
782 0, 65535, 0,
783 G_PARAM_READWRITE |
784 G_PARAM_CONSTRUCT_ONLY |
785 G_PARAM_STATIC_STRINGS));
786
787 /**
788 * GProxyAddressEnumerator:connectable:
789 *
790 * The connectable being enumerated.
791 */
792 g_object_class_install_property (object_class,
793 PROP_CONNECTABLE,
794 g_param_spec_object ("connectable", NULL, NULL,
795 G_TYPE_SOCKET_CONNECTABLE,
796 G_PARAM_READWRITE |
797 G_PARAM_CONSTRUCT_ONLY |
798 G_PARAM_STATIC_STRINGS));
799
800 /**
801 * GProxyAddressEnumerator:proxy-resolver:
802 *
803 * The proxy resolver to use.
804 *
805 * Since: 2.36
806 */
807 g_object_class_install_property (object_class,
808 PROP_PROXY_RESOLVER,
809 g_param_spec_object ("proxy-resolver", NULL, NULL,
810 G_TYPE_PROXY_RESOLVER,
811 G_PARAM_READWRITE |
812 G_PARAM_CONSTRUCT |
813 G_PARAM_STATIC_STRINGS));
814 }