80a3e31a699f116265ca5bbe82f4947ea319d47c
[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: 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. Ports may be given as decimal numbers or symbolic names (in
283  * which case an /etc/services lookup is performed).
284  *
285  * If no port is specified in @host_and_port then @default_port will be
286  * used as the port number to connect to.
287  *
288  * In general, @host_and_port is expected to be provided by the user
289  * (allowing them to give the hostname, and a port overide if necessary)
290  * and @default_port is expected to be provided by the application.
291  *
292  * Return value: the new #GNetworkAddress, or %NULL on error
293  *
294  * Since: 2.22
295  */
296 GSocketConnectable *
297 g_network_address_parse (const gchar  *host_and_port,
298                          guint16       default_port,
299                          GError      **error)
300 {
301   GSocketConnectable *connectable;
302   const gchar *port;
303   guint16 portnum;
304   gchar *name;
305
306   g_return_val_if_fail (host_and_port != NULL, NULL);
307
308   port = NULL;
309   if (host_and_port[0] == '[')
310     /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
311     {
312       const gchar *end;
313
314       end = strchr (host_and_port, ']');
315       if (end == NULL)
316         {
317           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
318                        _("Hostname '%s' contains '[' but not ']'"), host_and_port);
319           return NULL;
320         }
321
322       if (end[1] == '\0')
323         port = NULL;
324       else if (end[1] == ':')
325         port = &end[2];
326       else
327         {
328           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
329                        "The ']' character (in hostname '%s') must come at the"
330                        " end or be immediately followed by ':' and a port",
331                        host_and_port);
332           return NULL;
333         }
334
335       name = g_strndup (host_and_port + 1, end - host_and_port - 1);
336     }
337
338   else if ((port = strchr (host_and_port, ':')))
339     /* string has a ':' in it */
340     {
341       /* skip ':' */
342       port++;
343
344       if (strchr (port, ':'))
345         /* more than one ':' in string */
346         {
347           /* this is actually an unescaped IPv6 address */
348           name = g_strdup (host_and_port);
349           port = NULL;
350         }
351       else
352         name = g_strndup (host_and_port, port - host_and_port - 1);
353     }
354
355   else
356     /* plain hostname, no port */
357     name = g_strdup (host_and_port);
358
359   if (port != NULL)
360     {
361       if (port[0] == '\0')
362         {
363           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
364                        "If a ':' character is given, it must be followed by a "
365                        "port (in hostname '%s').", host_and_port);
366           g_free (name);
367           return NULL;
368         }
369
370       else if ('0' <= port[0] && port[0] <= '9')
371         {
372           char *end;
373           long value;
374
375           value = strtol (port, &end, 10);
376           if (*end != '\0' || value < 0 || value > G_MAXUINT16)
377             {
378               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
379                            "Invalid numeric port '%s' specified in hostname '%s'",
380                            port, host_and_port);
381               g_free (name);
382               return NULL;
383             }
384
385           portnum = value;
386         }
387
388       else
389         {
390           struct servent *entry;
391
392           entry = getservbyname (port, "tcp");
393           if (entry == NULL)
394             {
395               g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
396                            "Unknown service '%s' specified in hostname '%s'",
397                            port, host_and_port);
398 #ifdef HAVE_ENDSERVENT
399               endservent ();
400 #endif
401               g_free (name);
402               return NULL;
403             }
404
405           portnum = g_ntohs (entry->s_port);
406
407 #ifdef HAVE_ENDSERVENT
408           endservent ();
409 #endif
410         }
411     }
412   else
413     {
414       /* No port in host_and_port */
415       portnum = default_port;
416     }
417
418   connectable = g_network_address_new (name, portnum);
419   g_free (name);
420
421   return connectable;
422 }
423
424 /* Allowed characters outside alphanumeric for unreserved. */
425 #define G_URI_OTHER_UNRESERVED "-._~"
426
427 /* This or something equivalent will eventually go into glib/guri.h */
428 gboolean
429 _g_uri_parse_authority (const char  *uri,
430                         char       **host,
431                         guint16     *port,
432                         char       **userinfo)
433 {
434   char *tmp_str;
435   const char *start, *p;
436   char c;
437
438   g_return_val_if_fail (uri != NULL, FALSE);
439
440   if (host)
441     *host = NULL;
442
443   if (port)
444     *port = 0;
445
446   if (userinfo)
447     *userinfo = NULL;
448
449   /* From RFC 3986 Decodes:
450    * URI          = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
451    * hier-part    = "//" authority path-abempty
452    * path-abempty = *( "/" segment )
453    * authority    = [ userinfo "@" ] host [ ":" port ]
454    */
455
456   /* Check we have a valid scheme */
457   tmp_str = g_uri_parse_scheme (uri);
458
459   if (tmp_str == NULL)
460     return FALSE;
461
462   g_free (tmp_str);
463
464   /* Decode hier-part:
465    *  hier-part   = "//" authority path-abempty
466    */
467   p = uri;
468   start = strstr (p, "//");
469
470   if (start == NULL)
471     return FALSE;
472
473   start += 2;
474   p = strchr (start, '@');
475
476   if (p != NULL)
477     {
478       /* Decode userinfo:
479        * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
480        * unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
481        * pct-encoded   = "%" HEXDIG HEXDIG
482        */
483       while (1)
484         {
485           c = *p++;
486
487           if (c == '@')
488             break;
489
490           /* pct-encoded */
491           if (c == '%')
492             {
493               if (!(g_ascii_isxdigit (p[0]) ||
494                     g_ascii_isxdigit (p[1])))
495                 return FALSE;
496
497               p++;
498
499               continue;
500             }
501
502           /* unreserved /  sub-delims / : */
503           if (!(g_ascii_isalnum(c) ||
504                 strchr (G_URI_OTHER_UNRESERVED, c) ||
505                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
506                 c == ':'))
507             return FALSE;
508         }
509
510       if (userinfo)
511         *userinfo = g_strndup (start, p - start - 1);
512
513       start = p;
514     }
515   else
516     {
517       p = start;
518     }
519
520
521   /* decode host:
522    * host          = IP-literal / IPv4address / reg-name
523    * reg-name      = *( unreserved / pct-encoded / sub-delims )
524    */
525
526   /* If IPv6 or IPvFuture */
527   if (*p == '[')
528     {
529       while (1)
530         {
531           c = *p++;
532
533           if (c == ']')
534             break;
535
536           /* unreserved /  sub-delims */
537           if (!(g_ascii_isalnum(c) ||
538                 strchr (G_URI_OTHER_UNRESERVED, c) ||
539                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c) ||
540                 c == ':' ||
541                 c == '.'))
542             goto error;
543         }
544     }
545   else
546     {
547       while (1)
548         {
549           c = *p++;
550
551           if (c == ':' ||
552               c == '/' ||
553               c == '?' ||
554               c == '#' ||
555               c == '\0')
556             break;
557
558           /* pct-encoded */
559           if (c == '%')
560             {
561               if (!(g_ascii_isxdigit (p[0]) ||
562                     g_ascii_isxdigit (p[1])))
563                 goto error;
564
565               p++;
566
567               continue;
568             }
569
570           /* unreserved /  sub-delims */
571           if (!(g_ascii_isalnum(c) ||
572                 strchr (G_URI_OTHER_UNRESERVED, c) ||
573                 strchr (G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, c)))
574             goto error;
575         }
576     }
577
578   if (host)
579     *host = g_uri_unescape_segment (start, p - 1, NULL);
580
581   if (c == ':')
582     {
583       /* Decode pot:
584        *  port          = *DIGIT
585        */
586       guint tmp = 0;
587
588       while (1)
589         {
590           c = *p++;
591
592           if (c == '/' ||
593               c == '?' ||
594               c == '#' ||
595               c == '\0')
596             break;
597
598           if (!g_ascii_isdigit (c))
599             goto error;
600
601           tmp = (tmp * 10) + (c - '0');
602
603           if (tmp > 65535)
604             goto error;
605         }
606       if (port)
607         *port = (guint16) tmp;
608     }
609
610   return TRUE;
611
612 error:
613   if (host && *host)
614     {
615       g_free (*host);
616       *host = NULL;
617     }
618
619   if (userinfo && *userinfo)
620     {
621       g_free (*userinfo);
622       *userinfo = NULL;
623     }
624
625   return FALSE;
626 }
627
628 /**
629  * g_network_address_parse_uri:
630  * @uri: the hostname and optionally a port
631  * @default_port: The default port if none is found in the URI
632  * @error: a pointer to a #GError, or %NULL
633  *
634  * Creates a new #GSocketConnectable for connecting to the given
635  * @uri. May fail and return %NULL in case parsing @uri fails.
636  *
637  * Using this rather than g_network_address_new() or
638  * g_network_address_parse_host() allows #GSocketClient to determine
639  * when to use application-specific proxy protocols.
640  *
641  * Return value: the new #GNetworkAddress, or %NULL on error
642  *
643  * Since: 2.26
644  */
645 GSocketConnectable *
646 g_network_address_parse_uri (const gchar  *uri,
647                              guint16       default_port,
648                              GError      **error)
649 {
650   GSocketConnectable *conn;
651   gchar *scheme;
652   gchar *hostname;
653   guint16 port;
654
655   if (!_g_uri_parse_authority (uri, &hostname, &port, NULL))
656     {
657       g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
658                    "Invalid URI '%s'",
659                    uri);
660       return NULL;
661     }
662
663   if (port == 0)
664     port = default_port;
665
666   scheme = g_uri_parse_scheme (uri);
667
668   conn = g_object_new (G_TYPE_NETWORK_ADDRESS,
669                        "hostname", hostname,
670                        "port", port,
671                        "scheme", scheme,
672                        NULL);
673
674   g_free (scheme);
675   g_free (hostname);
676
677   return conn;
678 }
679
680 /**
681  * g_network_address_get_hostname:
682  * @addr: a #GNetworkAddress
683  *
684  * Gets @addr's hostname. This might be either UTF-8 or ASCII-encoded,
685  * depending on what @addr was created with.
686  *
687  * Return value: @addr's hostname
688  *
689  * Since: 2.22
690  */
691 const gchar *
692 g_network_address_get_hostname (GNetworkAddress *addr)
693 {
694   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
695
696   return addr->priv->hostname;
697 }
698
699 /**
700  * g_network_address_get_port:
701  * @addr: a #GNetworkAddress
702  *
703  * Gets @addr's port number
704  *
705  * Return value: @addr's port (which may be 0)
706  *
707  * Since: 2.22
708  */
709 guint16
710 g_network_address_get_port (GNetworkAddress *addr)
711 {
712   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
713
714   return addr->priv->port;
715 }
716
717 /**
718  * g_network_address_get_scheme:
719  * @addr: a #GNetworkAddress
720  *
721  * Gets @addr's scheme
722  *
723  * Return value: @addr's scheme (%NULL if not built from URI)
724  *
725  * Since: 2.26
726  */
727 const gchar *
728 g_network_address_get_scheme (GNetworkAddress *addr)
729 {
730   g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
731
732   return addr->priv->scheme;
733 }
734
735 #define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
736 #define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
737
738 typedef struct {
739   GSocketAddressEnumerator parent_instance;
740
741   GNetworkAddress *addr;
742   GList *a;
743 } GNetworkAddressAddressEnumerator;
744
745 typedef struct {
746   GSocketAddressEnumeratorClass parent_class;
747
748 } GNetworkAddressAddressEnumeratorClass;
749
750 G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
751
752 static void
753 g_network_address_address_enumerator_finalize (GObject *object)
754 {
755   GNetworkAddressAddressEnumerator *addr_enum =
756     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
757
758   g_object_unref (addr_enum->addr);
759
760   G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
761 }
762
763 static GSocketAddress *
764 g_network_address_address_enumerator_next (GSocketAddressEnumerator  *enumerator,
765                                            GCancellable              *cancellable,
766                                            GError                   **error)
767 {
768   GNetworkAddressAddressEnumerator *addr_enum =
769     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
770   GSocketAddress *sockaddr;
771
772   if (!addr_enum->addr->priv->sockaddrs)
773     {
774       GResolver *resolver = g_resolver_get_default ();
775       GList *addresses;
776
777       addresses = g_resolver_lookup_by_name (resolver,
778                                              addr_enum->addr->priv->hostname,
779                                              cancellable, error);
780       g_object_unref (resolver);
781
782       if (!addresses)
783         return NULL;
784
785       g_network_address_set_addresses (addr_enum->addr, addresses);
786       addr_enum->a = addr_enum->addr->priv->sockaddrs;
787     }
788
789   if (!addr_enum->a)
790     return NULL;
791   else
792     {
793       sockaddr = addr_enum->a->data;
794       addr_enum->a = addr_enum->a->next;
795       return g_object_ref (sockaddr);
796     }
797 }
798
799 static void
800 got_addresses (GObject      *source_object,
801                GAsyncResult *result,
802                gpointer      user_data)
803 {
804   GSimpleAsyncResult *simple = user_data;
805   GNetworkAddressAddressEnumerator *addr_enum =
806     g_simple_async_result_get_op_res_gpointer (simple);
807   GResolver *resolver = G_RESOLVER (source_object);
808   GList *addresses;
809   GError *error = NULL;
810
811   addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
812   if (!addr_enum->addr->priv->sockaddrs)
813     {
814       if (error)
815         {
816           g_simple_async_result_set_from_error (simple, error);
817           g_error_free (error);
818         }
819       else
820         {
821           g_network_address_set_addresses (addr_enum->addr, addresses);
822           addr_enum->a = addr_enum->addr->priv->sockaddrs;
823         }
824     }
825   else if (error)
826     g_error_free (error);
827
828   g_object_unref (resolver);
829
830   g_simple_async_result_complete (simple);
831   g_object_unref (simple);
832 }
833
834 static void
835 g_network_address_address_enumerator_next_async (GSocketAddressEnumerator  *enumerator,
836                                                  GCancellable              *cancellable,
837                                                  GAsyncReadyCallback        callback,
838                                                  gpointer                   user_data)
839 {
840   GNetworkAddressAddressEnumerator *addr_enum =
841     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
842   GSimpleAsyncResult *simple;
843
844   simple = g_simple_async_result_new (G_OBJECT (enumerator),
845                                       callback, user_data,
846                                       g_network_address_address_enumerator_next_async);
847
848   if (!addr_enum->addr->priv->sockaddrs)
849     {
850       GResolver *resolver = g_resolver_get_default ();
851
852       g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (addr_enum), g_object_unref);
853       g_resolver_lookup_by_name_async (resolver,
854                                        addr_enum->addr->priv->hostname,
855                                        cancellable,
856                                        got_addresses, simple);
857     }
858   else
859     {
860       g_simple_async_result_complete_in_idle (simple);
861       g_object_unref (simple);
862     }
863 }
864
865 static GSocketAddress *
866 g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator  *enumerator,
867                                                   GAsyncResult              *result,
868                                                   GError                   **error)
869 {
870   GNetworkAddressAddressEnumerator *addr_enum =
871     G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
872   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
873   GSocketAddress *sockaddr;
874
875   if (g_simple_async_result_propagate_error (simple, error))
876     return NULL;
877   else if (!addr_enum->a)
878     return NULL;
879   else
880     {
881       sockaddr = addr_enum->a->data;
882       addr_enum->a = addr_enum->a->next;
883       return g_object_ref (sockaddr);
884     }
885 }
886
887 static void
888 _g_network_address_address_enumerator_init (GNetworkAddressAddressEnumerator *enumerator)
889 {
890 }
891
892 static void
893 _g_network_address_address_enumerator_class_init (GNetworkAddressAddressEnumeratorClass *addrenum_class)
894 {
895   GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
896   GSocketAddressEnumeratorClass *enumerator_class =
897     G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
898
899   enumerator_class->next = g_network_address_address_enumerator_next;
900   enumerator_class->next_async = g_network_address_address_enumerator_next_async;
901   enumerator_class->next_finish = g_network_address_address_enumerator_next_finish;
902   object_class->finalize = g_network_address_address_enumerator_finalize;
903 }
904
905 static GSocketAddressEnumerator *
906 g_network_address_connectable_enumerate (GSocketConnectable *connectable)
907 {
908   GNetworkAddressAddressEnumerator *addr_enum;
909
910   addr_enum = g_object_new (G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, NULL);
911   addr_enum->addr = g_object_ref (connectable);
912
913   return (GSocketAddressEnumerator *)addr_enum;
914 }
915
916 static GSocketAddressEnumerator *
917 g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
918 {
919   GNetworkAddress *self = G_NETWORK_ADDRESS (connectable);
920   GSocketAddressEnumerator *proxy_enum;
921   gchar *uri;
922
923   uri = g_strdup_printf ("%s://%s:%u",
924                          self->priv->scheme ? self->priv->scheme : "none",
925                          self->priv->hostname, self->priv->port);
926
927   proxy_enum = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
928                              "connectable", connectable,
929                              "uri", uri,
930                              NULL);
931
932   g_free (uri);
933
934   return proxy_enum;
935 }