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