2e4042cc065877887e912321eab870c25bc2468b
[platform/upstream/glib.git] / gio / gnetworkaddress.c
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 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, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include <glib.h>
25 #include "glibintl.h"
26
27 #include <stdlib.h>
28 #include "gnetworkaddress.h"
29 #include "gasyncresult.h"
30 #include "ginetaddress.h"
31 #include "ginetsocketaddress.h"
32 #include "gnetworkingprivate.h"
33 #include "gproxyaddressenumerator.h"
34 #include "gresolver.h"
35 #include "gtask.h"
36 #include "gsocketaddressenumerator.h"
37 #include "gioerror.h"
38 #include "gsocketconnectable.h"
39
40 #include <string.h>
41
42
43 /**
44  * SECTION:gnetworkaddress
45  * @short_description: A GSocketConnectable for resolving hostnames
46  * @include: gio/gio.h
47  *
48  * #GNetworkAddress provides an easy way to resolve a hostname and
49  * then attempt to connect to that host, handling the possibility of
50  * multiple IP addresses and multiple address families.
51  *
52  * See #GSocketConnectable for and example of using the connectable
53  * interface.
54  */
55
56 /**
57  * GNetworkAddress:
58  *
59  * A #GSocketConnectable for resolving a hostname and connecting to
60  * that host.
61  */
62
63 struct _GNetworkAddressPrivate {
64   gchar *hostname;
65   guint16 port;
66   GList *sockaddrs;
67   gchar *scheme;
68
69   gint64 resolver_serial;
70 };
71
72 enum {
73   PROP_0,
74   PROP_HOSTNAME,
75   PROP_PORT,
76   PROP_SCHEME,
77 };
78
79 static void g_network_address_set_property (GObject      *object,
80                                             guint         prop_id,
81                                             const GValue *value,
82                                             GParamSpec   *pspec);
83 static void g_network_address_get_property (GObject      *object,
84                                             guint         prop_id,
85                                             GValue       *value,
86                                             GParamSpec   *pspec);
87
88 static void                      g_network_address_connectable_iface_init       (GSocketConnectableIface *iface);
89 static GSocketAddressEnumerator *g_network_address_connectable_enumerate        (GSocketConnectable      *connectable);
90 static GSocketAddressEnumerator *g_network_address_connectable_proxy_enumerate  (GSocketConnectable      *connectable);
91
92 G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
93                          G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
94                                                 g_network_address_connectable_iface_init))
95
96 static void
97 g_network_address_finalize (GObject *object)
98 {
99   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
100
101   g_free (addr->priv->hostname);
102   g_free (addr->priv->scheme);
103
104   if (addr->priv->sockaddrs)
105     {
106       GList *a;
107
108       for (a = addr->priv->sockaddrs; a; a = a->next)
109         g_object_unref (a->data);
110       g_list_free (addr->priv->sockaddrs);
111     }
112
113   G_OBJECT_CLASS (g_network_address_parent_class)->finalize (object);
114 }
115
116 static void
117 g_network_address_class_init (GNetworkAddressClass *klass)
118 {
119   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120
121   g_type_class_add_private (klass, sizeof (GNetworkAddressPrivate));
122
123   gobject_class->set_property = g_network_address_set_property;
124   gobject_class->get_property = g_network_address_get_property;
125   gobject_class->finalize = g_network_address_finalize;
126
127   g_object_class_install_property (gobject_class, PROP_HOSTNAME,
128                                    g_param_spec_string ("hostname",
129                                                         P_("Hostname"),
130                                                         P_("Hostname to resolve"),
131                                                         NULL,
132                                                         G_PARAM_READWRITE |
133                                                         G_PARAM_CONSTRUCT_ONLY |
134                                                         G_PARAM_STATIC_STRINGS));
135   g_object_class_install_property (gobject_class, PROP_PORT,
136                                    g_param_spec_uint ("port",
137                                                       P_("Port"),
138                                                       P_("Network port"),
139                                                       0, 65535, 0,
140                                                       G_PARAM_READWRITE |
141                                                       G_PARAM_CONSTRUCT_ONLY |
142                                                       G_PARAM_STATIC_STRINGS));
143
144   g_object_class_install_property (gobject_class, PROP_SCHEME,
145                                    g_param_spec_string ("scheme",
146                                                         P_("Scheme"),
147                                                         P_("URI Scheme"),
148                                                         NULL,
149                                                         G_PARAM_READWRITE |
150                                                         G_PARAM_CONSTRUCT_ONLY |
151                                                         G_PARAM_STATIC_STRINGS));
152 }
153
154 static void
155 g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
156 {
157   connectable_iface->enumerate  = g_network_address_connectable_enumerate;
158   connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate;
159 }
160
161 static void
162 g_network_address_init (GNetworkAddress *addr)
163 {
164   addr->priv = G_TYPE_INSTANCE_GET_PRIVATE (addr, G_TYPE_NETWORK_ADDRESS,
165                                             GNetworkAddressPrivate);
166 }
167
168 static void
169 g_network_address_set_property (GObject      *object,
170                                 guint         prop_id,
171                                 const GValue *value,
172                                 GParamSpec   *pspec)
173 {
174   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
175
176   switch (prop_id)
177     {
178     case PROP_HOSTNAME:
179       g_free (addr->priv->hostname);
180       addr->priv->hostname = g_value_dup_string (value);
181       break;
182
183     case PROP_PORT:
184       addr->priv->port = g_value_get_uint (value);
185       break;
186
187     case PROP_SCHEME:
188       if (addr->priv->scheme)
189         g_free (addr->priv->scheme);
190       addr->priv->scheme = g_value_dup_string (value);
191       break;
192
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195       break;
196     }
197
198 }
199
200 static void
201 g_network_address_get_property (GObject    *object,
202                                 guint       prop_id,
203                                 GValue     *value,
204                                 GParamSpec *pspec)
205 {
206   GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
207
208   switch (prop_id)
209     {
210     case PROP_HOSTNAME:
211       g_value_set_string (value, addr->priv->hostname);
212       break;
213
214     case PROP_PORT:
215       g_value_set_uint (value, addr->priv->port);
216       break;
217
218     case PROP_SCHEME:
219       g_value_set_string (value, addr->priv->scheme);
220       break;
221
222     default:
223       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224       break;
225     }
226
227 }
228
229 static void
230 g_network_address_set_addresses (GNetworkAddress *addr,
231                                  GList           *addresses,
232                                  guint64          resolver_serial)
233 {
234   GList *a;
235   GSocketAddress *sockaddr;
236
237   g_return_if_fail (addresses != NULL && addr->priv->sockaddrs == NULL);
238
239   for (a = addresses; a; a = a->next)
240     {
241       sockaddr = g_inet_socket_address_new (a->data, addr->priv->port);
242       addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
243       g_object_unref (a->data);
244     }
245   g_list_free (addresses);
246   addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
247
248   addr->priv->resolver_serial = resolver_serial;
249 }
250
251 static gboolean
252 g_network_address_parse_sockaddr (GNetworkAddress *addr)
253 {
254   struct addrinfo hints, *res = NULL;
255   GSocketAddress *sockaddr;
256   gchar port[32];
257
258   memset (&hints, 0, sizeof (hints));
259   hints.ai_flags = AI_NUMERICHOST
260 #ifdef AI_NUMERICSERV
261     | AI_NUMERICSERV
262 #endif
263     ;
264   g_snprintf (port, sizeof (port), "%u", addr->priv->port);
265
266   if (getaddrinfo (addr->priv->hostname, port, &hints, &res) != 0)
267     return FALSE;
268
269   sockaddr = g_socket_address_new_from_native (res->ai_addr, res->ai_addrlen);
270   freeaddrinfo (res);
271   if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
272     return FALSE;
273
274   addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
275   return TRUE;
276 }
277
278 /**
279  * g_network_address_new:
280  * @hostname: the hostname
281  * @port: the port
282  *
283  * Creates a new #GSocketConnectable for connecting to the given
284  * @hostname and @port.
285  *
286  * Return value: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
287  *
288  * Since: 2.22
289  */
290 GSocketConnectable *
291 g_network_address_new (const gchar *hostname,
292                        guint16      port)
293 {
294   return g_object_new (G_TYPE_NETWORK_ADDRESS,
295                        "hostname", hostname,
296                        "port", port,
297                        NULL);
298 }
299
300 /**
301  * g_network_address_parse:
302  * @host_and_port: the hostname and optionally a port
303  * @default_port: the default port if not in @host_and_port
304  * @error: a pointer to a #GError, or %NULL
305  *
306  * Creates a new #GSocketConnectable for connecting to the given
307  * @hostname and @port. May fail and return %NULL in case
308  * parsing @host_and_port fails.
309  *
310  * @host_and_port may be in any of a number of recognised formats; an IPv6
311  * address, an IPv4 address, or a domain name (in which case a DNS
312  * lookup is performed). Quoting with [] is supported for all address
313  * types. A port override may be specified in the usual way with a
314  * colon.
315  *
316  * If no port is specified in @host_and_port then @default_port will be
317  * used as the port number to connect to.
318  *
319  * In general, @host_and_port is expected to be provided by the user
320  * (allowing them to give the hostname, and a port overide if necessary)
321  * and @default_port is expected to be provided by the application.
322  *
323  * (The port component of @host_and_port can also be specified as a
324  * service name rather than as a numeric port, but this functionality
325  * is deprecated, because it depends on the contents of /etc/services,
326  * which is generally quite sparse on platforms other than Linux.)
327  *
328  * Return value: (transfer full): the new #GNetworkAddress, or %NULL on error
329  *
330  * Since: 2.22
331  */
332 GSocketConnectable *
333 g_network_address_parse (const gchar  *host_and_port,
334                          guint16       default_port,
335                          GError      **error)
336 {
337   GSocketConnectable *connectable;
338   const gchar *port;
339   guint16 portnum;
340   gchar *name;
341
342   g_return_val_if_fail (host_and_port != NULL, NULL);
343
344   port = NULL;
345   if (host_and_port[0] == '[')
346     /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
347     {
348       const gchar *end;
349
350       end = strchr (host_and_port, ']');
351       if (end == NULL)
352         {
353           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
354                        _("Hostname '%s' contains '[' but not ']'"), host_and_port);
355           return NULL;
356         }
357
358       if (end[1] == '\0')
359         port = NULL;
360       else if (end[1] == ':')
361         port = &end[2];
362       else
363         {
364           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
365                        "The ']' character (in hostname '%s') must come at the"
366                        " end or be immediately followed by ':' and a port",
367                        host_and_port);
368           return NULL;
369         }
370
371       name = g_strndup (host_and_port + 1, end - host_and_port - 1);
372     }
373
374   else if ((port = strchr (host_and_port, ':')))
375     /* string has a ':' in it */
376     {
377       /* skip ':' */
378       port++;
379
380       if (strchr (port, ':'))
381         /* more than one ':' in string */
382         {
383           /* this is actually an unescaped IPv6 address */
384           name = g_strdup (host_and_port);
385           port = NULL;
386         }
387       else
388         name = g_strndup (host_and_port, port - host_and_port - 1);
389     }
390
391   else
392     /* plain hostname, no port */
393     name = g_strdup (host_and_port);
394
395   if (port != NULL)
396     {
397       if (port[0] == '\0')
398         {
399           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
400                        "If a ':' character is given, it must be followed by a "
401                        "port (in hostname '%s').", host_and_port);
402           g_free (name);
403           return NULL;
404         }
405
406       else if ('0' <= port[0] && port[0] <= '9')
407         {
408           char *end;
409           long value;
410
411           value = strtol (port, &end, 10);
412           if (*end != '\0' || value < 0 || value > G_MAXUINT16)
413             {
414               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
415                            "Invalid numeric port '%s' specified in hostname '%s'",
416                            port, host_and_port);
417               g_free (name);
418               return NULL;
419             }
420
421           portnum = value;
422         }
423
424       else
425         {
426           struct servent *entry;
427
428           entry = getservbyname (port, "tcp");
429           if (entry == NULL)
430             {
431               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
432                            "Unknown service '%s' specified in hostname '%s'",
433                            port, host_and_port);
434 #ifdef HAVE_ENDSERVENT
435               endservent ();
436 #endif
437               g_free (name);
438               return NULL;
439             }
440
441           portnum = g_ntohs (entry->s_port);
442
443 #ifdef HAVE_ENDSERVENT
444           endservent ();
445 #endif
446         }
447     }
448   else
449     {
450       /* No port in host_and_port */
451       portnum = default_port;
452     }
453
454   connectable = g_network_address_new (name, portnum);
455   g_free (name);
456
457   return connectable;
458 }
459
460 /* Allowed characters outside alphanumeric for unreserved. */
461 #define G_URI_OTHER_UNRESERVED "-._~"
462
463 /* This or something equivalent will eventually go into glib/guri.h */
464 gboolean
465 _g_uri_parse_authority (const char  *uri,
466                         char       **host,
467                         guint16     *port,
468                         char       **userinfo)
469 {
470   char *tmp_str;
471   const char *start, *p;
472   char c;
473
474   g_return_val_if_fail (uri != NULL, FALSE);
475
476   if (host)
477     *host = NULL;
478
479   if (port)
480     *port = 0;
481
482   if (userinfo)
483     *userinfo = NULL;
484
485   /* From RFC 3986 Decodes:
486    * URI          = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
487    * hier-part    = "//" authority path-abempty
488    * path-abempty = *( "/" segment )
489    * authority    = [ userinfo "@" ] host [ ":" port ]
490    */
491
492   /* Check we have a valid scheme */
493   tmp_str = g_uri_parse_scheme (uri);
494
495   if (tmp_str == NULL)
496     return FALSE;
497
498   g_free (tmp_str);
499
500   /* Decode hier-part:
501    *  hier-part   = "//" authority path-abempty
502    */
503   p = uri;
504   start = strstr (p, "//");
505
506   if (start == NULL)
507     return FALSE;
508
509   start += 2;
510
511   if (strchr (start, '@') != NULL)
512     {
513       /* Decode userinfo:
514        * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
515        * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
516        * pct-encoded   = "%" HEXDIG HEXDIG
517        */
518       p = start;
519       while (1)
520         {
521           c = *p++;
522
523           if (c == '@')
524             break;
525
526           /* pct-encoded */
527           if (c == '%')
528             {
529               if (!(g_ascii_isxdigit (p[0]) ||
530                     g_ascii_isxdigit (p[1])))
531                 return FALSE;
532
533               p++;
534
535               continue;
536             }
537
538           /* unreserved /  sub-delims / : */
539           if (!(g_ascii_isalnum (c) ||
540                 strchr (G_URI_OTHER_UNRESERVED, c) ||
541                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
542                 c == ':'))
543             return FALSE;
544         }
545
546       if (userinfo)
547         *userinfo = g_strndup (start, p - start - 1);
548
549       start = p;
550     }
551   else
552     {
553       p = start;
554     }
555
556
557   /* decode host:
558    * host          = IP-literal / IPv4address / reg-name
559    * reg-name      = *( unreserved / pct-encoded / sub-delims )
560    */
561
562   /* If IPv6 or IPvFuture */
563   if (*p == '[')
564     {
565       start++;
566       p++;
567       while (1)
568         {
569           c = *p++;
570
571           if (c == ']')
572             break;
573
574           /* unreserved /  sub-delims */
575           if (!(g_ascii_isalnum (c) ||
576                 strchr (G_URI_OTHER_UNRESERVED, c) ||
577                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
578                 c == ':' ||
579                 c == '.'))
580             goto error;
581         }
582     }
583   else
584     {
585       while (1)
586         {
587           c = *p++;
588
589           if (c == ':' ||
590               c == '/' ||
591               c == '?' ||
592               c == '#' ||
593               c == '\0')
594             break;
595
596           /* pct-encoded */
597           if (c == '%')
598             {
599               if (!(g_ascii_isxdigit (p[0]) ||
600                     g_ascii_isxdigit (p[1])))
601                 goto error;
602
603               p++;
604
605               continue;
606             }
607
608           /* unreserved /  sub-delims */
609           if (!(g_ascii_isalnum (c) ||
610                 strchr (G_URI_OTHER_UNRESERVED, c) ||
611                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c)))
612             goto error;
613         }
614     }
615
616   if (host)
617     *host = g_uri_unescape_segment (start, p - 1, NULL);
618
619   if (c == ':')
620     {
621       /* Decode pot:
622        *  port          = *DIGIT
623        */
624       guint tmp = 0;
625
626       while (1)
627         {
628           c = *p++;
629
630           if (c == '/' ||
631               c == '?' ||
632               c == '#' ||
633               c == '\0')
634             break;
635
636           if (!g_ascii_isdigit (c))
637             goto error;
638
639           tmp = (tmp * 10) + (c - '0');
640
641           if (tmp > 65535)
642             goto error;
643         }
644       if (port)
645         *port = (guint16) tmp;
646     }
647
648   return TRUE;
649
650 error:
651   if (host && *host)
652     {
653       g_free (*host);
654       *host = NULL;
655     }
656
657   if (userinfo && *userinfo)
658     {
659       g_free (*userinfo);
660       *userinfo = NULL;
661     }
662
663   return FALSE;
664 }
665
666 gchar *
667 _g_uri_from_authority (const gchar *protocol,
668                        const gchar *host,
669                        guint        port,
670                        const gchar *userinfo)
671 {
672   GString *uri;
673
674   uri = g_string_new (protocol);
675   g_string_append (uri, "://");
676
677   if (userinfo)
678     {
679       g_string_append_uri_escaped (uri, userinfo, G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, FALSE);
680       g_string_append_c (uri, '@');
681     }
682
683   if (g_hostname_is_non_ascii (host))
684     {
685       gchar *ace_encoded = g_hostname_to_ascii (host);
686
687       if (!ace_encoded)
688         {
689           g_string_free (uri, TRUE);
690           return NULL;
691         }
692       g_string_append (uri, ace_encoded);
693       g_free (ace_encoded);
694     }
695   else if (strchr (host, ':'))
696     g_string_append_printf (uri, "[%s]", host);
697   else
698     g_string_append (uri, host);
699
700   if (port != 0)
701     g_string_append_printf (uri, ":%u", port);
702
703   return g_string_free (uri, FALSE);
704 }
705
706 /**
707  * g_network_address_parse_uri:
708  * @uri: the hostname and optionally a port
709  * @default_port: The default port if none is found in the URI
710  * @error: a pointer to a #GError, or %NULL
711  *
712  * Creates a new #GSocketConnectable for connecting to the given
713  * @uri. May fail and return %NULL in case parsing @uri fails.
714  *
715  * Using this rather than g_network_address_new() or
716  * g_network_address_parse() allows #GSocketClient to determine
717  * when to use application-specific proxy protocols.
718  *
719  * Return value: (transfer full): the new #GNetworkAddress, or %NULL on error
720  *
721  * Since: 2.26
722  */
723 GSocketConnectable *
724 g_network_address_parse_uri (const gchar  *uri,
725                              guint16       default_port,
726                              GError      **error)
727 {
728   GSocketConnectable *conn;
729   gchar *scheme;
730   gchar *hostname;
731   guint16 port;
732
733   if (!_g_uri_parse_authority (uri, &hostname, &port, NULL))
734     {
735       g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
736                    "Invalid URI '%s'",
737                    uri);
738       return NULL;
739     }
740
741   if (port == 0)
742     port = default_port;
743
744   scheme = g_uri_parse_scheme (uri);
745
746   conn = g_object_new (G_TYPE_NETWORK_ADDRESS,
747                        "hostname", hostname,
748                        "port", port,
749                        "scheme", scheme,
750                        NULL);
751
752   g_free (scheme);
753   g_free (hostname);
754
755   return conn;
756 }
757
758 /**
759  * g_network_address_get_hostname:
760  * @addr: a #GNetworkAddress
761  *
762  * Gets @addr's hostname. This might be either UTF-8 or ASCII-encoded,
763  * depending on what @addr was created with.
764  *
765  * Return value: @addr's hostname
766  *
767  * Since: 2.22
768  */
769 const gchar *
770 g_network_address_get_hostname (GNetworkAddress *addr)
771 {
772   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
773
774   return addr->priv->hostname;
775 }
776
777 /**
778  * g_network_address_get_port:
779  * @addr: a #GNetworkAddress
780  *
781  * Gets @addr's port number
782  *
783  * Return value: @addr's port (which may be 0)
784  *
785  * Since: 2.22
786  */
787 guint16
788 g_network_address_get_port (GNetworkAddress *addr)
789 {
790   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
791
792   return addr->priv->port;
793 }
794
795 /**
796  * g_network_address_get_scheme:
797  * @addr: a #GNetworkAddress
798  *
799  * Gets @addr's scheme
800  *
801  * Return value: @addr's scheme (%NULL if not built from URI)
802  *
803  * Since: 2.26
804  */
805 const gchar *
806 g_network_address_get_scheme (GNetworkAddress *addr)
807 {
808   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
809
810   return addr->priv->scheme;
811 }
812
813 #define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
814 #define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
815
816 typedef struct {
817   GSocketAddressEnumerator parent_instance;
818
819   GNetworkAddress *addr;
820   GList *addresses;
821   GList *next;
822 } GNetworkAddressAddressEnumerator;
823
824 typedef struct {
825   GSocketAddressEnumeratorClass parent_class;
826
827 } GNetworkAddressAddressEnumeratorClass;
828
829 static GType _g_network_address_address_enumerator_get_type (void);
830 G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
831
832 static void
833 g_network_address_address_enumerator_finalize (GObject *object)
834 {
835   GNetworkAddressAddressEnumerator *addr_enum =
836     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
837
838   g_object_unref (addr_enum->addr);
839
840   G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
841 }
842
843 static GSocketAddress *
844 g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
845                                            GCancellable              *cancellable,
846                                            GError                   **error)
847 {
848   GNetworkAddressAddressEnumerator *addr_enum =
849     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
850   GSocketAddress *sockaddr;
851
852   if (addr_enum->addresses == NULL)
853     {
854       GNetworkAddress *addr = addr_enum->addr;
855       GResolver *resolver = g_resolver_get_default ();
856       gint64 serial = g_resolver_get_serial (resolver);
857
858       if (addr->priv->resolver_serial != 0 &&
859           addr->priv->resolver_serial != serial)
860         {
861           /* Resolver has reloaded, discard cached addresses */
862           g_list_free_full (addr->priv->sockaddrs, g_object_unref);
863           addr->priv->sockaddrs = NULL;
864         }
865
866       if (!addr->priv->sockaddrs)
867         g_network_address_parse_sockaddr (addr);
868       if (!addr->priv->sockaddrs)
869         {
870           GList *addresses;
871
872           addresses = g_resolver_lookup_by_name (resolver,
873                                                  addr->priv->hostname,
874                                                  cancellable, error);
875           if (!addresses)
876             {
877               g_object_unref (resolver);
878               return NULL;
879             }
880
881           g_network_address_set_addresses (addr, addresses, serial);
882         }
883           
884       addr_enum->addresses = addr->priv->sockaddrs;
885       addr_enum->next = addr_enum->addresses;
886       g_object_unref (resolver);
887     }
888
889   if (addr_enum->next == NULL)
890     return NULL;
891
892   sockaddr = addr_enum->next->data;
893   addr_enum->next = addr_enum->next->next;
894   return g_object_ref (sockaddr);
895 }
896
897 static void
898 have_addresses (GNetworkAddressAddressEnumerator *addr_enum,
899                 GTask *task, GError *error)
900 {
901   GSocketAddress *sockaddr;
902
903   addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
904   addr_enum->next = addr_enum->addresses;
905
906   if (addr_enum->next)
907     {
908       sockaddr = g_object_ref (addr_enum->next->data);
909       addr_enum->next = addr_enum->next->next;
910     }
911   else
912     sockaddr = NULL;
913
914   if (error)
915     g_task_return_error (task, error);
916   else
917     g_task_return_pointer (task, sockaddr, g_object_unref);
918   g_object_unref (task);
919 }
920
921 static void
922 got_addresses (GObject      *source_object,
923                GAsyncResult *result,
924                gpointer      user_data)
925 {
926   GTask *task = user_data;
927   GNetworkAddressAddressEnumerator *addr_enum = g_task_get_source_object (task);
928   GResolver *resolver = G_RESOLVER (source_object);
929   GList *addresses;
930   GError *error = NULL;
931
932   if (!addr_enum->addr->priv->sockaddrs)
933     {
934       addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
935
936       if (!error)
937         {
938           g_network_address_set_addresses (addr_enum->addr, addresses,
939                                            g_resolver_get_serial (resolver));
940         }
941     }
942   have_addresses (addr_enum, task, error);
943 }
944
945 static void
946 g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
947                                                  GCancellable              *cancellable,
948                                                  GAsyncReadyCallback        callback,
949                                                  gpointer                   user_data)
950 {
951   GNetworkAddressAddressEnumerator *addr_enum =
952     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
953   GSocketAddress *sockaddr;
954   GTask *task;
955
956   task = g_task_new (addr_enum, cancellable, callback, user_data);
957
958   if (addr_enum->addresses == NULL)
959     {
960       GNetworkAddress *addr = addr_enum->addr;
961       GResolver *resolver = g_resolver_get_default ();
962       gint64 serial = g_resolver_get_serial (resolver);
963
964       if (addr->priv->resolver_serial != 0 &&
965           addr->priv->resolver_serial != serial)
966         {
967           /* Resolver has reloaded, discard cached addresses */
968           g_list_free_full (addr->priv->sockaddrs, g_object_unref);
969           addr->priv->sockaddrs = NULL;
970         }
971
972       if (!addr->priv->sockaddrs)
973         {
974           if (g_network_address_parse_sockaddr (addr))
975             have_addresses (addr_enum, task, NULL);
976           else
977             {
978               g_resolver_lookup_by_name_async (resolver,
979                                                addr->priv->hostname,
980                                                cancellable,
981                                                got_addresses, task);
982             }
983           g_object_unref (resolver);
984           return;
985         }
986
987       addr_enum->addresses = addr->priv->sockaddrs;
988       addr_enum->next = addr_enum->addresses;
989       g_object_unref (resolver);
990     }
991
992   if (addr_enum->next)
993     {
994       sockaddr = g_object_ref (addr_enum->next->data);
995       addr_enum->next = addr_enum->next->next;
996     }
997   else
998     sockaddr = NULL;
999
1000   g_task_return_pointer (task, sockaddr, g_object_unref);
1001   g_object_unref (task);
1002 }
1003
1004 static GSocketAddress *
1005 g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
1006                                                   GAsyncResult              *result,
1007                                                   GError                   **error)
1008 {
1009   g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
1010
1011   return g_task_propagate_pointer (G_TASK (result), error);
1012 }
1013
1014 static void
1015 _g_network_address_address_enumerator_init (GNetworkAddressAddressEnumerator *enumerator)
1016 {
1017 }
1018
1019 static void
1020 _g_network_address_address_enumerator_class_init (GNetworkAddressAddressEnumeratorClass *addrenum_class)
1021 {
1022   GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
1023   GSocketAddressEnumeratorClass *enumerator_class =
1024     G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
1025
1026   enumerator_class->next = g_network_address_address_enumerator_next;
1027   enumerator_class->next_async = g_network_address_address_enumerator_next_async;
1028   enumerator_class->next_finish = g_network_address_address_enumerator_next_finish;
1029   object_class->finalize = g_network_address_address_enumerator_finalize;
1030 }
1031
1032 static GSocketAddressEnumerator *
1033 g_network_address_connectable_enumerate (GSocketConnectable *connectable)
1034 {
1035   GNetworkAddressAddressEnumerator *addr_enum;
1036
1037   addr_enum = g_object_new (G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, NULL);
1038   addr_enum->addr = g_object_ref (connectable);
1039
1040   return (GSocketAddressEnumerator *)addr_enum;
1041 }
1042
1043 static GSocketAddressEnumerator *
1044 g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
1045 {
1046   GNetworkAddress *self = G_NETWORK_ADDRESS (connectable);
1047   GSocketAddressEnumerator *proxy_enum;
1048   gchar *uri;
1049
1050   uri = _g_uri_from_authority (self->priv->scheme ? self->priv->scheme : "none",
1051                                self->priv->hostname,
1052                                self->priv->port,
1053                                NULL);
1054
1055   proxy_enum = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1056                              "connectable", connectable,
1057                              "uri", uri,
1058                              NULL);
1059
1060   g_free (uri);
1061
1062   return proxy_enum;
1063 }