Add some more cases to the app-id unit tests
[glib.git] / gio / gnetworkservice.c
blobd2b9017bef3e82aaa716fd2c3fd994b095601275
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
5 * Copyright (C) 2008 Red Hat, Inc.
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 of the License, or (at your option) any later version.
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.
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/>.
21 #include "config.h"
22 #include <glib.h>
23 #include "glibintl.h"
25 #include "gnetworkservice.h"
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"
39 #include <stdlib.h>
40 #include <string.h>
43 /**
44 * SECTION:gnetworkservice
45 * @short_description: A GSocketConnectable for resolving SRV records
46 * @include: gio/gio.h
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.
54 * See #GSrvTarget for more information about SRV records, and see
55 * #GSocketConnectable for and example of using the connectable
56 * interface.
59 /**
60 * GNetworkService:
62 * A #GSocketConnectable for resolving a SRV record and connecting to
63 * that service.
66 struct _GNetworkServicePrivate
68 gchar *service, *protocol, *domain, *scheme;
69 GList *targets;
72 enum {
73 PROP_0,
74 PROP_SERVICE,
75 PROP_PROTOCOL,
76 PROP_DOMAIN,
77 PROP_SCHEME
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);
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);
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))
99 static void
100 g_network_service_finalize (GObject *object)
102 GNetworkService *srv = G_NETWORK_SERVICE (object);
104 g_free (srv->priv->service);
105 g_free (srv->priv->protocol);
106 g_free (srv->priv->domain);
107 g_free (srv->priv->scheme);
109 if (srv->priv->targets)
110 g_resolver_free_targets (srv->priv->targets);
112 G_OBJECT_CLASS (g_network_service_parent_class)->finalize (object);
115 static void
116 g_network_service_class_init (GNetworkServiceClass *klass)
118 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120 gobject_class->set_property = g_network_service_set_property;
121 gobject_class->get_property = g_network_service_get_property;
122 gobject_class->finalize = g_network_service_finalize;
124 g_object_class_install_property (gobject_class, PROP_SERVICE,
125 g_param_spec_string ("service",
126 P_("Service"),
127 P_("Service name, eg \"ldap\""),
128 NULL,
129 G_PARAM_READWRITE |
130 G_PARAM_CONSTRUCT_ONLY |
131 G_PARAM_STATIC_STRINGS));
132 g_object_class_install_property (gobject_class, PROP_PROTOCOL,
133 g_param_spec_string ("protocol",
134 P_("Protocol"),
135 P_("Network protocol, eg \"tcp\""),
136 NULL,
137 G_PARAM_READWRITE |
138 G_PARAM_CONSTRUCT_ONLY |
139 G_PARAM_STATIC_STRINGS));
140 g_object_class_install_property (gobject_class, PROP_DOMAIN,
141 g_param_spec_string ("domain",
142 P_("Domain"),
143 P_("Network domain, eg, \"example.com\""),
144 NULL,
145 G_PARAM_READWRITE |
146 G_PARAM_CONSTRUCT_ONLY |
147 G_PARAM_STATIC_STRINGS));
148 g_object_class_install_property (gobject_class, PROP_DOMAIN,
149 g_param_spec_string ("scheme",
150 P_("Scheme"),
151 P_("Network scheme (default is to use service)"),
152 NULL,
153 G_PARAM_READWRITE |
154 G_PARAM_STATIC_STRINGS));
158 static void
159 g_network_service_connectable_iface_init (GSocketConnectableIface *connectable_iface)
161 connectable_iface->enumerate = g_network_service_connectable_enumerate;
162 connectable_iface->proxy_enumerate = g_network_service_connectable_proxy_enumerate;
163 connectable_iface->to_string = g_network_service_connectable_to_string;
166 static void
167 g_network_service_init (GNetworkService *srv)
169 srv->priv = g_network_service_get_instance_private (srv);
172 static void
173 g_network_service_set_property (GObject *object,
174 guint prop_id,
175 const GValue *value,
176 GParamSpec *pspec)
178 GNetworkService *srv = G_NETWORK_SERVICE (object);
180 switch (prop_id)
182 case PROP_SERVICE:
183 srv->priv->service = g_value_dup_string (value);
184 break;
186 case PROP_PROTOCOL:
187 srv->priv->protocol = g_value_dup_string (value);
188 break;
190 case PROP_DOMAIN:
191 srv->priv->domain = g_value_dup_string (value);
192 break;
194 case PROP_SCHEME:
195 g_network_service_set_scheme (srv, g_value_get_string (value));
196 break;
198 default:
199 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200 break;
204 static void
205 g_network_service_get_property (GObject *object,
206 guint prop_id,
207 GValue *value,
208 GParamSpec *pspec)
210 GNetworkService *srv = G_NETWORK_SERVICE (object);
212 switch (prop_id)
214 case PROP_SERVICE:
215 g_value_set_string (value, g_network_service_get_service (srv));
216 break;
218 case PROP_PROTOCOL:
219 g_value_set_string (value, g_network_service_get_protocol (srv));
220 break;
222 case PROP_DOMAIN:
223 g_value_set_string (value, g_network_service_get_domain (srv));
224 break;
226 case PROP_SCHEME:
227 g_value_set_string (value, g_network_service_get_scheme (srv));
228 break;
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232 break;
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
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.
246 * Returns: (transfer full) (type GNetworkService): a new #GNetworkService
248 * Since: 2.22
250 GSocketConnectable *
251 g_network_service_new (const gchar *service,
252 const gchar *protocol,
253 const gchar *domain)
255 return g_object_new (G_TYPE_NETWORK_SERVICE,
256 "service", service,
257 "protocol", protocol,
258 "domain", domain,
259 NULL);
263 * g_network_service_get_service:
264 * @srv: a #GNetworkService
266 * Gets @srv's service name (eg, "ldap").
268 * Returns: @srv's service name
270 * Since: 2.22
272 const gchar *
273 g_network_service_get_service (GNetworkService *srv)
275 g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
277 return srv->priv->service;
281 * g_network_service_get_protocol:
282 * @srv: a #GNetworkService
284 * Gets @srv's protocol name (eg, "tcp").
286 * Returns: @srv's protocol name
288 * Since: 2.22
290 const gchar *
291 g_network_service_get_protocol (GNetworkService *srv)
293 g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
295 return srv->priv->protocol;
299 * g_network_service_get_domain:
300 * @srv: a #GNetworkService
302 * Gets the domain that @srv serves. This might be either UTF-8 or
303 * ASCII-encoded, depending on what @srv was created with.
305 * Returns: @srv's domain name
307 * Since: 2.22
309 const gchar *
310 g_network_service_get_domain (GNetworkService *srv)
312 g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
314 return srv->priv->domain;
318 * g_network_service_get_scheme:
319 * @srv: a #GNetworkService
321 * Get's the URI scheme used to resolve proxies. By default, the service name
322 * is used as scheme.
324 * Returns: @srv's scheme name
326 * Since: 2.26
328 const gchar *
329 g_network_service_get_scheme (GNetworkService *srv)
331 g_return_val_if_fail (G_IS_NETWORK_SERVICE (srv), NULL);
333 if (srv->priv->scheme)
334 return srv->priv->scheme;
335 else
336 return srv->priv->service;
340 * g_network_service_set_scheme:
341 * @srv: a #GNetworkService
342 * @scheme: a URI scheme
344 * Set's the URI scheme used to resolve proxies. By default, the service name
345 * is used as scheme.
347 * Since: 2.26
349 void
350 g_network_service_set_scheme (GNetworkService *srv,
351 const gchar *scheme)
353 g_return_if_fail (G_IS_NETWORK_SERVICE (srv));
355 g_free (srv->priv->scheme);
356 srv->priv->scheme = g_strdup (scheme);
358 g_object_notify (G_OBJECT (srv), "scheme");
361 static GList *
362 g_network_service_fallback_targets (GNetworkService *srv)
364 GSrvTarget *target;
365 struct servent *entry;
366 guint16 port;
368 entry = getservbyname (srv->priv->service, "tcp");
369 port = entry ? g_ntohs (entry->s_port) : 0;
370 #ifdef HAVE_ENDSERVENT
371 endservent ();
372 #endif
374 if (entry == NULL)
375 return NULL;
377 target = g_srv_target_new (srv->priv->domain, port, 0, 0);
378 return g_list_append (NULL, target);
381 #define G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR (_g_network_service_address_enumerator_get_type ())
382 #define G_NETWORK_SERVICE_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, GNetworkServiceAddressEnumerator))
384 typedef struct {
385 GSocketAddressEnumerator parent_instance;
387 GResolver *resolver;
388 GNetworkService *srv;
389 GSocketAddressEnumerator *addr_enum;
390 GList *t;
391 gboolean use_proxy;
393 GError *error;
395 } GNetworkServiceAddressEnumerator;
397 typedef struct {
398 GSocketAddressEnumeratorClass parent_class;
400 } GNetworkServiceAddressEnumeratorClass;
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)
405 static GSocketAddress *
406 g_network_service_address_enumerator_next (GSocketAddressEnumerator *enumerator,
407 GCancellable *cancellable,
408 GError **error)
410 GNetworkServiceAddressEnumerator *srv_enum =
411 G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator);
412 GSocketAddress *ret = NULL;
414 /* If we haven't yet resolved srv, do that */
415 if (!srv_enum->srv->priv->targets)
417 GList *targets;
418 GError *my_error = NULL;
420 targets = g_resolver_lookup_service (srv_enum->resolver,
421 srv_enum->srv->priv->service,
422 srv_enum->srv->priv->protocol,
423 srv_enum->srv->priv->domain,
424 cancellable, &my_error);
425 if (!targets && g_error_matches (my_error, G_RESOLVER_ERROR,
426 G_RESOLVER_ERROR_NOT_FOUND))
428 targets = g_network_service_fallback_targets (srv_enum->srv);
429 if (targets)
430 g_clear_error (&my_error);
433 if (my_error)
435 g_propagate_error (error, my_error);
436 return NULL;
439 srv_enum->srv->priv->targets = targets;
440 srv_enum->t = srv_enum->srv->priv->targets;
443 /* Delegate to GNetworkAddress */
446 if (srv_enum->addr_enum == NULL && srv_enum->t)
448 GError *error = NULL;
449 gchar *uri;
450 gchar *hostname;
451 GSocketConnectable *addr;
452 GSrvTarget *target = srv_enum->t->data;
454 srv_enum->t = g_list_next (srv_enum->t);
456 hostname = g_hostname_to_ascii (g_srv_target_get_hostname (target));
458 if (hostname == NULL)
460 if (srv_enum->error == NULL)
461 srv_enum->error =
462 g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
463 "Received invalid hostname '%s' from GSrvTarget",
464 g_srv_target_get_hostname (target));
465 continue;
468 uri = _g_uri_from_authority (g_network_service_get_scheme (srv_enum->srv),
469 hostname,
470 g_srv_target_get_port (target),
471 NULL);
472 g_free (hostname);
474 addr = g_network_address_parse_uri (uri,
475 g_srv_target_get_port (target),
476 &error);
477 g_free (uri);
479 if (addr == NULL)
481 if (srv_enum->error == NULL)
482 srv_enum->error = error;
483 else
484 g_error_free (error);
485 continue;
488 if (srv_enum->use_proxy)
489 srv_enum->addr_enum = g_socket_connectable_proxy_enumerate (addr);
490 else
491 srv_enum->addr_enum = g_socket_connectable_enumerate (addr);
492 g_object_unref (addr);
495 if (srv_enum->addr_enum)
497 GError *error = NULL;
499 ret = g_socket_address_enumerator_next (srv_enum->addr_enum,
500 cancellable,
501 &error);
503 if (error)
505 if (srv_enum->error == NULL)
506 srv_enum->error = error;
507 else
508 g_error_free (error);
511 if (!ret)
513 g_object_unref (srv_enum->addr_enum);
514 srv_enum->addr_enum = NULL;
518 while (srv_enum->addr_enum == NULL && srv_enum->t);
520 if (ret == NULL && srv_enum->error)
522 g_propagate_error (error, srv_enum->error);
523 srv_enum->error = NULL;
526 return ret;
529 static void next_async_resolved_targets (GObject *source_object,
530 GAsyncResult *result,
531 gpointer user_data);
532 static void next_async_have_targets (GTask *srv_enum);
533 static void next_async_have_address (GObject *source_object,
534 GAsyncResult *result,
535 gpointer user_data);
537 static void
538 g_network_service_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
539 GCancellable *cancellable,
540 GAsyncReadyCallback callback,
541 gpointer user_data)
543 GNetworkServiceAddressEnumerator *srv_enum =
544 G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (enumerator);
545 GTask *task;
547 task = g_task_new (enumerator, cancellable, callback, user_data);
548 g_task_set_source_tag (task, g_network_service_address_enumerator_next_async);
550 /* If we haven't yet resolved srv, do that */
551 if (!srv_enum->srv->priv->targets)
553 g_resolver_lookup_service_async (srv_enum->resolver,
554 srv_enum->srv->priv->service,
555 srv_enum->srv->priv->protocol,
556 srv_enum->srv->priv->domain,
557 cancellable,
558 next_async_resolved_targets,
559 task);
561 else
562 next_async_have_targets (task);
565 static void
566 next_async_resolved_targets (GObject *source_object,
567 GAsyncResult *result,
568 gpointer user_data)
570 GTask *task = user_data;
571 GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task);
572 GError *error = NULL;
573 GList *targets;
575 targets = g_resolver_lookup_service_finish (srv_enum->resolver,
576 result, &error);
578 if (!targets && g_error_matches (error, G_RESOLVER_ERROR,
579 G_RESOLVER_ERROR_NOT_FOUND))
581 targets = g_network_service_fallback_targets (srv_enum->srv);
582 if (targets)
583 g_clear_error (&error);
586 if (error)
588 g_task_return_error (task, error);
589 g_object_unref (task);
591 else
593 srv_enum->t = srv_enum->srv->priv->targets = targets;
594 next_async_have_targets (task);
598 static void
599 next_async_have_targets (GTask *task)
601 GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task);
603 /* Delegate to GNetworkAddress */
604 if (srv_enum->addr_enum == NULL && srv_enum->t)
606 GSocketConnectable *addr;
607 GSrvTarget *target = srv_enum->t->data;
609 srv_enum->t = g_list_next (srv_enum->t);
610 addr = g_network_address_new (g_srv_target_get_hostname (target),
611 (guint16) g_srv_target_get_port (target));
613 if (srv_enum->use_proxy)
614 srv_enum->addr_enum = g_socket_connectable_proxy_enumerate (addr);
615 else
616 srv_enum->addr_enum = g_socket_connectable_enumerate (addr);
618 g_object_unref (addr);
621 if (srv_enum->addr_enum)
623 g_socket_address_enumerator_next_async (srv_enum->addr_enum,
624 g_task_get_cancellable (task),
625 next_async_have_address,
626 task);
628 else
630 if (srv_enum->error)
632 g_task_return_error (task, srv_enum->error);
633 srv_enum->error = NULL;
635 else
636 g_task_return_pointer (task, NULL, NULL);
638 g_object_unref (task);
642 static void
643 next_async_have_address (GObject *source_object,
644 GAsyncResult *result,
645 gpointer user_data)
647 GTask *task = user_data;
648 GNetworkServiceAddressEnumerator *srv_enum = g_task_get_source_object (task);
649 GSocketAddress *address;
650 GError *error = NULL;
652 address = g_socket_address_enumerator_next_finish (srv_enum->addr_enum,
653 result,
654 &error);
656 if (error)
658 if (srv_enum->error == NULL)
659 srv_enum->error = error;
660 else
661 g_error_free (error);
664 if (!address)
666 g_object_unref (srv_enum->addr_enum);
667 srv_enum->addr_enum = NULL;
669 next_async_have_targets (task);
671 else
673 g_task_return_pointer (task, address, g_object_unref);
674 g_object_unref (task);
678 static GSocketAddress *
679 g_network_service_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
680 GAsyncResult *result,
681 GError **error)
683 return g_task_propagate_pointer (G_TASK (result), error);
686 static void
687 _g_network_service_address_enumerator_init (GNetworkServiceAddressEnumerator *enumerator)
691 static void
692 g_network_service_address_enumerator_finalize (GObject *object)
694 GNetworkServiceAddressEnumerator *srv_enum =
695 G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (object);
697 if (srv_enum->srv)
698 g_object_unref (srv_enum->srv);
700 if (srv_enum->addr_enum)
701 g_object_unref (srv_enum->addr_enum);
703 if (srv_enum->resolver)
704 g_object_unref (srv_enum->resolver);
706 if (srv_enum->error)
707 g_error_free (srv_enum->error);
709 G_OBJECT_CLASS (_g_network_service_address_enumerator_parent_class)->finalize (object);
712 static void
713 _g_network_service_address_enumerator_class_init (GNetworkServiceAddressEnumeratorClass *srvenum_class)
715 GObjectClass *object_class = G_OBJECT_CLASS (srvenum_class);
716 GSocketAddressEnumeratorClass *enumerator_class =
717 G_SOCKET_ADDRESS_ENUMERATOR_CLASS (srvenum_class);
719 enumerator_class->next = g_network_service_address_enumerator_next;
720 enumerator_class->next_async = g_network_service_address_enumerator_next_async;
721 enumerator_class->next_finish = g_network_service_address_enumerator_next_finish;
723 object_class->finalize = g_network_service_address_enumerator_finalize;
726 static GSocketAddressEnumerator *
727 g_network_service_connectable_enumerate (GSocketConnectable *connectable)
729 GNetworkServiceAddressEnumerator *srv_enum;
731 srv_enum = g_object_new (G_TYPE_NETWORK_SERVICE_ADDRESS_ENUMERATOR, NULL);
732 srv_enum->srv = g_object_ref (connectable);
733 srv_enum->resolver = g_resolver_get_default ();
734 srv_enum->use_proxy = FALSE;
736 return G_SOCKET_ADDRESS_ENUMERATOR (srv_enum);
739 static GSocketAddressEnumerator *
740 g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable)
742 GSocketAddressEnumerator *addr_enum;
743 GNetworkServiceAddressEnumerator *srv_enum;
745 addr_enum = g_network_service_connectable_enumerate (connectable);
746 srv_enum = G_NETWORK_SERVICE_ADDRESS_ENUMERATOR (addr_enum);
747 srv_enum->use_proxy = TRUE;
749 return addr_enum;
752 static gchar *
753 g_network_service_connectable_to_string (GSocketConnectable *connectable)
755 GNetworkService *service;
757 service = G_NETWORK_SERVICE (connectable);
759 return g_strdup_printf ("(%s, %s, %s, %s)", service->priv->service,
760 service->priv->protocol, service->priv->domain,
761 service->priv->scheme);