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