Branch data Line data Source code
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 : : * Copyright (C) 2018 Igalia S.L.
7 : : *
8 : : * SPDX-License-Identifier: LGPL-2.1-or-later
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Lesser General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2.1 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Lesser General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Lesser General
21 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 : : */
23 : :
24 : : #include "config.h"
25 : : #include <glib.h>
26 : : #include "glibintl.h"
27 : :
28 : : #include <stdlib.h>
29 : : #include "gnetworkaddress.h"
30 : : #include "gasyncresult.h"
31 : : #include "ginetaddress.h"
32 : : #include "ginetsocketaddress.h"
33 : : #include "gnetworkingprivate.h"
34 : : #include "gproxyaddressenumerator.h"
35 : : #include "gresolver.h"
36 : : #include "gtask.h"
37 : : #include "gsocketaddressenumerator.h"
38 : : #include "gioerror.h"
39 : : #include "gsocketconnectable.h"
40 : :
41 : : #include <string.h>
42 : :
43 : : /* As recommended by RFC 8305 this is the time it waits for a following
44 : : DNS response to come in (ipv4 waiting on ipv6 generally)
45 : : */
46 : : #define HAPPY_EYEBALLS_RESOLUTION_DELAY_MS 50
47 : :
48 : : /**
49 : : * GNetworkAddress:
50 : : *
51 : : * `GNetworkAddress` provides an easy way to resolve a hostname and
52 : : * then attempt to connect to that host, handling the possibility of
53 : : * multiple IP addresses and multiple address families.
54 : : *
55 : : * The enumeration results of resolved addresses *may* be cached as long
56 : : * as this object is kept alive which may have unexpected results if
57 : : * alive for too long.
58 : : *
59 : : * See [iface@Gio.SocketConnectable] for an example of using the connectable
60 : : * interface.
61 : : */
62 : :
63 : : struct _GNetworkAddressPrivate {
64 : : gchar *hostname;
65 : : guint16 port;
66 : : GList *cached_sockaddrs;
67 : : gchar *scheme;
68 : :
69 : : gint64 resolver_serial;
70 : : };
71 : :
72 : : enum {
73 : : PROP_0,
74 : : PROP_HOSTNAME,
75 : : PROP_PORT,
76 : : PROP_SCHEME,
77 : : };
78 : :
79 : : static void g_network_address_set_property (GObject *object,
80 : : guint prop_id,
81 : : const GValue *value,
82 : : GParamSpec *pspec);
83 : : static void g_network_address_get_property (GObject *object,
84 : : guint prop_id,
85 : : GValue *value,
86 : : GParamSpec *pspec);
87 : :
88 : : static void g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
89 : : static GSocketAddressEnumerator *g_network_address_connectable_enumerate (GSocketConnectable *connectable);
90 : : static GSocketAddressEnumerator *g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable);
91 : : static gchar *g_network_address_connectable_to_string (GSocketConnectable *connectable);
92 : :
93 : 4691 : G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
94 : : G_ADD_PRIVATE (GNetworkAddress)
95 : : G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
96 : : g_network_address_connectable_iface_init))
97 : :
98 : : static void
99 : 582 : g_network_address_finalize (GObject *object)
100 : : {
101 : 582 : GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
102 : :
103 : 582 : g_free (addr->priv->hostname);
104 : 582 : g_free (addr->priv->scheme);
105 : 582 : g_list_free_full (addr->priv->cached_sockaddrs, g_object_unref);
106 : :
107 : 582 : G_OBJECT_CLASS (g_network_address_parent_class)->finalize (object);
108 : 582 : }
109 : :
110 : : static void
111 : 12 : g_network_address_class_init (GNetworkAddressClass *klass)
112 : : {
113 : 12 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
114 : :
115 : 12 : gobject_class->set_property = g_network_address_set_property;
116 : 12 : gobject_class->get_property = g_network_address_get_property;
117 : 12 : gobject_class->finalize = g_network_address_finalize;
118 : :
119 : : /**
120 : : * GNetworkAddress:hostname:
121 : : *
122 : : * Hostname to resolve.
123 : : *
124 : : * Since: 2.22
125 : : */
126 : 12 : g_object_class_install_property (gobject_class, PROP_HOSTNAME,
127 : : g_param_spec_string ("hostname", NULL, NULL,
128 : : NULL,
129 : : G_PARAM_READWRITE |
130 : : G_PARAM_CONSTRUCT_ONLY |
131 : : G_PARAM_STATIC_STRINGS));
132 : :
133 : : /**
134 : : * GNetworkAddress:port:
135 : : *
136 : : * Network port.
137 : : *
138 : : * Since: 2.22
139 : : */
140 : 12 : g_object_class_install_property (gobject_class, PROP_PORT,
141 : : g_param_spec_uint ("port", NULL, NULL,
142 : : 0, 65535, 0,
143 : : G_PARAM_READWRITE |
144 : : G_PARAM_CONSTRUCT_ONLY |
145 : : G_PARAM_STATIC_STRINGS));
146 : :
147 : : /**
148 : : * GNetworkAddress:scheme:
149 : : *
150 : : * URI scheme.
151 : : *
152 : : * Since: 2.22
153 : : */
154 : 12 : g_object_class_install_property (gobject_class, PROP_SCHEME,
155 : : g_param_spec_string ("scheme", NULL, NULL,
156 : : NULL,
157 : : G_PARAM_READWRITE |
158 : : G_PARAM_CONSTRUCT_ONLY |
159 : : G_PARAM_STATIC_STRINGS));
160 : 12 : }
161 : :
162 : : static void
163 : 12 : g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
164 : : {
165 : 12 : connectable_iface->enumerate = g_network_address_connectable_enumerate;
166 : 12 : connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate;
167 : 12 : connectable_iface->to_string = g_network_address_connectable_to_string;
168 : 12 : }
169 : :
170 : : static void
171 : 584 : g_network_address_init (GNetworkAddress *addr)
172 : : {
173 : 584 : addr->priv = g_network_address_get_instance_private (addr);
174 : 584 : }
175 : :
176 : : static void
177 : 1752 : g_network_address_set_property (GObject *object,
178 : : guint prop_id,
179 : : const GValue *value,
180 : : GParamSpec *pspec)
181 : : {
182 : 1752 : GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
183 : :
184 : 1752 : switch (prop_id)
185 : : {
186 : 584 : case PROP_HOSTNAME:
187 : 584 : g_free (addr->priv->hostname);
188 : 584 : addr->priv->hostname = g_value_dup_string (value);
189 : 584 : break;
190 : :
191 : 584 : case PROP_PORT:
192 : 584 : addr->priv->port = g_value_get_uint (value);
193 : 584 : break;
194 : :
195 : 584 : case PROP_SCHEME:
196 : 584 : g_free (addr->priv->scheme);
197 : 584 : addr->priv->scheme = g_value_dup_string (value);
198 : 584 : break;
199 : :
200 : 0 : default:
201 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202 : 0 : break;
203 : : }
204 : :
205 : 1752 : }
206 : :
207 : : static void
208 : 922 : g_network_address_get_property (GObject *object,
209 : : guint prop_id,
210 : : GValue *value,
211 : : GParamSpec *pspec)
212 : : {
213 : 922 : GNetworkAddress *addr = G_NETWORK_ADDRESS (object);
214 : :
215 : 922 : switch (prop_id)
216 : : {
217 : 460 : case PROP_HOSTNAME:
218 : 460 : g_value_set_string (value, addr->priv->hostname);
219 : 460 : break;
220 : :
221 : 460 : case PROP_PORT:
222 : 460 : g_value_set_uint (value, addr->priv->port);
223 : 460 : break;
224 : :
225 : 2 : case PROP_SCHEME:
226 : 2 : g_value_set_string (value, addr->priv->scheme);
227 : 2 : break;
228 : :
229 : 0 : default:
230 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 : 0 : break;
232 : : }
233 : :
234 : 922 : }
235 : :
236 : : /*
237 : : * inet_addresses_to_inet_socket_addresses:
238 : : * @addresses: (transfer full): #GList of #GInetAddress
239 : : *
240 : : * Returns: (transfer full): #GList of #GInetSocketAddress
241 : : */
242 : : static GList *
243 : 35 : inet_addresses_to_inet_socket_addresses (GNetworkAddress *addr,
244 : : GList *addresses)
245 : : {
246 : 35 : GList *a, *socket_addresses = NULL;
247 : :
248 : 96 : for (a = addresses; a; a = a->next)
249 : : {
250 : 61 : GSocketAddress *sockaddr = g_inet_socket_address_new (a->data, addr->priv->port);
251 : 61 : socket_addresses = g_list_append (socket_addresses, g_steal_pointer (&sockaddr));
252 : 61 : g_object_unref (a->data);
253 : : }
254 : :
255 : 35 : g_list_free (addresses);
256 : 35 : return socket_addresses;
257 : : }
258 : :
259 : : /*
260 : : * g_network_address_set_cached_addresses:
261 : : * @addr: A #GNetworkAddress
262 : : * @addresses: (transfer full): List of #GInetAddress or #GInetSocketAddress
263 : : * @resolver_serial: Serial of #GResolver used
264 : : *
265 : : * Consumes @addresses and uses them to replace the current internal list.
266 : : */
267 : : static void
268 : 22 : g_network_address_set_cached_addresses (GNetworkAddress *addr,
269 : : GList *addresses,
270 : : guint64 resolver_serial)
271 : : {
272 : 22 : g_assert (addresses != NULL);
273 : :
274 : 22 : if (addr->priv->cached_sockaddrs)
275 : 1 : g_list_free_full (addr->priv->cached_sockaddrs, g_object_unref);
276 : :
277 : 22 : if (G_IS_INET_SOCKET_ADDRESS (addresses->data))
278 : 14 : addr->priv->cached_sockaddrs = g_steal_pointer (&addresses);
279 : : else
280 : 8 : addr->priv->cached_sockaddrs = inet_addresses_to_inet_socket_addresses (addr, g_steal_pointer (&addresses));
281 : 22 : addr->priv->resolver_serial = resolver_serial;
282 : 22 : }
283 : :
284 : : static gboolean
285 : 83 : g_network_address_parse_sockaddr (GNetworkAddress *addr)
286 : : {
287 : : GSocketAddress *sockaddr;
288 : :
289 : 83 : g_assert (addr->priv->cached_sockaddrs == NULL);
290 : :
291 : 83 : sockaddr = g_inet_socket_address_new_from_string (addr->priv->hostname,
292 : 83 : addr->priv->port);
293 : 83 : if (sockaddr)
294 : : {
295 : 49 : addr->priv->cached_sockaddrs = g_list_append (addr->priv->cached_sockaddrs, sockaddr);
296 : 49 : return TRUE;
297 : : }
298 : : else
299 : 34 : return FALSE;
300 : : }
301 : :
302 : : /**
303 : : * g_network_address_new:
304 : : * @hostname: the hostname
305 : : * @port: the port
306 : : *
307 : : * Creates a new #GSocketConnectable for connecting to the given
308 : : * @hostname and @port.
309 : : *
310 : : * Note that depending on the configuration of the machine, a
311 : : * @hostname of `localhost` may refer to the IPv4 loopback address
312 : : * only, or to both IPv4 and IPv6; use
313 : : * g_network_address_new_loopback() to create a #GNetworkAddress that
314 : : * is guaranteed to resolve to both addresses.
315 : : *
316 : : * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
317 : : *
318 : : * Since: 2.22
319 : : */
320 : : GSocketConnectable *
321 : 59 : g_network_address_new (const gchar *hostname,
322 : : guint16 port)
323 : : {
324 : 59 : return g_object_new (G_TYPE_NETWORK_ADDRESS,
325 : : "hostname", hostname,
326 : : "port", port,
327 : : NULL);
328 : : }
329 : :
330 : : /**
331 : : * g_network_address_new_loopback:
332 : : * @port: the port
333 : : *
334 : : * Creates a new #GSocketConnectable for connecting to the local host
335 : : * over a loopback connection to the given @port. This is intended for
336 : : * use in connecting to local services which may be running on IPv4 or
337 : : * IPv6.
338 : : *
339 : : * The connectable will return IPv4 and IPv6 loopback addresses,
340 : : * regardless of how the host resolves `localhost`. By contrast,
341 : : * g_network_address_new() will often only return an IPv4 address when
342 : : * resolving `localhost`, and an IPv6 address for `localhost6`.
343 : : *
344 : : * g_network_address_get_hostname() will always return `localhost` for
345 : : * a #GNetworkAddress created with this constructor.
346 : : *
347 : : * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
348 : : *
349 : : * Since: 2.44
350 : : */
351 : : GSocketConnectable *
352 : 3 : g_network_address_new_loopback (guint16 port)
353 : : {
354 : : GNetworkAddress *addr;
355 : 3 : GList *addrs = NULL;
356 : :
357 : 3 : addr = g_object_new (G_TYPE_NETWORK_ADDRESS,
358 : : "hostname", "localhost",
359 : : "port", port,
360 : : NULL);
361 : :
362 : 3 : addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET6));
363 : 3 : addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET));
364 : 3 : g_network_address_set_cached_addresses (addr, g_steal_pointer (&addrs), 0);
365 : :
366 : 3 : return G_SOCKET_CONNECTABLE (addr);
367 : : }
368 : :
369 : : /**
370 : : * g_network_address_parse:
371 : : * @host_and_port: the hostname and optionally a port
372 : : * @default_port: the default port if not in @host_and_port
373 : : * @error: a pointer to a #GError, or %NULL
374 : : *
375 : : * Creates a new #GSocketConnectable for connecting to the given
376 : : * @hostname and @port. May fail and return %NULL in case
377 : : * parsing @host_and_port fails.
378 : : *
379 : : * @host_and_port may be in any of a number of recognised formats; an IPv6
380 : : * address, an IPv4 address, or a domain name (in which case a DNS
381 : : * lookup is performed). Quoting with [] is supported for all address
382 : : * types. A port override may be specified in the usual way with a
383 : : * colon.
384 : : *
385 : : * If no port is specified in @host_and_port then @default_port will be
386 : : * used as the port number to connect to.
387 : : *
388 : : * In general, @host_and_port is expected to be provided by the user
389 : : * (allowing them to give the hostname, and a port override if necessary)
390 : : * and @default_port is expected to be provided by the application.
391 : : *
392 : : * (The port component of @host_and_port can also be specified as a
393 : : * service name rather than as a numeric port, but this functionality
394 : : * is deprecated, because it depends on the contents of /etc/services,
395 : : * which is generally quite sparse on platforms other than Linux.)
396 : : *
397 : : * Returns: (transfer full) (type GNetworkAddress): the new
398 : : * #GNetworkAddress, or %NULL on error
399 : : *
400 : : * Since: 2.22
401 : : */
402 : : GSocketConnectable *
403 : 31 : g_network_address_parse (const gchar *host_and_port,
404 : : guint16 default_port,
405 : : GError **error)
406 : : {
407 : : GSocketConnectable *connectable;
408 : : const gchar *port;
409 : : guint16 portnum;
410 : : gchar *name;
411 : :
412 : 31 : g_return_val_if_fail (host_and_port != NULL, NULL);
413 : :
414 : 31 : port = NULL;
415 : 31 : if (host_and_port[0] == '[')
416 : : /* escaped host part (to allow, eg. "[2001:db8::1]:888") */
417 : : {
418 : : const gchar *end;
419 : :
420 : 8 : end = strchr (host_and_port, ']');
421 : 8 : if (end == NULL)
422 : : {
423 : 1 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
424 : : _("Hostname “%s” contains “[” but not “]”"), host_and_port);
425 : 1 : return NULL;
426 : : }
427 : :
428 : 7 : if (end[1] == '\0')
429 : 4 : port = NULL;
430 : 3 : else if (end[1] == ':')
431 : 2 : port = &end[2];
432 : : else
433 : : {
434 : 1 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
435 : : "The ']' character (in hostname '%s') must come at the"
436 : : " end or be immediately followed by ':' and a port",
437 : : host_and_port);
438 : 1 : return NULL;
439 : : }
440 : :
441 : 6 : name = g_strndup (host_and_port + 1, end - host_and_port - 1);
442 : : }
443 : :
444 : 23 : else if ((port = strchr (host_and_port, ':')))
445 : : /* string has a ':' in it */
446 : : {
447 : : /* skip ':' */
448 : 10 : port++;
449 : :
450 : 10 : if (strchr (port, ':'))
451 : : /* more than one ':' in string */
452 : : {
453 : : /* this is actually an unescaped IPv6 address */
454 : 2 : name = g_strdup (host_and_port);
455 : 2 : port = NULL;
456 : : }
457 : : else
458 : 8 : name = g_strndup (host_and_port, port - host_and_port - 1);
459 : : }
460 : :
461 : : else
462 : : /* plain hostname, no port */
463 : 13 : name = g_strdup (host_and_port);
464 : :
465 : 29 : if (port != NULL)
466 : : {
467 : 10 : if (port[0] == '\0')
468 : : {
469 : 1 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
470 : : "If a ':' character is given, it must be followed by a "
471 : : "port (in hostname '%s').", host_and_port);
472 : 1 : g_free (name);
473 : 1 : return NULL;
474 : : }
475 : :
476 : 9 : else if ('0' <= port[0] && port[0] <= '9')
477 : 4 : {
478 : : char *end;
479 : : long value;
480 : :
481 : 5 : value = strtol (port, &end, 10);
482 : 5 : if (*end != '\0' || value < 0 || value > G_MAXUINT16)
483 : : {
484 : 1 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
485 : : "Invalid numeric port '%s' specified in hostname '%s'",
486 : : port, host_and_port);
487 : 1 : g_free (name);
488 : 1 : return NULL;
489 : : }
490 : :
491 : 4 : portnum = value;
492 : : }
493 : :
494 : : else
495 : : {
496 : 4 : if (!g_getservbyname_ntohs (port, "tcp", &portnum))
497 : : {
498 : 2 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
499 : : "Unknown service '%s' specified in hostname '%s'",
500 : : port, host_and_port);
501 : : #ifdef HAVE_ENDSERVENT
502 : 2 : endservent ();
503 : : #endif
504 : 2 : g_free (name);
505 : 2 : return NULL;
506 : : }
507 : :
508 : : #ifdef HAVE_ENDSERVENT
509 : 2 : endservent ();
510 : : #endif
511 : : }
512 : : }
513 : : else
514 : : {
515 : : /* No port in host_and_port */
516 : 19 : portnum = default_port;
517 : : }
518 : :
519 : 25 : connectable = g_network_address_new (name, portnum);
520 : 25 : g_free (name);
521 : :
522 : 25 : return connectable;
523 : : }
524 : :
525 : : /**
526 : : * g_network_address_parse_uri:
527 : : * @uri: the hostname and optionally a port
528 : : * @default_port: The default port if none is found in the URI
529 : : * @error: a pointer to a #GError, or %NULL
530 : : *
531 : : * Creates a new #GSocketConnectable for connecting to the given
532 : : * @uri. May fail and return %NULL in case parsing @uri fails.
533 : : *
534 : : * Using this rather than g_network_address_new() or
535 : : * g_network_address_parse() allows #GSocketClient to determine
536 : : * when to use application-specific proxy protocols.
537 : : *
538 : : * Returns: (transfer full) (type GNetworkAddress): the new
539 : : * #GNetworkAddress, or %NULL on error
540 : : *
541 : : * Since: 2.26
542 : : */
543 : : GSocketConnectable *
544 : 528 : g_network_address_parse_uri (const gchar *uri,
545 : : guint16 default_port,
546 : : GError **error)
547 : : {
548 : 528 : GSocketConnectable *conn = NULL;
549 : 528 : gchar *scheme = NULL;
550 : 528 : gchar *hostname = NULL;
551 : : gint port;
552 : :
553 : 528 : if (!g_uri_split_network (uri, G_URI_FLAGS_NONE,
554 : : &scheme, &hostname, &port, NULL))
555 : : {
556 : 7 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
557 : : "Invalid URI ‘%s’", uri);
558 : 7 : return NULL;
559 : : }
560 : :
561 : 521 : if (port <= 0)
562 : 424 : port = default_port;
563 : :
564 : 521 : conn = g_object_new (G_TYPE_NETWORK_ADDRESS,
565 : : "hostname", hostname,
566 : : "port", (guint) port,
567 : : "scheme", scheme,
568 : : NULL);
569 : 521 : g_free (scheme);
570 : 521 : g_free (hostname);
571 : :
572 : 521 : return conn;
573 : : }
574 : :
575 : : /**
576 : : * g_network_address_get_hostname:
577 : : * @addr: a #GNetworkAddress
578 : : *
579 : : * Gets @addr's hostname. This might be either UTF-8 or ASCII-encoded,
580 : : * depending on what @addr was created with.
581 : : *
582 : : * Returns: @addr's hostname
583 : : *
584 : : * Since: 2.22
585 : : */
586 : : const gchar *
587 : 24 : g_network_address_get_hostname (GNetworkAddress *addr)
588 : : {
589 : 24 : g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
590 : :
591 : 24 : return addr->priv->hostname;
592 : : }
593 : :
594 : : /**
595 : : * g_network_address_get_port:
596 : : * @addr: a #GNetworkAddress
597 : : *
598 : : * Gets @addr's port number
599 : : *
600 : : * Returns: @addr's port (which may be 0)
601 : : *
602 : : * Since: 2.22
603 : : */
604 : : guint16
605 : 22 : g_network_address_get_port (GNetworkAddress *addr)
606 : : {
607 : 22 : g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), 0);
608 : :
609 : 22 : return addr->priv->port;
610 : : }
611 : :
612 : : /**
613 : : * g_network_address_get_scheme:
614 : : * @addr: a #GNetworkAddress
615 : : *
616 : : * Gets @addr's scheme
617 : : *
618 : : * Returns: (nullable): @addr's scheme (%NULL if not built from URI)
619 : : *
620 : : * Since: 2.26
621 : : */
622 : : const gchar *
623 : 21 : g_network_address_get_scheme (GNetworkAddress *addr)
624 : : {
625 : 21 : g_return_val_if_fail (G_IS_NETWORK_ADDRESS (addr), NULL);
626 : :
627 : 21 : return addr->priv->scheme;
628 : : }
629 : :
630 : : #define G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (_g_network_address_address_enumerator_get_type ())
631 : : #define G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, GNetworkAddressAddressEnumerator))
632 : :
633 : : typedef enum {
634 : : RESOLVE_STATE_NONE = 0,
635 : : RESOLVE_STATE_WAITING_ON_IPV4 = 1 << 0,
636 : : RESOLVE_STATE_WAITING_ON_IPV6 = 1 << 1,
637 : : } ResolveState;
638 : :
639 : : typedef struct {
640 : : GSocketAddressEnumerator parent_instance;
641 : :
642 : : GNetworkAddress *addr; /* (owned) */
643 : : GList *addresses; /* (owned) (nullable) */
644 : : GList *current_item; /* (unowned) (nullable) */
645 : : GTask *queued_task; /* (owned) (nullable) */
646 : : GTask *waiting_task; /* (owned) (nullable) */
647 : : GError *last_error; /* (owned) (nullable) */
648 : : GSource *wait_source; /* (owned) (nullable) */
649 : : GMainContext *context; /* (owned) (nullable) */
650 : : ResolveState state;
651 : : } GNetworkAddressAddressEnumerator;
652 : :
653 : : typedef struct {
654 : : GSocketAddressEnumeratorClass parent_class;
655 : :
656 : : } GNetworkAddressAddressEnumeratorClass;
657 : :
658 : : static GType _g_network_address_address_enumerator_get_type (void);
659 : 347 : G_DEFINE_TYPE (GNetworkAddressAddressEnumerator, _g_network_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
660 : :
661 : : static void
662 : 84 : g_network_address_address_enumerator_finalize (GObject *object)
663 : : {
664 : : GNetworkAddressAddressEnumerator *addr_enum =
665 : 84 : G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
666 : :
667 : 84 : if (addr_enum->wait_source)
668 : : {
669 : 0 : g_source_destroy (addr_enum->wait_source);
670 : 0 : g_clear_pointer (&addr_enum->wait_source, g_source_unref);
671 : : }
672 : 84 : g_clear_object (&addr_enum->queued_task);
673 : 84 : g_clear_object (&addr_enum->waiting_task);
674 : 84 : g_clear_error (&addr_enum->last_error);
675 : 84 : g_object_unref (addr_enum->addr);
676 : 84 : g_clear_pointer (&addr_enum->context, g_main_context_unref);
677 : 84 : g_list_free_full (addr_enum->addresses, g_object_unref);
678 : :
679 : 84 : G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
680 : 84 : }
681 : :
682 : : static inline GSocketFamily
683 : 73 : get_address_family (GInetSocketAddress *address)
684 : : {
685 : 73 : return g_inet_address_get_family (g_inet_socket_address_get_address (address));
686 : : }
687 : :
688 : : static void
689 : 36 : list_split_families (GList *list,
690 : : GList **out_ipv4,
691 : : GList **out_ipv6)
692 : : {
693 : 36 : g_assert (out_ipv4);
694 : 36 : g_assert (out_ipv6);
695 : :
696 : 99 : while (list)
697 : : {
698 : 63 : GSocketFamily family = get_address_family (list->data);
699 : 63 : switch (family)
700 : : {
701 : 36 : case G_SOCKET_FAMILY_IPV4:
702 : 36 : *out_ipv4 = g_list_prepend (*out_ipv4, list->data);
703 : 36 : break;
704 : 27 : case G_SOCKET_FAMILY_IPV6:
705 : 27 : *out_ipv6 = g_list_prepend (*out_ipv6, list->data);
706 : 27 : break;
707 : 0 : case G_SOCKET_FAMILY_INVALID:
708 : : case G_SOCKET_FAMILY_UNIX:
709 : : g_assert_not_reached ();
710 : : }
711 : :
712 : 63 : list = g_list_next (list);
713 : : }
714 : :
715 : 36 : *out_ipv4 = g_list_reverse (*out_ipv4);
716 : 36 : *out_ipv6 = g_list_reverse (*out_ipv6);
717 : 36 : }
718 : :
719 : : static GList *
720 : 25 : list_interleave_families (GList *list1,
721 : : GList *list2)
722 : : {
723 : 25 : GList *interleaved = NULL;
724 : :
725 : 67 : while (list1 || list2)
726 : : {
727 : 42 : if (list1)
728 : : {
729 : 38 : interleaved = g_list_append (interleaved, list1->data);
730 : 38 : list1 = g_list_delete_link (list1, list1);
731 : : }
732 : 42 : if (list2)
733 : : {
734 : 25 : interleaved = g_list_append (interleaved, list2->data);
735 : 25 : list2 = g_list_delete_link (list2, list2);
736 : : }
737 : : }
738 : :
739 : 25 : return interleaved;
740 : : }
741 : :
742 : : /* list_copy_interleaved:
743 : : * @list: (transfer container): List to copy
744 : : *
745 : : * Does a shallow copy of a list with address families interleaved.
746 : : *
747 : : * For example:
748 : : * Input: [ipv6, ipv6, ipv4, ipv4]
749 : : * Output: [ipv6, ipv4, ipv6, ipv4]
750 : : *
751 : : * Returns: (transfer container): A new list
752 : : */
753 : : static GList *
754 : 14 : list_copy_interleaved (GList *list)
755 : : {
756 : 14 : GList *ipv4 = NULL, *ipv6 = NULL;
757 : :
758 : 14 : list_split_families (list, &ipv4, &ipv6);
759 : 14 : return list_interleave_families (ipv6, ipv4);
760 : : }
761 : :
762 : : /* list_concat_interleaved:
763 : : * @parent_list: (transfer container): Already existing list
764 : : * @current_item: (transfer container): Item after which to resort
765 : : * @new_list: (transfer container): New list to be interleaved and concatenated
766 : : *
767 : : * This differs from g_list_concat() + list_copy_interleaved() in that it sorts
768 : : * items in the previous list starting from @current_item and concats the results
769 : : * to @parent_list.
770 : : *
771 : : * Returns: (transfer container): New start of list
772 : : */
773 : : static GList *
774 : 11 : list_concat_interleaved (GList *parent_list,
775 : : GList *current_item,
776 : : GList *new_list)
777 : : {
778 : 11 : GList *ipv4 = NULL, *ipv6 = NULL, *interleaved, *trailing = NULL;
779 : 11 : GSocketFamily last_family = G_SOCKET_FAMILY_IPV4; /* Default to starting with ipv6 */
780 : :
781 : 11 : if (current_item)
782 : : {
783 : 10 : last_family = get_address_family (current_item->data);
784 : :
785 : : /* Unused addresses will get removed, resorted, then readded */
786 : 10 : trailing = g_list_next (current_item);
787 : 10 : current_item->next = NULL;
788 : : }
789 : :
790 : 11 : list_split_families (trailing, &ipv4, &ipv6);
791 : 11 : list_split_families (new_list, &ipv4, &ipv6);
792 : 11 : g_list_free (new_list);
793 : :
794 : 11 : if (trailing)
795 : 3 : g_list_free (trailing);
796 : :
797 : 11 : if (last_family == G_SOCKET_FAMILY_IPV4)
798 : 2 : interleaved = list_interleave_families (ipv6, ipv4);
799 : : else
800 : 9 : interleaved = list_interleave_families (ipv4, ipv6);
801 : :
802 : 11 : return g_list_concat (parent_list, interleaved);
803 : : }
804 : :
805 : : static void
806 : 27 : maybe_update_address_cache (GNetworkAddressAddressEnumerator *addr_enum,
807 : : GResolver *resolver)
808 : : {
809 : : GList *addresses, *p;
810 : :
811 : : /* Only cache complete results */
812 : 27 : if (addr_enum->state & RESOLVE_STATE_WAITING_ON_IPV4 || addr_enum->state & RESOLVE_STATE_WAITING_ON_IPV6)
813 : 13 : return;
814 : :
815 : : /* The enumerators list will not necessarily be fully sorted */
816 : 14 : addresses = list_copy_interleaved (addr_enum->addresses);
817 : 56 : for (p = addresses; p; p = p->next)
818 : 42 : g_object_ref (p->data);
819 : :
820 : 14 : g_network_address_set_cached_addresses (addr_enum->addr, g_steal_pointer (&addresses), g_resolver_get_serial (resolver));
821 : : }
822 : :
823 : : static void
824 : 27 : g_network_address_address_enumerator_add_addresses (GNetworkAddressAddressEnumerator *addr_enum,
825 : : GList *addresses,
826 : : GResolver *resolver)
827 : : {
828 : 27 : GList *new_addresses = inet_addresses_to_inet_socket_addresses (addr_enum->addr, addresses);
829 : :
830 : 27 : if (addr_enum->addresses == NULL)
831 : 16 : addr_enum->addresses = g_steal_pointer (&new_addresses);
832 : : else
833 : 11 : addr_enum->addresses = list_concat_interleaved (addr_enum->addresses, addr_enum->current_item, g_steal_pointer (&new_addresses));
834 : :
835 : 27 : maybe_update_address_cache (addr_enum, resolver);
836 : 27 : }
837 : :
838 : : static gpointer
839 : 66 : copy_object (gconstpointer src,
840 : : gpointer user_data)
841 : : {
842 : 66 : return g_object_ref (G_OBJECT (src));
843 : : }
844 : :
845 : : static GSocketAddress *
846 : 157 : init_and_query_next_address (GNetworkAddressAddressEnumerator *addr_enum)
847 : : {
848 : : GList *next_item;
849 : :
850 : 157 : if (addr_enum->addresses == NULL)
851 : 57 : addr_enum->addresses = g_list_copy_deep (addr_enum->addr->priv->cached_sockaddrs,
852 : : copy_object, NULL);
853 : :
854 : : /* We always want to look at the next item at call time to get the latest results.
855 : : That means that sometimes ->next is NULL this call but is valid next call.
856 : : */
857 : 157 : if (addr_enum->current_item == NULL)
858 : 73 : next_item = addr_enum->current_item = addr_enum->addresses;
859 : : else
860 : 84 : next_item = g_list_next (addr_enum->current_item);
861 : :
862 : 157 : if (next_item)
863 : : {
864 : 112 : addr_enum->current_item = next_item;
865 : 112 : return g_object_ref (addr_enum->current_item->data);
866 : : }
867 : : else
868 : 45 : return NULL;
869 : : }
870 : :
871 : : static GSocketAddress *
872 : 77 : g_network_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
873 : : GCancellable *cancellable,
874 : : GError **error)
875 : : {
876 : : GNetworkAddressAddressEnumerator *addr_enum =
877 : 77 : G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
878 : :
879 : 77 : if (addr_enum->addresses == NULL)
880 : : {
881 : 50 : GNetworkAddress *addr = addr_enum->addr;
882 : 50 : GResolver *resolver = g_resolver_get_default ();
883 : 50 : gint64 serial = g_resolver_get_serial (resolver);
884 : :
885 : 50 : if (addr->priv->resolver_serial != 0 &&
886 : 0 : addr->priv->resolver_serial != serial)
887 : : {
888 : : /* Resolver has reloaded, discard cached addresses */
889 : 0 : g_list_free_full (addr->priv->cached_sockaddrs, g_object_unref);
890 : 0 : addr->priv->cached_sockaddrs = NULL;
891 : : }
892 : :
893 : 50 : if (!addr->priv->cached_sockaddrs)
894 : 49 : g_network_address_parse_sockaddr (addr);
895 : 50 : if (!addr->priv->cached_sockaddrs)
896 : : {
897 : : GList *addresses;
898 : :
899 : 26 : addresses = g_resolver_lookup_by_name (resolver,
900 : 13 : addr->priv->hostname,
901 : : cancellable, error);
902 : 13 : if (!addresses)
903 : : {
904 : 8 : g_object_unref (resolver);
905 : 8 : return NULL;
906 : : }
907 : :
908 : 5 : g_network_address_set_cached_addresses (addr, g_steal_pointer (&addresses), serial);
909 : : }
910 : :
911 : 42 : g_object_unref (resolver);
912 : : }
913 : :
914 : 69 : return init_and_query_next_address (addr_enum);
915 : : }
916 : :
917 : : static void
918 : 37 : complete_queued_task (GNetworkAddressAddressEnumerator *addr_enum,
919 : : GTask *task,
920 : : GError *error)
921 : : {
922 : 37 : if (error)
923 : 5 : g_task_return_error (task, error);
924 : : else
925 : : {
926 : 32 : GSocketAddress *sockaddr = init_and_query_next_address (addr_enum);
927 : 32 : g_task_return_pointer (task, g_steal_pointer (&sockaddr), g_object_unref);
928 : : }
929 : 37 : g_object_unref (task);
930 : 37 : }
931 : :
932 : : static int
933 : 2 : on_address_timeout (gpointer user_data)
934 : : {
935 : 2 : GNetworkAddressAddressEnumerator *addr_enum = user_data;
936 : :
937 : : /* Upon completion it may get unref'd by the owner */
938 : 2 : g_object_ref (addr_enum);
939 : :
940 : 2 : if (addr_enum->queued_task != NULL)
941 : 2 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->queued_task),
942 : 2 : g_steal_pointer (&addr_enum->last_error));
943 : 0 : else if (addr_enum->waiting_task != NULL)
944 : 0 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->waiting_task),
945 : : NULL);
946 : :
947 : 2 : g_clear_pointer (&addr_enum->wait_source, g_source_unref);
948 : 2 : g_object_unref (addr_enum);
949 : :
950 : 2 : return G_SOURCE_REMOVE;
951 : : }
952 : :
953 : : static void
954 : 20 : got_ipv6_addresses (GObject *source_object,
955 : : GAsyncResult *result,
956 : : gpointer user_data)
957 : : {
958 : 20 : GNetworkAddressAddressEnumerator *addr_enum = user_data;
959 : 20 : GResolver *resolver = G_RESOLVER (source_object);
960 : : GList *addresses;
961 : 20 : GError *error = NULL;
962 : :
963 : 20 : addr_enum->state ^= RESOLVE_STATE_WAITING_ON_IPV6;
964 : :
965 : 20 : addresses = g_resolver_lookup_by_name_with_flags_finish (resolver, result, &error);
966 : 20 : if (!error)
967 : 13 : g_network_address_address_enumerator_add_addresses (addr_enum, g_steal_pointer (&addresses), resolver);
968 : : else
969 : 7 : g_debug ("IPv6 DNS error: %s", error->message);
970 : :
971 : : /* If ipv4 was first and waiting on us it can stop waiting */
972 : 20 : if (addr_enum->wait_source)
973 : : {
974 : 4 : g_source_destroy (addr_enum->wait_source);
975 : 4 : g_clear_pointer (&addr_enum->wait_source, g_source_unref);
976 : : }
977 : :
978 : : /* If we got an error before ipv4 then let its response handle it.
979 : : * If we get ipv6 response first or error second then
980 : : * immediately complete the task.
981 : : */
982 : 20 : if (error != NULL && !addr_enum->last_error && (addr_enum->state & RESOLVE_STATE_WAITING_ON_IPV4))
983 : : {
984 : : /* ipv6 lookup failed, but ipv4 is still outstanding. wait. */
985 : 5 : addr_enum->last_error = g_steal_pointer (&error);
986 : : }
987 : 15 : else if (addr_enum->waiting_task != NULL)
988 : : {
989 : 1 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->waiting_task), NULL);
990 : : }
991 : 14 : else if (addr_enum->queued_task != NULL)
992 : : {
993 : 14 : GError *task_error = NULL;
994 : :
995 : : /* If both errored just use the ipv6 one,
996 : : but if ipv6 errored and ipv4 didn't we don't error */
997 : 14 : if (error != NULL && addr_enum->last_error)
998 : 1 : task_error = g_steal_pointer (&error);
999 : :
1000 : 14 : g_clear_error (&addr_enum->last_error);
1001 : 14 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->queued_task),
1002 : 14 : g_steal_pointer (&task_error));
1003 : : }
1004 : :
1005 : 20 : g_clear_error (&error);
1006 : 20 : g_object_unref (addr_enum);
1007 : 20 : }
1008 : :
1009 : : static void
1010 : 21 : got_ipv4_addresses (GObject *source_object,
1011 : : GAsyncResult *result,
1012 : : gpointer user_data)
1013 : : {
1014 : 21 : GNetworkAddressAddressEnumerator *addr_enum = user_data;
1015 : 21 : GResolver *resolver = G_RESOLVER (source_object);
1016 : : GList *addresses;
1017 : 21 : GError *error = NULL;
1018 : :
1019 : 21 : addr_enum->state ^= RESOLVE_STATE_WAITING_ON_IPV4;
1020 : :
1021 : 21 : addresses = g_resolver_lookup_by_name_with_flags_finish (resolver, result, &error);
1022 : 21 : if (!error)
1023 : 14 : g_network_address_address_enumerator_add_addresses (addr_enum, g_steal_pointer (&addresses), resolver);
1024 : : else
1025 : 7 : g_debug ("IPv4 DNS error: %s", error->message);
1026 : :
1027 : 21 : if (addr_enum->wait_source)
1028 : : {
1029 : 0 : g_source_destroy (addr_enum->wait_source);
1030 : 0 : g_clear_pointer (&addr_enum->wait_source, g_source_unref);
1031 : : }
1032 : :
1033 : : /* If ipv6 already came in and errored then we return.
1034 : : * If ipv6 returned successfully then we don't need to do anything unless
1035 : : * another enumeration was waiting on us.
1036 : : * If ipv6 hasn't come we should wait a short while for it as RFC 8305 suggests.
1037 : : */
1038 : 21 : if (addr_enum->last_error)
1039 : : {
1040 : 5 : g_assert (addr_enum->queued_task);
1041 : 5 : g_clear_error (&addr_enum->last_error);
1042 : 5 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->queued_task),
1043 : 5 : g_steal_pointer (&error));
1044 : : }
1045 : 16 : else if (addr_enum->waiting_task != NULL)
1046 : : {
1047 : 2 : complete_queued_task (addr_enum, g_steal_pointer (&addr_enum->waiting_task), NULL);
1048 : : }
1049 : 14 : else if (addr_enum->queued_task != NULL)
1050 : : {
1051 : 6 : addr_enum->last_error = g_steal_pointer (&error);
1052 : 6 : addr_enum->wait_source = g_timeout_source_new (HAPPY_EYEBALLS_RESOLUTION_DELAY_MS);
1053 : 6 : g_source_set_callback (addr_enum->wait_source,
1054 : : on_address_timeout,
1055 : : addr_enum, NULL);
1056 : 6 : g_source_attach (addr_enum->wait_source, addr_enum->context);
1057 : : }
1058 : :
1059 : 21 : g_clear_error (&error);
1060 : 21 : g_object_unref (addr_enum);
1061 : 21 : }
1062 : :
1063 : : static void
1064 : 90 : g_network_address_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
1065 : : GCancellable *cancellable,
1066 : : GAsyncReadyCallback callback,
1067 : : gpointer user_data)
1068 : : {
1069 : : GNetworkAddressAddressEnumerator *addr_enum =
1070 : 90 : G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
1071 : : GSocketAddress *sockaddr;
1072 : : GTask *task;
1073 : :
1074 : 90 : task = g_task_new (addr_enum, cancellable, callback, user_data);
1075 : 90 : g_task_set_source_tag (task, g_network_address_address_enumerator_next_async);
1076 : :
1077 : 90 : if (addr_enum->addresses == NULL && addr_enum->state == RESOLVE_STATE_NONE)
1078 : : {
1079 : 36 : GNetworkAddress *addr = addr_enum->addr;
1080 : 36 : GResolver *resolver = g_resolver_get_default ();
1081 : 36 : gint64 serial = g_resolver_get_serial (resolver);
1082 : :
1083 : 36 : if (addr->priv->resolver_serial != 0 &&
1084 : 1 : addr->priv->resolver_serial != serial)
1085 : : {
1086 : : /* Resolver has reloaded, discard cached addresses */
1087 : 0 : g_list_free_full (addr->priv->cached_sockaddrs, g_object_unref);
1088 : 0 : addr->priv->cached_sockaddrs = NULL;
1089 : : }
1090 : :
1091 : 36 : if (addr->priv->cached_sockaddrs == NULL)
1092 : : {
1093 : 34 : if (g_network_address_parse_sockaddr (addr))
1094 : 13 : complete_queued_task (addr_enum, task, NULL);
1095 : : else
1096 : : {
1097 : : /* It does not make sense for this to be called multiple
1098 : : * times before the initial callback has been called */
1099 : 21 : g_assert (addr_enum->queued_task == NULL);
1100 : :
1101 : 21 : addr_enum->state = RESOLVE_STATE_WAITING_ON_IPV4 | RESOLVE_STATE_WAITING_ON_IPV6;
1102 : 21 : addr_enum->queued_task = g_steal_pointer (&task);
1103 : : /* Look up in parallel as per RFC 8305 */
1104 : 21 : g_resolver_lookup_by_name_with_flags_async (resolver,
1105 : 21 : addr->priv->hostname,
1106 : : G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY,
1107 : : cancellable,
1108 : : got_ipv6_addresses, g_object_ref (addr_enum));
1109 : 21 : g_resolver_lookup_by_name_with_flags_async (resolver,
1110 : 21 : addr->priv->hostname,
1111 : : G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY,
1112 : : cancellable,
1113 : : got_ipv4_addresses, g_object_ref (addr_enum));
1114 : : }
1115 : 34 : g_object_unref (resolver);
1116 : 34 : return;
1117 : : }
1118 : :
1119 : 2 : g_object_unref (resolver);
1120 : : }
1121 : :
1122 : 56 : sockaddr = init_and_query_next_address (addr_enum);
1123 : 56 : if (sockaddr == NULL && (addr_enum->state & RESOLVE_STATE_WAITING_ON_IPV4 ||
1124 : 20 : addr_enum->state & RESOLVE_STATE_WAITING_ON_IPV6))
1125 : : {
1126 : 3 : addr_enum->waiting_task = task;
1127 : : }
1128 : : else
1129 : : {
1130 : 53 : g_task_return_pointer (task, sockaddr, g_object_unref);
1131 : 53 : g_object_unref (task);
1132 : : }
1133 : : }
1134 : :
1135 : : static GSocketAddress *
1136 : 90 : g_network_address_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
1137 : : GAsyncResult *result,
1138 : : GError **error)
1139 : : {
1140 : 90 : g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL);
1141 : :
1142 : 90 : return g_task_propagate_pointer (G_TASK (result), error);
1143 : : }
1144 : :
1145 : : static void
1146 : 86 : _g_network_address_address_enumerator_init (GNetworkAddressAddressEnumerator *enumerator)
1147 : : {
1148 : 86 : enumerator->context = g_main_context_ref_thread_default ();
1149 : 86 : }
1150 : :
1151 : : static void
1152 : 5 : _g_network_address_address_enumerator_class_init (GNetworkAddressAddressEnumeratorClass *addrenum_class)
1153 : : {
1154 : 5 : GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
1155 : : GSocketAddressEnumeratorClass *enumerator_class =
1156 : 5 : G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
1157 : :
1158 : 5 : enumerator_class->next = g_network_address_address_enumerator_next;
1159 : 5 : enumerator_class->next_async = g_network_address_address_enumerator_next_async;
1160 : 5 : enumerator_class->next_finish = g_network_address_address_enumerator_next_finish;
1161 : 5 : object_class->finalize = g_network_address_address_enumerator_finalize;
1162 : 5 : }
1163 : :
1164 : : static GSocketAddressEnumerator *
1165 : 86 : g_network_address_connectable_enumerate (GSocketConnectable *connectable)
1166 : : {
1167 : : GNetworkAddressAddressEnumerator *addr_enum;
1168 : :
1169 : 86 : addr_enum = g_object_new (G_TYPE_NETWORK_ADDRESS_ADDRESS_ENUMERATOR, NULL);
1170 : 86 : addr_enum->addr = g_object_ref (G_NETWORK_ADDRESS (connectable));
1171 : :
1172 : 86 : return (GSocketAddressEnumerator *)addr_enum;
1173 : : }
1174 : :
1175 : : static GSocketAddressEnumerator *
1176 : 30 : g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
1177 : : {
1178 : 30 : GNetworkAddress *self = G_NETWORK_ADDRESS (connectable);
1179 : : GSocketAddressEnumerator *proxy_enum;
1180 : : gchar *uri;
1181 : :
1182 : 30 : uri = g_uri_join (G_URI_FLAGS_NONE,
1183 : 26 : self->priv->scheme ? self->priv->scheme : "none",
1184 : : NULL,
1185 : 30 : self->priv->hostname,
1186 : 30 : self->priv->port,
1187 : : "",
1188 : : NULL,
1189 : : NULL);
1190 : :
1191 : 30 : proxy_enum = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1192 : : "connectable", connectable,
1193 : : "uri", uri,
1194 : : NULL);
1195 : :
1196 : 30 : g_free (uri);
1197 : :
1198 : 30 : return proxy_enum;
1199 : : }
1200 : :
1201 : : static gchar *
1202 : 5 : g_network_address_connectable_to_string (GSocketConnectable *connectable)
1203 : : {
1204 : : GNetworkAddress *addr;
1205 : : const gchar *scheme;
1206 : : guint16 port;
1207 : : GString *out; /* owned */
1208 : :
1209 : 5 : addr = G_NETWORK_ADDRESS (connectable);
1210 : 5 : out = g_string_new ("");
1211 : :
1212 : 5 : scheme = g_network_address_get_scheme (addr);
1213 : 5 : if (scheme != NULL)
1214 : 1 : g_string_append_printf (out, "%s:", scheme);
1215 : :
1216 : 5 : g_string_append (out, g_network_address_get_hostname (addr));
1217 : :
1218 : 5 : port = g_network_address_get_port (addr);
1219 : 5 : if (port != 0)
1220 : 4 : g_string_append_printf (out, ":%u", port);
1221 : :
1222 : 5 : return g_string_free (out, FALSE);
1223 : : }
|