Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / gio / gproxyaddressenumerator.c
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  * SECTION:gproxyaddressenumerator
46  * @short_description: Proxy wrapper enumerator for socket addresses
47  * @include: gio/gio.h
48  *
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.
53  *
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
57  * with one.
58  */
59
60 #define GET_PRIVATE(o) (G_PROXY_ADDRESS_ENUMERATOR (o)->priv)
61
62 enum
63 {
64   PROP_0,
65   PROP_URI,
66   PROP_DEFAULT_PORT,
67   PROP_CONNECTABLE,
68   PROP_PROXY_RESOLVER
69 };
70
71 struct _GProxyAddressEnumeratorPrivate
72 {
73   /* Destination address */
74   GSocketConnectable *connectable;
75   gchar              *dest_uri;
76   guint16             default_port;
77   gchar              *dest_hostname;
78   guint16             dest_port;
79   GList              *dest_ips;
80
81   /* Proxy enumeration */
82   GProxyResolver           *proxy_resolver;
83   gchar                   **proxies;
84   gchar                   **next_proxy;
85   GSocketAddressEnumerator *addr_enum;
86   GSocketAddress           *proxy_address;
87   const gchar              *proxy_uri;
88   gchar                    *proxy_type;
89   gchar                    *proxy_username;
90   gchar                    *proxy_password;
91   gboolean                  supports_hostname;
92   GList                    *next_dest_ip;
93   GError                   *last_error;
94
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.)
102    *
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.
106    */
107   gboolean                  ever_enumerated;
108 };
109
110 G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
111
112 static void
113 save_userinfo (GProxyAddressEnumeratorPrivate *priv,
114                const gchar *proxy)
115 {
116   g_clear_pointer (&priv->proxy_username, g_free);
117   g_clear_pointer (&priv->proxy_password, g_free);
118
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);
122 }
123
124 static void
125 next_enumerator (GProxyAddressEnumeratorPrivate *priv)
126 {
127   if (priv->proxy_address)
128     return;
129
130   while (priv->addr_enum == NULL && *priv->next_proxy)
131     {
132       GSocketConnectable *connectable = NULL;
133       GProxy *proxy;
134
135       priv->proxy_uri = *priv->next_proxy++;
136       g_free (priv->proxy_type);
137       priv->proxy_type = g_uri_parse_scheme (priv->proxy_uri);
138
139       if (priv->proxy_type == NULL)
140         continue;
141
142       /* Assumes hostnames are supported for unknown protocols */
143       priv->supports_hostname = TRUE;
144       proxy = g_proxy_get_default_for_protocol (priv->proxy_type);
145       if (proxy)
146         {
147           priv->supports_hostname = g_proxy_supports_hostname (proxy);
148           g_object_unref (proxy);
149         }
150
151       if (strcmp ("direct", priv->proxy_type) == 0)
152         {
153           if (priv->connectable)
154             connectable = g_object_ref (priv->connectable);
155           else
156             connectable = g_network_address_new (priv->dest_hostname,
157                                                  priv->dest_port);
158         }
159       else
160         {
161           GError *error = NULL;
162           int default_port;
163
164           default_port = GLIB_PRIVATE_CALL (g_uri_get_default_scheme_port) (priv->proxy_type);
165           if (default_port == -1)
166             default_port = 0;
167
168           connectable = g_network_address_parse_uri (priv->proxy_uri, default_port, &error);
169           if (error)
170             {
171               g_warning ("Invalid proxy URI '%s': %s",
172                          priv->proxy_uri, error->message);
173               g_error_free (error);
174             }
175
176           save_userinfo (priv, priv->proxy_uri);
177         }
178
179       if (connectable)
180         {
181           priv->addr_enum = g_socket_connectable_enumerate (connectable);
182           g_object_unref (connectable);
183         }
184     }
185 }
186
187 static GSocketAddress *
188 g_proxy_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
189                                  GCancellable              *cancellable,
190                                  GError                   **error)
191 {
192   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
193   GSocketAddress *result = NULL;
194   GError *first_error = NULL;
195
196   if (!priv->ever_enumerated)
197     {
198       g_assert (priv->proxies == NULL);
199       priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
200                                                priv->dest_uri,
201                                                cancellable,
202                                                error);
203       priv->next_proxy = priv->proxies;
204
205       if (priv->proxies == NULL)
206         {
207           priv->ever_enumerated = TRUE;
208           return NULL;
209         }
210     }
211
212   while (result == NULL && (*priv->next_proxy || priv->addr_enum))
213     {
214       gchar *dest_hostname;
215       gchar *dest_protocol;
216       GInetSocketAddress *inetsaddr;
217       GInetAddress *inetaddr;
218       guint16 port;
219
220       next_enumerator (priv);
221
222       if (!priv->addr_enum)
223         continue;
224
225       if (priv->proxy_address == NULL)
226         {
227           priv->proxy_address = g_socket_address_enumerator_next (
228                                     priv->addr_enum,
229                                     cancellable,
230                                     first_error ? NULL : &first_error);
231         }
232
233       if (priv->proxy_address == NULL)
234         {
235           g_object_unref (priv->addr_enum);
236           priv->addr_enum = NULL;
237
238           if (priv->dest_ips)
239             {
240               g_resolver_free_addresses (priv->dest_ips);
241               priv->dest_ips = NULL;
242             }
243
244           continue;
245         }
246
247       if (strcmp ("direct", priv->proxy_type) == 0)
248         {
249           result = priv->proxy_address;
250           priv->proxy_address = NULL;
251           continue;
252         }
253
254       if (!priv->supports_hostname)
255         {
256           GInetAddress *dest_ip;
257
258           if (!priv->dest_ips)
259             {
260               GResolver *resolver;
261
262               resolver = g_resolver_get_default();
263               priv->dest_ips = g_resolver_lookup_by_name (resolver,
264                                                           priv->dest_hostname,
265                                                           cancellable,
266                                                           first_error ? NULL : &first_error);
267               g_object_unref (resolver);
268
269               if (!priv->dest_ips)
270                 {
271                   g_object_unref (priv->proxy_address);
272                   priv->proxy_address = NULL;
273                   continue;
274                 }
275             }
276
277           if (!priv->next_dest_ip)
278             priv->next_dest_ip = priv->dest_ips;
279         
280           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
281           dest_hostname = g_inet_address_to_string (dest_ip);
282
283           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
284         }
285       else
286         {
287           dest_hostname = g_strdup (priv->dest_hostname);
288         }
289       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
290                                   
291       if (!G_IS_INET_SOCKET_ADDRESS (priv->proxy_address))
292         {
293           g_free (dest_hostname);
294           g_free (dest_protocol);
295         }
296       g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (priv->proxy_address), NULL);
297
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);
301
302       result = g_object_new (G_TYPE_PROXY_ADDRESS,
303                              "address", inetaddr,
304                              "port", port,
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,
312                              NULL);
313       g_free (dest_hostname);
314       g_free (dest_protocol);
315
316       if (priv->supports_hostname || priv->next_dest_ip == NULL)
317         {
318           g_object_unref (priv->proxy_address);
319           priv->proxy_address = NULL;
320         }
321     }
322
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);
327
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"));
330
331   priv->ever_enumerated = TRUE;
332
333   return result;
334 }
335
336 static void
337 complete_async (GTask *task)
338 {
339   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
340
341   if (priv->last_error && (!priv->ever_enumerated || g_error_matches (priv->last_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
342     {
343       g_task_return_error (task, priv->last_error);
344       priv->last_error = NULL;
345     }
346   else if (!priv->ever_enumerated)
347     g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unspecified proxy lookup failure"));
348   else
349     g_task_return_pointer (task, NULL, NULL);
350
351   priv->ever_enumerated = TRUE;
352
353   g_clear_error (&priv->last_error);
354   g_object_unref (task);
355 }
356
357 static void
358 return_result (GTask *task)
359 {
360   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
361   GSocketAddress *result;
362   gboolean is_inet_socket_address;
363
364   if (strcmp ("direct", priv->proxy_type) == 0)
365     {
366       result = priv->proxy_address;
367       priv->proxy_address = NULL;
368     }
369   else
370     {
371       gchar *dest_hostname, *dest_protocol;
372       GInetSocketAddress *inetsaddr;
373       GInetAddress *inetaddr;
374       guint16 port;
375
376       if (!priv->supports_hostname)
377         {
378           GInetAddress *dest_ip;
379
380           if (!priv->next_dest_ip)
381             priv->next_dest_ip = priv->dest_ips;
382
383           dest_ip = G_INET_ADDRESS (priv->next_dest_ip->data);
384           dest_hostname = g_inet_address_to_string (dest_ip);
385
386           priv->next_dest_ip = g_list_next (priv->next_dest_ip);
387         }
388       else
389         {
390           dest_hostname = g_strdup (priv->dest_hostname);
391         }
392       dest_protocol = g_uri_parse_scheme (priv->dest_uri);
393
394       is_inet_socket_address = G_IS_INET_SOCKET_ADDRESS (priv->proxy_address);
395       if (!is_inet_socket_address)
396         {
397           g_free (dest_hostname);
398           g_free (dest_protocol);
399         }
400       g_return_if_fail (is_inet_socket_address);
401
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);
405
406       result = g_object_new (G_TYPE_PROXY_ADDRESS,
407                              "address", inetaddr,
408                              "port", port,
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,
416                              NULL);
417       g_free (dest_hostname);
418       g_free (dest_protocol);
419
420       if (priv->supports_hostname || priv->next_dest_ip == NULL)
421         {
422           g_object_unref (priv->proxy_address);
423           priv->proxy_address = NULL;
424         }
425     }
426
427   priv->ever_enumerated = TRUE;
428   g_task_return_pointer (task, result, g_object_unref);
429   g_object_unref (task);
430 }
431
432 static void address_enumerate_cb (GObject      *object,
433                                   GAsyncResult *result,
434                                   gpointer      user_data);
435
436 static void
437 next_proxy (GTask *task)
438 {
439   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
440
441   if (*priv->next_proxy)
442     {
443       g_object_unref (priv->addr_enum);
444       priv->addr_enum = NULL;
445
446       if (priv->dest_ips)
447         {
448           g_resolver_free_addresses (priv->dest_ips);
449           priv->dest_ips = NULL;
450         }
451
452       next_enumerator (priv);
453
454       if (priv->addr_enum)
455         {
456           g_socket_address_enumerator_next_async (priv->addr_enum,
457                                                   g_task_get_cancellable (task),
458                                                   address_enumerate_cb,
459                                                   task);
460           return;
461         }
462     }
463
464   complete_async (task);
465 }
466
467 static void
468 dest_hostname_lookup_cb (GObject           *object,
469                          GAsyncResult      *result,
470                          gpointer           user_data)
471 {
472   GTask *task = user_data;
473   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
474
475   g_clear_error (&priv->last_error);
476   priv->dest_ips = g_resolver_lookup_by_name_finish (G_RESOLVER (object),
477                                                      result,
478                                                      &priv->last_error);
479   if (priv->dest_ips)
480     return_result (task);
481   else
482     {
483       g_clear_object (&priv->proxy_address);
484       next_proxy (task);
485     }
486 }
487
488 static void
489 address_enumerate_cb (GObject      *object,
490                       GAsyncResult *result,
491                       gpointer      user_data)
492 {
493   GTask *task = user_data;
494   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
495
496   g_clear_error (&priv->last_error);
497   priv->proxy_address =
498     g_socket_address_enumerator_next_finish (priv->addr_enum,
499                                              result,
500                                              &priv->last_error);
501   if (priv->proxy_address)
502     {
503       if (!priv->supports_hostname && !priv->dest_ips)
504         {
505           GResolver *resolver;
506           resolver = g_resolver_get_default();
507           g_resolver_lookup_by_name_async (resolver,
508                                            priv->dest_hostname,
509                                            g_task_get_cancellable (task),
510                                            dest_hostname_lookup_cb,
511                                            task);
512           g_object_unref (resolver);
513           return;
514         }
515
516       return_result (task);
517     }
518   else
519     next_proxy (task);
520 }
521
522 static void
523 proxy_lookup_cb (GObject      *object,
524                  GAsyncResult *result,
525                  gpointer      user_data)
526 {
527   GTask *task = user_data;
528   GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
529
530   g_clear_error (&priv->last_error);
531   priv->proxies = g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (object),
532                                                   result,
533                                                   &priv->last_error);
534   priv->next_proxy = priv->proxies;
535
536   if (priv->last_error)
537     {
538       complete_async (task);
539       return;
540     }
541   else
542     {
543       next_enumerator (priv);
544       if (priv->addr_enum)
545         {
546           g_socket_address_enumerator_next_async (priv->addr_enum,
547                                                   g_task_get_cancellable (task),
548                                                   address_enumerate_cb,
549                                                   task);
550           return;
551         }
552     }
553
554   complete_async (task);
555 }
556
557 static void
558 g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
559                                        GCancellable             *cancellable,
560                                        GAsyncReadyCallback       callback,
561                                        gpointer                  user_data)
562 {
563   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (enumerator);
564   GTask *task;
565
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);
569
570   if (priv->proxies == NULL)
571     {
572       g_proxy_resolver_lookup_async (priv->proxy_resolver,
573                                      priv->dest_uri,
574                                      cancellable,
575                                      proxy_lookup_cb,
576                                      task);
577       return;
578     }
579
580   if (priv->addr_enum)
581     {
582       if (priv->proxy_address)
583         {
584           return_result (task);
585           return;
586         }
587       else
588         {
589           g_socket_address_enumerator_next_async (priv->addr_enum,
590                                                   cancellable,
591                                                   address_enumerate_cb,
592                                                   task);
593           return;
594         }
595     }
596
597   complete_async (task);
598 }
599
600 static GSocketAddress *
601 g_proxy_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
602                                         GAsyncResult              *result,
603                                         GError                   **error)
604 {
605   g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
606
607   return g_task_propagate_pointer (G_TASK (result), error);
608 }
609
610 static void
611 g_proxy_address_enumerator_constructed (GObject *object)
612 {
613   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
614   GSocketConnectable *conn;
615   guint port;
616
617   if (priv->dest_uri)
618     {
619       conn = g_network_address_parse_uri (priv->dest_uri, priv->default_port, NULL);
620       if (conn)
621         {
622           g_object_get (conn,
623                         "hostname", &priv->dest_hostname,
624                         "port", &port,
625                         NULL);
626           priv->dest_port = port;
627
628           g_object_unref (conn);
629         }
630       else
631         g_warning ("Invalid URI '%s'", priv->dest_uri);
632     }
633
634   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->constructed (object);
635 }
636
637 static void
638 g_proxy_address_enumerator_get_property (GObject        *object,
639                                          guint           property_id,
640                                          GValue         *value,
641                                          GParamSpec     *pspec)
642 {
643   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
644   switch (property_id)
645     {
646     case PROP_URI:
647       g_value_set_string (value, priv->dest_uri);
648       break;
649
650     case PROP_DEFAULT_PORT:
651       g_value_set_uint (value, priv->default_port);
652       break;
653
654     case PROP_CONNECTABLE:
655       g_value_set_object (value, priv->connectable);
656       break;
657
658     case PROP_PROXY_RESOLVER:
659       g_value_set_object (value, priv->proxy_resolver);
660       break;
661
662     default:
663       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
664     }
665 }
666
667 static void
668 g_proxy_address_enumerator_set_property (GObject        *object,
669                                          guint           property_id,
670                                          const GValue   *value,
671                                          GParamSpec     *pspec)
672 {
673   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
674   switch (property_id)
675     {
676     case PROP_URI:
677       priv->dest_uri = g_value_dup_string (value);
678       break;
679
680     case PROP_DEFAULT_PORT:
681       priv->default_port = g_value_get_uint (value);
682       break;
683
684     case PROP_CONNECTABLE:
685       priv->connectable = g_value_dup_object (value);
686       break;
687
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);
695       break;
696
697     default:
698       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
699     }
700 }
701
702 static void
703 g_proxy_address_enumerator_finalize (GObject *object)
704 {
705   GProxyAddressEnumeratorPrivate *priv = GET_PRIVATE (object);
706
707   if (priv->connectable)
708     g_object_unref (priv->connectable);
709
710   if (priv->proxy_resolver)
711     g_object_unref (priv->proxy_resolver);
712
713   g_free (priv->dest_uri);
714   g_free (priv->dest_hostname);
715
716   if (priv->dest_ips)
717     g_resolver_free_addresses (priv->dest_ips);
718
719   g_strfreev (priv->proxies);
720
721   if (priv->addr_enum)
722     g_object_unref (priv->addr_enum);
723
724   g_free (priv->proxy_type);
725   g_free (priv->proxy_username);
726   g_free (priv->proxy_password);
727
728   g_clear_error (&priv->last_error);
729
730   G_OBJECT_CLASS (g_proxy_address_enumerator_parent_class)->finalize (object);
731 }
732
733 static void
734 g_proxy_address_enumerator_init (GProxyAddressEnumerator *self)
735 {
736   self->priv = g_proxy_address_enumerator_get_instance_private (self);
737 }
738
739 static void
740 g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enumerator_class)
741 {
742   GObjectClass *object_class = G_OBJECT_CLASS (proxy_enumerator_class);
743   GSocketAddressEnumeratorClass *enumerator_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (proxy_enumerator_class);
744
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;
749
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;
753
754   g_object_class_install_property (object_class,
755                                    PROP_URI,
756                                    g_param_spec_string ("uri",
757                                                         P_("URI"),
758                                                         P_("The destination URI, use none:// for generic socket"),
759                                                         NULL,
760                                                         G_PARAM_READWRITE |
761                                                         G_PARAM_CONSTRUCT_ONLY |
762                                                         G_PARAM_STATIC_STRINGS));
763
764   /**
765    * GProxyAddressEnumerator:default-port:
766    *
767    * The default port to use if #GProxyAddressEnumerator:uri does not
768    * specify one.
769    *
770    * Since: 2.38
771    */
772   g_object_class_install_property (object_class,
773                                    PROP_DEFAULT_PORT,
774                                    g_param_spec_uint ("default-port",
775                                                       P_("Default port"),
776                                                       P_("The default port to use if uri does not specify one"),
777                                                       0, 65535, 0,
778                                                       G_PARAM_READWRITE |
779                                                       G_PARAM_CONSTRUCT_ONLY |
780                                                       G_PARAM_STATIC_STRINGS));
781
782   g_object_class_install_property (object_class,
783                                    PROP_CONNECTABLE,
784                                    g_param_spec_object ("connectable",
785                                                         P_("Connectable"),
786                                                         P_("The connectable being enumerated."),
787                                                         G_TYPE_SOCKET_CONNECTABLE,
788                                                         G_PARAM_READWRITE |
789                                                         G_PARAM_CONSTRUCT_ONLY |
790                                                         G_PARAM_STATIC_STRINGS));
791
792   /**
793    * GProxyAddressEnumerator:proxy-resolver:
794    *
795    * The proxy resolver to use.
796    *
797    * Since: 2.36
798    */
799   g_object_class_install_property (object_class,
800                                    PROP_PROXY_RESOLVER,
801                                    g_param_spec_object ("proxy-resolver",
802                                                         P_("Proxy resolver"),
803                                                         P_("The proxy resolver to use."),
804                                                         G_TYPE_PROXY_RESOLVER,
805                                                         G_PARAM_READWRITE |
806                                                         G_PARAM_CONSTRUCT |
807                                                         G_PARAM_STATIC_STRINGS));
808 }