/src/glib/gio/gnetworkservice.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
2 | | |
3 | | /* GIO - GLib Input, Output and Streaming Library |
4 | | * |
5 | | * Copyright (C) 2008 Red Hat, Inc. |
6 | | * |
7 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
8 | | * |
9 | | * This library is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * This library is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General |
20 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include "config.h" |
24 | | #include <glib.h> |
25 | | #include "glibintl.h" |
26 | | |
27 | | #include "gnetworkservice.h" |
28 | | |
29 | | #include "gcancellable.h" |
30 | | #include "ginetaddress.h" |
31 | | #include "ginetsocketaddress.h" |
32 | | #include "gioerror.h" |
33 | | #include "gnetworkaddress.h" |
34 | | #include "gnetworkingprivate.h" |
35 | | #include "gresolver.h" |
36 | | #include "gtask.h" |
37 | | #include "gsocketaddressenumerator.h" |
38 | | #include "gsocketconnectable.h" |
39 | | #include "gsrvtarget.h" |
40 | | |
41 | | #include <stdlib.h> |
42 | | #include <string.h> |
43 | | |
44 | | |
45 | | /** |
46 | | * SECTION:gnetworkservice |
47 | | * @short_description: A GSocketConnectable for resolving SRV records |
48 | | * @include: gio/gio.h |
49 | | * |
50 | | * Like #GNetworkAddress does with hostnames, #GNetworkService |
51 | | * provides an easy way to resolve a SRV record, and then attempt to |
52 | | * connect to one of the hosts that implements that service, handling |
53 | | * service priority/weighting, multiple IP addresses, and multiple |
54 | | * address families. |
55 | | * |
56 | | * See #GSrvTarget for more information about SRV records, and see |
57 | | * #GSocketConnectable for an example of using the connectable |
58 | | * interface. |
59 | | */ |
60 | | |
61 | | /** |
62 | | * GNetworkService: |
63 | | * |
64 | | * A #GSocketConnectable for resolving a SRV record and connecting to |
65 | | * that service. |
66 | | */ |
67 | | |
68 | | struct _GNetworkServicePrivate |
69 | | { |
70 | | gchar *service, *protocol, *domain, *scheme; |
71 | | GList *targets; |
72 | | }; |
73 | | |
74 | | enum { |
75 | | PROP_0, |
76 | | PROP_SERVICE, |
77 | | PROP_PROTOCOL, |
78 | | PROP_DOMAIN, |
79 | | PROP_SCHEME |
80 | | }; |
81 | | |
82 | | static void g_network_service_set_property (GObject *object, |
83 | | guint prop_id, |
84 | | const GValue *value, |
85 | | GParamSpec *pspec); |
86 | | static void g_network_service_get_property (GObject *object, |
87 | | guint prop_id, |
88 | | GValue *value, |
89 | | GParamSpec *pspec); |
90 | | |
91 | | static void g_network_service_connectable_iface_init (GSocketConnectableIface *iface); |
92 | | static GSocketAddressEnumerator *g_network_service_connectable_enumerate (GSocketConnectable *connectable); |
93 | | static GSocketAddressEnumerator *g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable); |
94 | | static gchar *g_network_service_connectable_to_string (GSocketConnectable *connectable); |
95 | | |
96 | | G_DEFINE_TYPE_WITH_CODE (GNetworkService, g_network_service, G_TYPE_OBJECT, |
97 | | G_ADD_PRIVATE (GNetworkService) |
98 | | G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE, |
99 | | g_network_service_connectable_iface_init)) |
100 | | |
101 | | static void |
102 | | g_network_service_finalize (GObject *object) |
103 | 0 | { |
104 | 0 | GNetworkService *srv = G_NETWORK_SERVICE (object); |
105 | |
|
106 | 0 | g_free (srv->priv->service); |
107 | 0 | g_free (srv->priv->protocol); |
108 | 0 | g_free (srv->priv->domain); |
109 | 0 | g_free (srv->priv->scheme); |
110 | |
|
111 | 0 | if (srv->priv->targets) |
112 | 0 | g_resolver_free_targets (srv->priv->targets); |
113 | |
|
114 | 0 | G_OBJECT_CLASS (g_network_service_parent_class)->finalize (object); |
115 | 0 | } |
116 | | |
117 | | static void |
118 | | g_network_service_class_init (GNetworkServiceClass *klass) |
119 | 0 | { |
120 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
121 | |
|
122 | 0 | gobject_class->set_property = g_network_service_set_property; |
123 | 0 | gobject_class->get_property = g_network_service_get_property; |
124 | 0 | gobject_class->finalize = g_network_service_finalize; |
125 | |
|
126 | 0 | g_object_class_install_property (gobject_class, PROP_SERVICE, |
127 | 0 | g_param_spec_string ("service", |
128 | 0 | P_("Service"), |
129 | 0 | P_("Service name, eg \"ldap\""), |
130 | 0 | NULL, |
131 | 0 | G_PARAM_READWRITE | |
132 | 0 | G_PARAM_CONSTRUCT_ONLY | |
133 | 0 | G_PARAM_STATIC_STRINGS)); |
134 | 0 | g_object_class_install_property (gobject_class, PROP_PROTOCOL, |
135 | 0 | g_param_spec_string ("protocol", |
136 | 0 | P_("Protocol"), |
137 | 0 | P_("Network protocol, eg \"tcp\""), |
138 | 0 | NULL, |
139 | 0 | G_PARAM_READWRITE | |
140 | 0 | G_PARAM_CONSTRUCT_ONLY | |
141 | 0 | G_PARAM_STATIC_STRINGS)); |
142 | 0 | g_object_class_install_property (gobject_class, PROP_DOMAIN, |
143 | 0 | g_param_spec_string ("domain", |
144 | 0 | P_("Domain"), |
145 | 0 | P_("Network domain, eg, \"example.com\""), |
146 | 0 | NULL, |
147 | 0 | G_PARAM_READWRITE | |
148 | 0 | G_PARAM_CONSTRUCT_ONLY | |
149 | 0 | G_PARAM_STATIC_STRINGS)); |
150 | 0 | g_object_class_install_property (gobject_class, PROP_DOMAIN, |
151 | 0 | g_param_spec_string ("scheme", |
152 | 0 | P_("Scheme"), |
153 | 0 | P_("Network scheme (default is to use service)"), |
154 | 0 | NULL, |
155 | 0 | G_PARAM_READWRITE | |
156 | 0 | G_PARAM_STATIC_STRINGS)); |
157 | |
|
158 | 0 | } |
159 | | |
160 | | static void |
161 | | g_network_service_connectable_iface_init (GSocketConnectableIface *connectable_iface) |
162 | 0 | { |
163 | 0 | connectable_iface->enumerate = g_network_service_connectable_enumerate; |
164 | 0 | connectable_iface->proxy_enumerate = g_network_service_connectable_proxy_enumerate; |
165 | 0 | connectable_iface->to_string = g_network_service_connectable_to_string; |
166 | 0 | } |
167 | | |
168 | | static void |
169 | | g_network_service_init (GNetworkService *srv) |
170 | 0 | { |
171 | 0 | srv->priv = g_network_service_get_instance_private (srv); |
172 | 0 | } |
173 | | |
174 | | static void |
175 | | g_network_service_set_property (GObject *object, |
176 | | guint prop_id, |
177 | | const GValue *value, |
178 | | GParamSpec *pspec) |
179 | 0 | { |
180 | 0 | GNetworkService *srv = G_NETWORK_SERVICE (object); |
181 | |
|
182 | 0 | switch (prop_id) |
183 | 0 | { |
184 | 0 | case PROP_SERVICE: |
185 | 0 | srv->priv->service = g_value_dup_string (value); |
186 | 0 | break; |
187 | | |
188 | 0 | case PROP_PROTOCOL: |
189 | 0 | srv->priv->protocol = g_value_dup_string (value); |
190 | 0 | break; |
191 | | |
192 | 0 | case PROP_DOMAIN: |
193 | 0 | srv->priv->domain = g_value_dup_string (value); |
194 | 0 | break; |
195 | | |
196 | 0 | case PROP_SCHEME: |
197 | 0 | g_network_service_set_scheme (srv, g_value_get_string (value)); |
198 | 0 | break; |
199 | | |
200 | 0 | default: |
201 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
202 | 0 | break; |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | static void |
207 | | g_network_service_get_property (GObject *object, |
208 | | guint prop_id, |
209 | | GValue *value, |
210 | | GParamSpec *pspec) |
211 | 0 | { |
212 | 0 | GNetworkService *srv = G_NETWORK_SERVICE (object); |
213 | |
|
214 | 0 | switch (prop_id) |
215 | 0 | { |
216 | 0 | case PROP_SERVICE: |
217 | 0 | g_value_set_string (value, g_network_service_get_service (srv)); |
218 | 0 | break; |
219 | | |
220 | 0 | case PROP_PROTOCOL: |
221 | 0 | g_value_set_string (value, g_network_service_get_protocol (srv)); |
222 | 0 | break; |
223 | | |
224 | 0 | case PROP_DOMAIN: |
225 | 0 | g_value_set_string (value, g_network_service_get_domain (srv)); |
226 | 0 | break; |
227 | | |
228 | 0 | case PROP_SCHEME: |
229 | 0 | g_value_set_string (value, g_network_service_get_scheme (srv)); |
230 | 0 | break; |
231 | | |
232 | 0 | default: |
233 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
234 | 0 | break; |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | /** |
239 | | * g_network_service_new: |
240 | | * @service: the service type to look up (eg, "ldap") |
241 | | * @protocol: the networking protocol to use for @service (eg, "tcp") |
242 | | * @domain: the DNS domain to look up the service in |
243 | | * |
244 | | * Creates a new #GNetworkService representing the given @service, |
245 | | * @protocol, and @domain. This will initially be unresolved; use the |
246 | | * #GSocketConnectable interface to resolve it. |
247 | | * |
248 | | * Returns: (transfer full) (type GNetworkService): a new #GNetworkService |
249 | | * |
250 | | * Since: 2.22 |
251 | | */ |
252 | | GSocketConnectable * |
253 | | g_network_service_new (const gchar *service, |
254 | | const gchar *protocol, |
255 | | const gchar *domain) |
256 | 0 | { |
257 | 0 | return g_object_new (G_TYPE_NETWORK_SERVICE, |
258 | 0 | "service", service, |
259 | 0 | "protocol", protocol, |
260 | 0 | "domain", domain, |
261 | 0 | NULL); |
262 | 0 | } |
263 | | |
264 | | /** |
265 | | * g_network_service_get_service: |
266 | | * @srv: a #GNetworkService |
267 | | * |
268 | | * Gets @srv's service name (eg, "ldap"). |
269 | | * |
270 | | * Returns: @srv's service name |
271 | | * |
272 | | * Since: 2.22 |
273 | | */ |
274 | | const gchar * |
275 | | g_network_service_get_service (GNetworkService *srv) |
276 | 0 | { |
277 | 0 | g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL); |
278 | | |
279 | 0 | return srv->priv->service; |
280 | 0 | } |
281 | | |
282 | | /** |
283 | | * g_network_service_get_protocol: |
284 | | * @srv: a #GNetworkService |
285 | | * |
286 | | * Gets @srv's protocol name (eg, "tcp"). |
287 | | * |
288 | | * Returns: @srv's protocol name |
289 | | * |
290 | | * Since: 2.22 |
291 | | */ |
292 | | const gchar * |
293 | | g_network_service_get_protocol (GNetworkService *srv) |
294 | 0 | { |
295 | 0 | g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL); |
296 | | |
297 | 0 | return srv->priv->protocol; |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * g_network_service_get_domain: |
302 | | * @srv: a #GNetworkService |
303 | | * |
304 | | * Gets the domain that @srv serves. This might be either UTF-8 or |
305 | | * ASCII-encoded, depending on what @srv was created with. |
306 | | * |
307 | | * Returns: @srv's domain name |
308 | | * |
309 | | * Since: 2.22 |
310 | | */ |
311 | | const gchar * |
312 | | g_network_service_get_domain (GNetworkService *srv) |
313 | 0 | { |
314 | 0 | g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL); |
315 | | |
316 | 0 | return srv->priv->domain; |
317 | 0 | } |
318 | | |
319 | | /** |
320 | | * g_network_service_get_scheme: |
321 | | * @srv: a #GNetworkService |
322 | | * |
323 | | * Gets the URI scheme used to resolve proxies. By default, the service name |
324 | | * is used as scheme. |
325 | | * |
326 | | * Returns: @srv's scheme name |
327 | | * |
328 | | * Since: 2.26 |
329 | | */ |
330 | | const gchar * |
331 | | g_network_service_get_scheme (GNetworkService *srv) |
332 | 0 | { |
333 | 0 | g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL); |
334 | | |
335 | 0 | if (srv->priv->scheme) |
336 | 0 | return srv->priv->scheme; |
337 | 0 | else |
338 | 0 | return srv->priv->service; |
339 | 0 | } |
340 | | |
341 | | /** |
342 | | * g_network_service_set_scheme: |
343 | | * @srv: a #GNetworkService |
344 | | * @scheme: a URI scheme |
345 | | * |
346 | | * Set's the URI scheme used to resolve proxies. By default, the service name |
347 | | * is used as scheme. |
348 | | * |
349 | | * Since: 2.26 |
350 | | */ |
351 | | void |
352 | | g_network_service_set_scheme (GNetworkService *srv, |
353 | | const gchar *scheme) |
354 | 0 | { |
355 | 0 | g_return_if_fail (G_IS_NETWORK_SERVICE (srv)); |
356 | | |
357 | 0 | g_free (srv->priv->scheme); |
358 | 0 | srv->priv->scheme = g_strdup (scheme); |
359 | |
|
360 | 0 | g_object_notify (G_OBJECT (srv), "scheme"); |
361 | 0 | } |
362 | | |
363 | | static GList * |
364 | | g_network_service_fallback_targets (GNetworkService *srv) |
365 | 0 | { |
366 | 0 | GSrvTarget *target; |
367 | 0 | gboolean has_port; |
368 | 0 | guint16 port; |
369 | |
|
370 | 0 | has_port = g_getservbyname_ntohs (srv->priv->service, "tcp", &port); |
371 | |
|
372 | 0 | #ifdef HAVE_ENDSERVENT |
373 | 0 | endservent (); |
374 | 0 | #endif |
375 | |
|
376 | 0 | if (!has_port) |
377 | 0 | return NULL; |
378 | | |
379 | 0 | target = g_srv_target_new (srv->priv->domain, port, 0, 0); |
380 | 0 | return g_list_append (NULL, target); |
381 | 0 | } |
382 | | |
383 | 0 | #define G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR (_g_network_service_address_enumerator_get_type ()) |
384 | 0 | #define G_NETWORK_SERVICE_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, GNetworkServiceAddressEnumerator)) |
385 | | |
386 | | typedef struct { |
387 | | GSocketAddressEnumerator parent_instance; |
388 | | |
389 | | GResolver *resolver; |
390 | | GNetworkService *srv; |
391 | | GSocketAddressEnumerator *addr_enum; |
392 | | GList *t; |
393 | | gboolean use_proxy; |
394 | | |
395 | | GError *error; |
396 | | |
397 | | } GNetworkServiceAddressEnumerator; |
398 | | |
399 | | typedef struct { |
400 | | GSocketAddressEnumeratorClass parent_class; |
401 | | |
402 | | } GNetworkServiceAddressEnumeratorClass; |
403 | | |
404 | | static GType _g_network_service_address_enumerator_get_type (void); |
405 | | G_DEFINE_TYPE (GNetworkServiceAddressEnumerator, _g_network_service_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR) |
406 | | |
407 | | static GSocketAddress * |
408 | | g_network_service_address_enumerator_next (GSocketAddressEnumerator *enumerator, |
409 | | GCancellable *cancellable, |
410 | | GError **error) |
411 | 0 | { |
412 | 0 | GNetworkServiceAddressEnumerator *srv_enum = |
413 | 0 | G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator); |
414 | 0 | GSocketAddress *ret = NULL; |
415 | | |
416 | | /* If we haven't yet resolved srv, do that */ |
417 | 0 | if (!srv_enum->srv->priv->targets) |
418 | 0 | { |
419 | 0 | GList *targets; |
420 | 0 | GError *my_error = NULL; |
421 | |
|
422 | 0 | targets = g_resolver_lookup_service (srv_enum->resolver, |
423 | 0 | srv_enum->srv->priv->service, |
424 | 0 | srv_enum->srv->priv->protocol, |
425 | 0 | srv_enum->srv->priv->domain, |
426 | 0 | cancellable, &my_error); |
427 | 0 | if (!targets && g_error_matches (my_error, G_RESOLVER_ERROR, |
428 | 0 | G_RESOLVER_ERROR_NOT_FOUND)) |
429 | 0 | { |
430 | 0 | targets = g_network_service_fallback_targets (srv_enum->srv); |
431 | 0 | if (targets) |
432 | 0 | g_clear_error (&my_error); |
433 | 0 | } |
434 | |
|
435 | 0 | if (my_error) |
436 | 0 | { |
437 | 0 | g_propagate_error (error, my_error); |
438 | 0 | return NULL; |
439 | 0 | } |
440 | | |
441 | 0 | srv_enum->srv->priv->targets = targets; |
442 | 0 | srv_enum->t = srv_enum->srv->priv->targets; |
443 | 0 | } |
444 | | |
445 | | /* Delegate to GNetworkAddress */ |
446 | 0 | do |
447 | 0 | { |
448 | 0 | if (srv_enum->addr_enum == NULL && srv_enum->t) |
449 | 0 | { |
450 | 0 | GError *my_error = NULL; |
451 | 0 | gchar *uri; |
452 | 0 | gchar *hostname; |
453 | 0 | GSocketConnectable *addr; |
454 | 0 | GSrvTarget *target = srv_enum->t->data; |
455 | |
|
456 | 0 | srv_enum->t = g_list_next (srv_enum->t); |
457 | |
|
458 | 0 | hostname = g_hostname_to_ascii (g_srv_target_get_hostname (target)); |
459 | |
|
460 | 0 | if (hostname == NULL) |
461 | 0 | { |
462 | 0 | if (srv_enum->error == NULL) |
463 | 0 | srv_enum->error = |
464 | 0 | g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, |
465 | 0 | "Received invalid hostname '%s' from GSrvTarget", |
466 | 0 | g_srv_target_get_hostname (target)); |
467 | 0 | continue; |
468 | 0 | } |
469 | | |
470 | 0 | uri = g_uri_join (G_URI_FLAGS_NONE, |
471 | 0 | g_network_service_get_scheme (srv_enum->srv), |
472 | 0 | NULL, |
473 | 0 | hostname, |
474 | 0 | g_srv_target_get_port (target), |
475 | 0 | "", |
476 | 0 | NULL, |
477 | 0 | NULL); |
478 | 0 | g_free (hostname); |
479 | |
|
480 | 0 | addr = g_network_address_parse_uri (uri, |
481 | 0 | g_srv_target_get_port (target), |
482 | 0 | &my_error); |
483 | 0 | g_free (uri); |
484 | |
|
485 | 0 | if (addr == NULL) |
486 | 0 | { |
487 | 0 | if (srv_enum->error == NULL) |
488 | 0 | srv_enum->error = my_error; |
489 | 0 | else |
490 | 0 | g_error_free (my_error); |
491 | 0 | continue; |
492 | 0 | } |
493 | | |
494 | 0 | if (srv_enum->use_proxy) |
495 | 0 | srv_enum->addr_enum = g_socket_connectable_proxy_enumerate (addr); |
496 | 0 | else |
497 | 0 | srv_enum->addr_enum = g_socket_connectable_enumerate (addr); |
498 | 0 | g_object_unref (addr); |
499 | 0 | } |
500 | | |
501 | 0 | if (srv_enum->addr_enum) |
502 | 0 | { |
503 | 0 | GError *my_error = NULL; |
504 | |
|
505 | 0 | ret = g_socket_address_enumerator_next (srv_enum->addr_enum, |
506 | 0 | cancellable, |
507 | 0 | &my_error); |
508 | |
|
509 | 0 | if (my_error) |
510 | 0 | { |
511 | 0 | if (srv_enum->error == NULL) |
512 | 0 | srv_enum->error = my_error; |
513 | 0 | else |
514 | 0 | g_error_free (my_error); |
515 | 0 | } |
516 | |
|
517 | 0 | if (!ret) |
518 | 0 | { |
519 | 0 | g_object_unref (srv_enum->addr_enum); |
520 | 0 | srv_enum->addr_enum = NULL; |
521 | 0 | } |
522 | 0 | } |
523 | 0 | } |
524 | 0 | while (srv_enum->addr_enum == NULL && srv_enum->t); |
525 | | |
526 | 0 | if (ret == NULL && srv_enum->error) |
527 | 0 | { |
528 | 0 | g_propagate_error (error, srv_enum->error); |
529 | 0 | srv_enum->error = NULL; |
530 | 0 | } |
531 | |
|
532 | 0 | return ret; |
533 | 0 | } |
534 | | |
535 | | static void next_async_resolved_targets (GObject *source_object, |
536 | | GAsyncResult *result, |
537 | | gpointer user_data); |
538 | | static void next_async_have_targets (GTask *srv_enum); |
539 | | static void next_async_have_address (GObject *source_object, |
540 | | GAsyncResult *result, |
541 | | gpointer user_data); |
542 | | |
543 | | static void |
544 | | g_network_service_address_enumerator_next_async (GSocketAddressEnumerator *enumerator, |
545 | | GCancellable *cancellable, |
546 | | GAsyncReadyCallback callback, |
547 | | gpointer user_data) |
548 | 0 | { |
549 | 0 | GNetworkServiceAddressEnumerator *srv_enum = |
550 | 0 | G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator); |
551 | 0 | GTask *task; |
552 | |
|
553 | 0 | task = g_task_new (enumerator, cancellable, callback, user_data); |
554 | 0 | g_task_set_source_tag (task, g_network_service_address_enumerator_next_async); |
555 | | |
556 | | /* If we haven't yet resolved srv, do that */ |
557 | 0 | if (!srv_enum->srv->priv->targets) |
558 | 0 | { |
559 | 0 | g_resolver_lookup_service_async (srv_enum->resolver, |
560 | 0 | srv_enum->srv->priv->service, |
561 | 0 | srv_enum->srv->priv->protocol, |
562 | 0 | srv_enum->srv->priv->domain, |
563 | 0 | cancellable, |
564 | 0 | next_async_resolved_targets, |
565 | 0 | task); |
566 | 0 | } |
567 | 0 | else |
568 | 0 | next_async_have_targets (task); |
569 | 0 | } |
570 | | |
571 | | static void |
572 | | next_async_resolved_targets (GObject *source_object, |
573 | | GAsyncResult *result, |
574 | | gpointer user_data) |
575 | 0 | { |
576 | 0 | GTask *task = user_data; |
577 | 0 | GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task); |
578 | 0 | GError *error = NULL; |
579 | 0 | GList *targets; |
580 | |
|
581 | 0 | targets = g_resolver_lookup_service_finish (srv_enum->resolver, |
582 | 0 | result, &error); |
583 | |
|
584 | 0 | if (!targets && g_error_matches (error, G_RESOLVER_ERROR, |
585 | 0 | G_RESOLVER_ERROR_NOT_FOUND)) |
586 | 0 | { |
587 | 0 | targets = g_network_service_fallback_targets (srv_enum->srv); |
588 | 0 | if (targets) |
589 | 0 | g_clear_error (&error); |
590 | 0 | } |
591 | |
|
592 | 0 | if (error) |
593 | 0 | { |
594 | 0 | g_task_return_error (task, error); |
595 | 0 | g_object_unref (task); |
596 | 0 | } |
597 | 0 | else |
598 | 0 | { |
599 | 0 | srv_enum->t = srv_enum->srv->priv->targets = targets; |
600 | 0 | next_async_have_targets (task); |
601 | 0 | } |
602 | 0 | } |
603 | | |
604 | | static void |
605 | | next_async_have_targets (GTask *task) |
606 | 0 | { |
607 | 0 | GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task); |
608 | | |
609 | | /* Delegate to GNetworkAddress */ |
610 | 0 | if (srv_enum->addr_enum == NULL && srv_enum->t) |
611 | 0 | { |
612 | 0 | GSocketConnectable *addr; |
613 | 0 | GSrvTarget *target = srv_enum->t->data; |
614 | |
|
615 | 0 | srv_enum->t = g_list_next (srv_enum->t); |
616 | 0 | addr = g_network_address_new (g_srv_target_get_hostname (target), |
617 | 0 | (guint16) g_srv_target_get_port (target)); |
618 | |
|
619 | 0 | if (srv_enum->use_proxy) |
620 | 0 | srv_enum->addr_enum = g_socket_connectable_proxy_enumerate (addr); |
621 | 0 | else |
622 | 0 | srv_enum->addr_enum = g_socket_connectable_enumerate (addr); |
623 | |
|
624 | 0 | g_object_unref (addr); |
625 | 0 | } |
626 | |
|
627 | 0 | if (srv_enum->addr_enum) |
628 | 0 | { |
629 | 0 | g_socket_address_enumerator_next_async (srv_enum->addr_enum, |
630 | 0 | g_task_get_cancellable (task), |
631 | 0 | next_async_have_address, |
632 | 0 | task); |
633 | 0 | } |
634 | 0 | else |
635 | 0 | { |
636 | 0 | if (srv_enum->error) |
637 | 0 | { |
638 | 0 | g_task_return_error (task, srv_enum->error); |
639 | 0 | srv_enum->error = NULL; |
640 | 0 | } |
641 | 0 | else |
642 | 0 | g_task_return_pointer (task, NULL, NULL); |
643 | |
|
644 | 0 | g_object_unref (task); |
645 | 0 | } |
646 | 0 | } |
647 | | |
648 | | static void |
649 | | next_async_have_address (GObject *source_object, |
650 | | GAsyncResult *result, |
651 | | gpointer user_data) |
652 | 0 | { |
653 | 0 | GTask *task = user_data; |
654 | 0 | GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task); |
655 | 0 | GSocketAddress *address; |
656 | 0 | GError *error = NULL; |
657 | | |
658 | 0 | address = g_socket_address_enumerator_next_finish (srv_enum->addr_enum, |
659 | 0 | result, |
660 | 0 | &error); |
661 | |
|
662 | 0 | if (error) |
663 | 0 | { |
664 | 0 | if (srv_enum->error == NULL) |
665 | 0 | srv_enum->error = error; |
666 | 0 | else |
667 | 0 | g_error_free (error); |
668 | 0 | } |
669 | |
|
670 | 0 | if (!address) |
671 | 0 | { |
672 | 0 | g_object_unref (srv_enum->addr_enum); |
673 | 0 | srv_enum->addr_enum = NULL; |
674 | |
|
675 | 0 | next_async_have_targets (task); |
676 | 0 | } |
677 | 0 | else |
678 | 0 | { |
679 | 0 | g_task_return_pointer (task, address, g_object_unref); |
680 | 0 | g_object_unref (task); |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | static GSocketAddress * |
685 | | g_network_service_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator, |
686 | | GAsyncResult *result, |
687 | | GError **error) |
688 | 0 | { |
689 | 0 | return g_task_propagate_pointer (G_TASK (result), error); |
690 | 0 | } |
691 | | |
692 | | static void |
693 | | _g_network_service_address_enumerator_init (GNetworkServiceAddressEnumerator *enumerator) |
694 | 0 | { |
695 | 0 | } |
696 | | |
697 | | static void |
698 | | g_network_service_address_enumerator_finalize (GObject *object) |
699 | 0 | { |
700 | 0 | GNetworkServiceAddressEnumerator *srv_enum = |
701 | 0 | G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (object); |
702 | |
|
703 | 0 | if (srv_enum->srv) |
704 | 0 | g_object_unref (srv_enum->srv); |
705 | |
|
706 | 0 | if (srv_enum->addr_enum) |
707 | 0 | g_object_unref (srv_enum->addr_enum); |
708 | |
|
709 | 0 | if (srv_enum->resolver) |
710 | 0 | g_object_unref (srv_enum->resolver); |
711 | |
|
712 | 0 | if (srv_enum->error) |
713 | 0 | g_error_free (srv_enum->error); |
714 | |
|
715 | 0 | G_OBJECT_CLASS (_g_network_service_address_enumerator_parent_class)->finalize (object); |
716 | 0 | } |
717 | | |
718 | | static void |
719 | | _g_network_service_address_enumerator_class_init (GNetworkServiceAddressEnumeratorClass *srvenum_class) |
720 | 0 | { |
721 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (srvenum_class); |
722 | 0 | GSocketAddressEnumeratorClass *enumerator_class = |
723 | 0 | G_SOCKET_ADDRESS_ENUMERATOR_CLASS (srvenum_class); |
724 | |
|
725 | 0 | enumerator_class->next = g_network_service_address_enumerator_next; |
726 | 0 | enumerator_class->next_async = g_network_service_address_enumerator_next_async; |
727 | 0 | enumerator_class->next_finish = g_network_service_address_enumerator_next_finish; |
728 | |
|
729 | 0 | object_class->finalize = g_network_service_address_enumerator_finalize; |
730 | 0 | } |
731 | | |
732 | | static GSocketAddressEnumerator * |
733 | | g_network_service_connectable_enumerate (GSocketConnectable *connectable) |
734 | 0 | { |
735 | 0 | GNetworkServiceAddressEnumerator *srv_enum; |
736 | |
|
737 | 0 | srv_enum = g_object_new (G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, NULL); |
738 | 0 | srv_enum->srv = g_object_ref (G_NETWORK_SERVICE (connectable)); |
739 | 0 | srv_enum->resolver = g_resolver_get_default (); |
740 | 0 | srv_enum->use_proxy = FALSE; |
741 | |
|
742 | 0 | return G_SOCKET_ADDRESS_ENUMERATOR (srv_enum); |
743 | 0 | } |
744 | | |
745 | | static GSocketAddressEnumerator * |
746 | | g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable) |
747 | 0 | { |
748 | 0 | GSocketAddressEnumerator *addr_enum; |
749 | 0 | GNetworkServiceAddressEnumerator *srv_enum; |
750 | |
|
751 | 0 | addr_enum = g_network_service_connectable_enumerate (connectable); |
752 | 0 | srv_enum = G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (addr_enum); |
753 | 0 | srv_enum->use_proxy = TRUE; |
754 | |
|
755 | 0 | return addr_enum; |
756 | 0 | } |
757 | | |
758 | | static gchar * |
759 | | g_network_service_connectable_to_string (GSocketConnectable *connectable) |
760 | 0 | { |
761 | 0 | GNetworkService *service; |
762 | |
|
763 | 0 | service = G_NETWORK_SERVICE (connectable); |
764 | |
|
765 | 0 | return g_strdup_printf ("(%s, %s, %s, %s)", service->priv->service, |
766 | 0 | service->priv->protocol, service->priv->domain, |
767 | 0 | service->priv->scheme); |
768 | 0 | } |