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