[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: Fri, 25 Jan 2019 19:44:42 +0000 (UTC)
commit 170b6747da56a11c35ca124e711f05e4073e09dc
Author: Patrick Griffis <pgriffis igalia com>
Date: Thu Jan 24 14:47:35 2019 -0500
Always resolve localhost to loopback address
This always resolves "localhost" to a loopback address which
has security benefits such as preventing a malicious dns server
redirecting local connections and allows software to assume
it is a secure hostname.
gio/gresolver.c | 52 +++++++++++--
gio/tests/network-address.c | 179 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 221 insertions(+), 10 deletions(-)
---
diff --git a/gio/gresolver.c b/gio/gresolver.c
index 6a33634c5..583a7450d 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -296,15 +296,47 @@ remove_duplicates (GList *addrs)
}
}
+static gboolean
+hostname_is_localhost (const char *hostname)
+{
+ size_t len = strlen (hostname);
+ const char *p;
+
+ /* Match "localhost", "localhost.", and "*.localhost" */
+ if (len < strlen ("localhost"))
+ return FALSE;
+
+ if (hostname[len - 1] == '.')
+ len--;
+
+ p = hostname + len - 1;
+ while (p >= hostname)
+ {
+ if (*p == '.')
+ {
+ p++;
+ break;
+ }
+ else if (p == hostname)
+ break;
+ p--;
+ }
+
+ len -= p - hostname;
+
+ return g_ascii_strncasecmp (p, "localhost", len) == 0;
+}
+
/* Note that this does not follow the "FALSE means @error is set"
* convention. The return value tells the caller whether it should
* return @addrs and @error to the caller right away, or if it should
* 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 +378,16 @@ handle_ip_address (const char *hostname,
return TRUE;
}
+ /* Always resolve localhost to a loopback address so it can be reliably considered secure */
+ if (hostname_is_localhost (hostname))
+ {
+ 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 +407,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 +546,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..be7a0c265 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -414,13 +414,134 @@ 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_object_unref (enumerator);
+ g_object_unref (addr);
+
+ 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_object_unref (enumerator);
+ g_object_unref (addr);
+
+ addr = g_network_address_new ("foo.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_object_unref (enumerator);
+ g_object_unref (addr);
+
+ 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_object_unref (enumerator);
+ g_object_unref (addr);
+
+
+ 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);
+}
+
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 +574,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 +615,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 +1068,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]