mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
gnetworkaddress: Add g_network_address_new_loopback() constructor
This is a convenience method for creating a GNetworkAddress which is guaranteed to return IPv4 and IPv6 loopback addresses. The program cannot guarantee that 'localhost' will resolve to both types of address, so programs which wish to connect to a local service over either IPv4 or IPv6 must currently manually create an IPv4 and another IPv6 socket, and detect which of the two are working. This new API allows the existing GSocketConnectable machinery to be used to automate that. Based on a patch from Philip Withnall. https://bugzilla.gnome.org/show_bug.cgi?id=732317
This commit is contained in:
parent
296c710c64
commit
64f9bf96fd
@ -1867,11 +1867,12 @@ g_socket_connectable_get_type
|
||||
<TITLE>GNetworkAddress</TITLE>
|
||||
GNetworkAddress
|
||||
g_network_address_new
|
||||
g_network_address_new_loopback
|
||||
g_network_address_parse
|
||||
g_network_address_parse_uri
|
||||
g_network_address_get_hostname
|
||||
g_network_address_get_port
|
||||
g_network_address_get_scheme
|
||||
g_network_address_parse
|
||||
g_network_address_parse_uri
|
||||
<SUBSECTION Standard>
|
||||
GNetworkAddressClass
|
||||
GNetworkAddressPrivate
|
||||
|
@ -267,6 +267,12 @@ g_network_address_parse_sockaddr (GNetworkAddress *addr)
|
||||
* Creates a new #GSocketConnectable for connecting to the given
|
||||
* @hostname and @port.
|
||||
*
|
||||
* Note that depending on the configuration of the machine, a
|
||||
* @hostname of `localhost` may refer to the IPv4 loopback address
|
||||
* only, or to both IPv4 and IPv6; use
|
||||
* g_network_address_new_loopback() to create a #GNetworkAddress that
|
||||
* is guaranteed to resolve to both addresses.
|
||||
*
|
||||
* Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
|
||||
*
|
||||
* Since: 2.22
|
||||
@ -281,6 +287,45 @@ g_network_address_new (const gchar *hostname,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_network_address_new_loopback:
|
||||
* @port: the port
|
||||
*
|
||||
* Creates a new #GSocketConnectable for connecting to the local host
|
||||
* over a loopback connection to the given @port. This is intended for
|
||||
* use in connecting to local services which may be running on IPv4 or
|
||||
* IPv6.
|
||||
*
|
||||
* The connectable will return IPv4 and IPv6 loopback addresses,
|
||||
* regardless of how the host resolves `localhost`. By contrast,
|
||||
* g_network_address_new() will often only return an IPv4 address when
|
||||
* resolving `localhost`, and an IPv6 address for `localhost6`.
|
||||
*
|
||||
* g_network_address_get_hostname() will always return `localhost` for
|
||||
* #GNetworkAddresses created with this constructor.
|
||||
*
|
||||
* Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
|
||||
*
|
||||
* Since: 2.44
|
||||
*/
|
||||
GSocketConnectable *
|
||||
g_network_address_new_loopback (guint16 port)
|
||||
{
|
||||
GNetworkAddress *addr;
|
||||
GList *addrs = NULL;
|
||||
|
||||
addr = g_object_new (G_TYPE_NETWORK_ADDRESS,
|
||||
"hostname", "localhost",
|
||||
"port", port,
|
||||
NULL);
|
||||
|
||||
addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET6));
|
||||
addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET));
|
||||
g_network_address_set_addresses (addr, addrs, 0);
|
||||
|
||||
return G_SOCKET_CONNECTABLE (addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_network_address_parse:
|
||||
* @host_and_port: the hostname and optionally a port
|
||||
|
@ -57,6 +57,8 @@ GType g_network_address_get_type (void) G_GNUC_CONST;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GSocketConnectable *g_network_address_new (const gchar *hostname,
|
||||
guint16 port);
|
||||
GLIB_AVAILABLE_IN_2_44
|
||||
GSocketConnectable *g_network_address_new_loopback (guint16 port);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GSocketConnectable *g_network_address_parse (const gchar *host_and_port,
|
||||
guint16 default_port,
|
||||
|
@ -348,6 +348,135 @@ test_uri_scope_id (void)
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
static void
|
||||
test_loopback_basic (void)
|
||||
{
|
||||
GNetworkAddress *addr; /* owned */
|
||||
|
||||
addr = G_NETWORK_ADDRESS (g_network_address_new_loopback (666));
|
||||
|
||||
/* Test basic properties. */
|
||||
g_assert_cmpstr (g_network_address_get_hostname (addr), ==, "localhost");
|
||||
g_assert_cmpuint (g_network_address_get_port (addr), ==, 666);
|
||||
g_assert_null (g_network_address_get_scheme (addr));
|
||||
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_socket_address_matches (GSocketAddress *a,
|
||||
const gchar *expected_address,
|
||||
guint16 expected_port)
|
||||
{
|
||||
GInetSocketAddress *sa;
|
||||
gchar *str; /* owned */
|
||||
|
||||
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
|
||||
|
||||
sa = G_INET_SOCKET_ADDRESS (a);
|
||||
g_assert_cmpint (g_inet_socket_address_get_port (sa), ==, expected_port);
|
||||
|
||||
str = g_inet_address_to_string (g_inet_socket_address_get_address (sa));
|
||||
g_assert_cmpstr (str, ==, expected_address);
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
test_loopback_sync (void)
|
||||
{
|
||||
GSocketConnectable *addr; /* owned */
|
||||
GSocketAddressEnumerator *enumerator; /* owned */
|
||||
GSocketAddress *a; /* owned */
|
||||
GError *error = NULL;
|
||||
|
||||
addr = g_network_address_new_loopback (616);
|
||||
enumerator = g_socket_connectable_enumerate (addr);
|
||||
|
||||
/* IPv6 address. */
|
||||
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
assert_socket_address_matches (a, "::1", 616);
|
||||
g_object_unref (a);
|
||||
|
||||
/* IPv4 address. */
|
||||
a = g_socket_address_enumerator_next (enumerator, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
assert_socket_address_matches (a, "127.0.0.1", 616);
|
||||
g_object_unref (a);
|
||||
|
||||
/* End of results. */
|
||||
g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_object_unref (enumerator);
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GList/*<owned GSocketAddress> */ *addrs; /* owned */
|
||||
GMainLoop *loop; /* owned */
|
||||
} AsyncData;
|
||||
|
||||
static void
|
||||
got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
|
||||
{
|
||||
GSocketAddressEnumerator *enumerator;
|
||||
AsyncData *data;
|
||||
GSocketAddress *a; /* owned */
|
||||
GError *error = NULL;
|
||||
|
||||
enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source_object);
|
||||
data = user_data;
|
||||
|
||||
a = g_socket_address_enumerator_next_finish (enumerator, result, &error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
if (a == NULL)
|
||||
{
|
||||
/* End of results. */
|
||||
data->addrs = g_list_reverse (data->addrs);
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
|
||||
data->addrs = g_list_prepend (data->addrs, a);
|
||||
|
||||
g_socket_address_enumerator_next_async (enumerator, NULL,
|
||||
got_addr, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_loopback_async (void)
|
||||
{
|
||||
GSocketConnectable *addr; /* owned */
|
||||
GSocketAddressEnumerator *enumerator; /* owned */
|
||||
AsyncData data = { 0, };
|
||||
|
||||
addr = g_network_address_new_loopback (610);
|
||||
enumerator = g_socket_connectable_enumerate (addr);
|
||||
|
||||
/* Get all the addresses. */
|
||||
data.addrs = NULL;
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
|
||||
|
||||
g_main_loop_run (data.loop);
|
||||
g_main_loop_unref (data.loop);
|
||||
|
||||
/* Check results. */
|
||||
g_assert_cmpuint (g_list_length (data.addrs), ==, 2);
|
||||
assert_socket_address_matches (data.addrs->data, "::1", 610);
|
||||
assert_socket_address_matches (data.addrs->next->data, "127.0.0.1", 610);
|
||||
|
||||
g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
|
||||
|
||||
g_object_unref (enumerator);
|
||||
g_object_unref (addr);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@ -388,6 +517,9 @@ main (int argc, char *argv[])
|
||||
|
||||
g_test_add_func ("/network-address/scope-id", test_host_scope_id);
|
||||
g_test_add_func ("/network-address/uri-scope-id", test_uri_scope_id);
|
||||
g_test_add_func ("/network-address/loopback/basic", test_loopback_basic);
|
||||
g_test_add_func ("/network-address/loopback/sync", test_loopback_sync);
|
||||
g_test_add_func ("/network-address/loopback/async", test_loopback_async);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user