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