[glib/wip/tingping/localhost-is-local] Always resolve localhost to loopback address
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/tingping/localhost-is-local] Always resolve localhost to loopback address
- Date: Thu, 24 Jan 2019 19:52:49 +0000 (UTC)
commit 660488a550155a6307e460fe276616ca031a9c9a
Author: Patrick Griffis <tingping tingping se>
Date: Thu Jan 24 14:47:35 2019 -0500
Always resolve localhost to loopback address
This implements this draft rfc[1] which recommends clients always
resolve "localhost" to a loopback address which has security
benefits such as preventing a malicious dns server to redirect
local connections.
[1] https://tools.ietf.org/html/draft-west-let-localhost-be-localhost-06
gio/gresolver.c | 21 ++++++--
gio/tests/network-address.c | 115 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 126 insertions(+), 10 deletions(-)
---
diff --git a/gio/gresolver.c b/gio/gresolver.c
index 6a33634c5..7dbf22fa1 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -302,9 +302,10 @@ remove_duplicates (GList *addrs)
* continue and trying to resolve the name as a hostname.
*/
static gboolean
-handle_ip_address (const char *hostname,
- GList **addrs,
- GError **error)
+handle_ip_address_or_localhost (const char *hostname,
+ GList **addrs,
+ GResolverNameLookupFlags flags,
+ GError **error)
{
GInetAddress *addr;
@@ -346,6 +347,16 @@ handle_ip_address (const char *hostname,
return TRUE;
}
+ /* As per https://tools.ietf.org/html/draft-west-let-localhost-be-localhost-06 */
+ if (g_ascii_strcasecmp (hostname, "localhost") == 0)
+ {
+ if (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY))
+ *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6));
+ if (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY))
+ *addrs = g_list_append (*addrs, g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4));
+ return TRUE;
+ }
+
return FALSE;
}
@@ -365,7 +376,7 @@ lookup_by_name_real (GResolver *resolver,
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* Check if @hostname is just an IP address */
- if (handle_ip_address (hostname, &addrs, error))
+ if (handle_ip_address_or_localhost (hostname, &addrs, flags, error))
return addrs;
if (g_hostname_is_non_ascii (hostname))
@@ -504,7 +515,7 @@ lookup_by_name_async_real (GResolver *resolver,
g_return_if_fail (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY && flags &
G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY));
/* Check if @hostname is just an IP address */
- if (handle_ip_address (hostname, &addrs, &error))
+ if (handle_ip_address_or_localhost (hostname, &addrs, flags, &error))
{
GTask *task;
diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c
index 4a2718e24..06a873a00 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -414,13 +414,70 @@ test_loopback_sync (void)
g_object_unref (addr);
}
+static void
+test_localhost_sync (void)
+{
+ GSocketConnectable *addr; /* owned */
+ GSocketAddressEnumerator *enumerator; /* owned */
+ GSocketAddress *a; /* owned */
+ GError *error = NULL;
+ GResolver *original_resolver; /* owned */
+ MockResolver *mock_resolver; /* owned */
+ GList *ipv4_results = NULL; /* owned */
+
+ /* Set up a DNS resolver that returns nonsense for "localhost" */
+ original_resolver = g_resolver_get_default ();
+ mock_resolver = mock_resolver_new ();
+ g_resolver_set_default (G_RESOLVER (mock_resolver));
+ ipv4_results = g_list_append (ipv4_results, g_inet_address_new_from_string ("123.123.123.123"));
+ mock_resolver_set_ipv4_results (mock_resolver, ipv4_results);
+
+ addr = g_network_address_new ("localhost", 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_resolver_set_default (original_resolver);
+ g_list_free_full (ipv4_results, (GDestroyNotify) g_object_unref);
+ g_object_unref (original_resolver);
+ g_object_unref (mock_resolver);
+ g_object_unref (enumerator);
+ g_object_unref (addr);
+}
+
typedef struct {
GList/*<owned GSocketAddress> */ *addrs; /* owned */
GMainLoop *loop; /* owned */
+ GSocketAddressEnumerator *enumerator; /* unowned */
guint delay_ms;
gint expected_error_code;
} AsyncData;
+static void got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data);
+
+static int
+on_delayed_get_addr (gpointer user_data)
+{
+ AsyncData *data = user_data;
+ g_socket_address_enumerator_next_async (data->enumerator, NULL,
+ got_addr, user_data);
+ return G_SOURCE_REMOVE;
+}
+
static void
got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
{
@@ -453,11 +510,14 @@ got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
data->addrs = g_list_prepend (data->addrs, a);
- if (data->delay_ms)
- g_usleep (data->delay_ms * 1000);
-
- g_socket_address_enumerator_next_async (enumerator, NULL,
- got_addr, user_data);
+ if (!data->delay_ms)
+ g_socket_address_enumerator_next_async (enumerator, NULL,
+ got_addr, user_data);
+ else
+ {
+ data->enumerator = enumerator;
+ g_timeout_add (data->delay_ms, on_delayed_get_addr, data);
+ }
}
}
@@ -491,6 +551,49 @@ test_loopback_async (void)
g_object_unref (addr);
}
+static void
+test_localhost_async (void)
+{
+ GSocketConnectable *addr; /* owned */
+ GSocketAddressEnumerator *enumerator; /* owned */
+ AsyncData data = { 0, };
+ GResolver *original_resolver; /* owned */
+ MockResolver *mock_resolver; /* owned */
+ GList *ipv4_results = NULL; /* owned */
+
+ /* Set up a DNS resolver that returns nonsense for "localhost" */
+ original_resolver = g_resolver_get_default ();
+ mock_resolver = mock_resolver_new ();
+ g_resolver_set_default (G_RESOLVER (mock_resolver));
+ ipv4_results = g_list_append (ipv4_results, g_inet_address_new_from_string ("123.123.123.123"));
+ mock_resolver_set_ipv4_results (mock_resolver, ipv4_results);
+
+ addr = g_network_address_new ("localhost", 610);
+ enumerator = g_socket_connectable_enumerate (addr);
+
+ /* Get all the addresses. */
+ data.addrs = NULL;
+ data.delay_ms = 1;
+ 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);
+
+ /* 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_resolver_set_default (original_resolver);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
+ g_list_free_full (ipv4_results, (GDestroyNotify) g_object_unref);
+ g_object_unref (original_resolver);
+ g_object_unref (mock_resolver);
+ g_object_unref (enumerator);
+ g_object_unref (addr);
+ g_main_loop_unref (data.loop);
+}
+
static void
test_to_string (void)
{
@@ -901,6 +1004,8 @@ main (int argc, char *argv[])
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);
+ g_test_add_func ("/network-address/localhost/async", test_localhost_async);
+ g_test_add_func ("/network-address/localhost/sync", test_localhost_sync);
g_test_add_func ("/network-address/to-string", test_to_string);
g_test_add ("/network-address/happy-eyeballs/basic", HappyEyeballsFixture, NULL,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]