/* GIO - GLib Input, Output and Streaming Library * * Copyright 2011 Red Hat, Inc. * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . */ #include "gio.h" /* hack */ #define GIO_COMPILATION #include "gnetworkmonitorbase.h" #include /* Test data; the GInetAddresses and GInetAddressMasks get filled in * in main(). Each address in a TestAddress matches the mask in its * corresponding TestMask, and none of them match any of the other * masks. The addresses in unmatched don't match any of the masks. */ typedef struct { const char *string; GInetAddress *address; } TestAddress; typedef struct { const char *mask_string; GInetAddressMask *mask; TestAddress *addresses; } TestMask; TestAddress net127addrs[] = { { "127.0.0.1", NULL }, { "127.0.0.2", NULL }, { "127.0.0.255", NULL }, { "127.0.1.0", NULL }, { "127.0.255.0", NULL }, { "127.0.255.0", NULL }, { "127.255.255.255", NULL }, { NULL, NULL } }; TestMask net127 = { "127.0.0.0/8", NULL, net127addrs }; TestAddress net10addrs[] = { { "10.0.0.1", NULL }, { "10.0.0.2", NULL }, { "10.0.0.255", NULL }, { NULL, NULL } }; TestMask net10 = { "10.0.0.0/24", NULL, net10addrs }; TestAddress net192addrs[] = { { "192.168.0.1", NULL }, { "192.168.0.2", NULL }, { "192.168.0.255", NULL }, { "192.168.1.0", NULL }, { "192.168.15.0", NULL }, { NULL, NULL } }; TestMask net192 = { "192.168.0.0/20", NULL, net192addrs }; TestAddress netlocal6addrs[] = { { "::1", NULL }, { NULL, NULL } }; TestMask netlocal6 = { "::1/128", NULL, netlocal6addrs }; TestAddress netfe80addrs[] = { { "fe80::", NULL }, { "fe80::1", NULL }, { "fe80::21b:77ff:fea2:972a", NULL }, { NULL, NULL } }; TestMask netfe80 = { "fe80::/64", NULL, netfe80addrs }; TestAddress unmatched[] = { { "10.0.1.0", NULL }, { "10.0.255.0", NULL }, { "10.255.255.255", NULL }, { "192.168.16.0", NULL }, { "192.168.255.0", NULL }, { "192.169.0.0", NULL }, { "192.255.255.255", NULL }, { "::2", NULL }, { "1::1", NULL }, { "fe80::1:0:0:0:0", NULL }, { "fe80:8000::0:0:0:0", NULL }, { NULL, NULL } }; GInetAddressMask *ip4_default, *ip6_default; static void notify_handler (GObject *object, GParamSpec *pspec, gpointer user_data) { gboolean *emitted = user_data; *emitted = TRUE; } static void network_changed_handler (GNetworkMonitor *monitor, gboolean available, gpointer user_data) { gboolean *emitted = user_data; *emitted = TRUE; } static void assert_signals (GNetworkMonitor *monitor, gboolean should_emit_notify, gboolean should_emit_network_changed, gboolean expected_network_available) { gboolean emitted_notify = FALSE, emitted_network_changed = FALSE; guint h1, h2; h1 = g_signal_connect (monitor, "notify::network-available", G_CALLBACK (notify_handler), &emitted_notify); h2 = g_signal_connect (monitor, "network-changed", G_CALLBACK (network_changed_handler), &emitted_network_changed); g_main_context_iteration (NULL, FALSE); g_signal_handler_disconnect (monitor, h1); g_signal_handler_disconnect (monitor, h2); g_assert (emitted_notify == should_emit_notify); g_assert (emitted_network_changed == should_emit_network_changed); g_assert (g_network_monitor_get_network_available (monitor) == expected_network_available); } typedef struct { GNetworkMonitor *monitor; GMainLoop *loop; GSocketAddress *sockaddr; gboolean should_be_reachable; } CanReachData; static void reach_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; gboolean reachable; CanReachData *data = user_data; reachable = g_network_monitor_can_reach_finish (data->monitor, res, &error); if (data->should_be_reachable) g_assert_no_error (error); else { g_assert (error != NULL); g_clear_error (&error); } g_assert (reachable == data->should_be_reachable); g_main_loop_quit (data->loop); } static gboolean test_reach_async (gpointer user_data) { CanReachData *data = user_data; g_network_monitor_can_reach_async (data->monitor, G_SOCKET_CONNECTABLE (data->sockaddr), NULL, reach_cb, data); return G_SOURCE_REMOVE; } static void run_tests (GNetworkMonitor *monitor, TestAddress *addresses, gboolean should_be_reachable) { GError *error = NULL; int i; gboolean reachable; GSocketAddress *sockaddr; CanReachData data; data.monitor = monitor; data.loop = g_main_loop_new (NULL, FALSE); for (i = 0; addresses[i].address; i++) { sockaddr = g_inet_socket_address_new (addresses[i].address, 0); reachable = g_network_monitor_can_reach (monitor, G_SOCKET_CONNECTABLE (sockaddr), NULL, &error); data.sockaddr = sockaddr; data.should_be_reachable = should_be_reachable; g_idle_add (test_reach_async, &data); g_main_loop_run (data.loop); g_object_unref (sockaddr); g_assert_cmpint (reachable, ==, should_be_reachable); if (should_be_reachable) g_assert_no_error (error); else { g_assert (error != NULL); g_clear_error (&error); } } g_main_loop_unref (data.loop); } static void test_default (void) { GNetworkMonitor *monitor, *m; GError *error = NULL; m = g_network_monitor_get_default (); g_assert (G_IS_NETWORK_MONITOR (m)); monitor = g_object_new (G_TYPE_NETWORK_MONITOR_BASE, NULL); g_initable_init (G_INITABLE (monitor), NULL, &error); g_assert_no_error (error); /* In the default configuration, all addresses are reachable */ run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, TRUE); assert_signals (monitor, FALSE, FALSE, TRUE); g_object_unref (monitor); } static void test_remove_default (void) { GNetworkMonitor *monitor; GError *error = NULL; monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL); g_assert_no_error (error); assert_signals (monitor, FALSE, FALSE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip4_default); assert_signals (monitor, FALSE, TRUE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip6_default); assert_signals (monitor, TRUE, TRUE, FALSE); /* Now nothing should be reachable */ run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_object_unref (monitor); } static void test_add_networks (void) { GNetworkMonitor *monitor; GError *error = NULL; monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL); g_assert_no_error (error); assert_signals (monitor, FALSE, FALSE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip4_default); assert_signals (monitor, FALSE, TRUE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip6_default); assert_signals (monitor, TRUE, TRUE, FALSE); /* Now add the masks one by one */ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net127.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net10.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net192.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), netlocal6.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), netfe80.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); g_object_unref (monitor); } static void test_remove_networks (void) { GNetworkMonitor *monitor; GError *error = NULL; monitor = g_initable_new (G_TYPE_NETWORK_MONITOR_BASE, NULL, &error, NULL); g_assert_no_error (error); assert_signals (monitor, FALSE, FALSE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip4_default); assert_signals (monitor, FALSE, TRUE, TRUE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), ip6_default); assert_signals (monitor, TRUE, TRUE, FALSE); /* First add them */ g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net127.mask); assert_signals (monitor, FALSE, TRUE, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net10.mask); assert_signals (monitor, FALSE, TRUE, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), net192.mask); assert_signals (monitor, FALSE, TRUE, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), netlocal6.mask); assert_signals (monitor, FALSE, TRUE, FALSE); g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (monitor), netfe80.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, TRUE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); /* Now remove them one by one */ g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), net127.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, TRUE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), net10.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, TRUE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), net192.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, TRUE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), netlocal6.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, TRUE); run_tests (monitor, unmatched, FALSE); g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (monitor), netfe80.mask); assert_signals (monitor, FALSE, TRUE, FALSE); run_tests (monitor, net127.addresses, FALSE); run_tests (monitor, net10.addresses, FALSE); run_tests (monitor, net192.addresses, FALSE); run_tests (monitor, netlocal6.addresses, FALSE); run_tests (monitor, netfe80.addresses, FALSE); run_tests (monitor, unmatched, FALSE); g_object_unref (monitor); } static void init_test (TestMask *test) { GError *error = NULL; int i; test->mask = g_inet_address_mask_new_from_string (test->mask_string, &error); g_assert_no_error (error); for (i = 0; test->addresses[i].string; i++) { test->addresses[i].address = g_inet_address_new_from_string (test->addresses[i].string); if (strchr (test->addresses[i].string, ':')) g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV6); else g_assert_cmpint (g_inet_address_get_family (test->addresses[i].address), ==, G_SOCKET_FAMILY_IPV4); } } static void cleanup_test (TestMask *test) { int i; g_object_unref (test->mask); for (i = 0; test->addresses[i].string; i++) g_object_unref (test->addresses[i].address); } static void watch_network_changed (GNetworkMonitor *monitor, gboolean available, gpointer user_data) { g_print ("Network is %s\n", available ? "up" : "down"); } static void watch_connectivity_changed (GNetworkMonitor *monitor, GParamSpec *pspec, gpointer user_data) { g_print ("Connectivity is %d\n", g_network_monitor_get_connectivity (monitor)); } static void watch_metered_changed (GNetworkMonitor *monitor, GParamSpec *pspec, gpointer user_data) { g_print ("Metered is %d\n", g_network_monitor_get_network_metered (monitor)); } static void do_watch_network (void) { GNetworkMonitor *monitor = g_network_monitor_get_default (); GMainLoop *loop; g_print ("Monitoring via %s\n", g_type_name_from_instance ((GTypeInstance *) monitor)); g_signal_connect (monitor, "network-changed", G_CALLBACK (watch_network_changed), NULL); g_signal_connect (monitor, "notify::connectivity", G_CALLBACK (watch_connectivity_changed), NULL); g_signal_connect (monitor, "notify::network-metered", G_CALLBACK (watch_metered_changed), NULL); watch_network_changed (monitor, g_network_monitor_get_network_available (monitor), NULL); watch_connectivity_changed (monitor, NULL, NULL); watch_metered_changed (monitor, NULL, NULL); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); } int main (int argc, char **argv) { int ret; if (argc == 2 && !strcmp (argv[1], "--watch")) { do_watch_network (); return 0; } g_test_init (&argc, &argv, NULL); /* GNetworkMonitor will resolve addresses through a proxy if one is set and a * GIO module is available to handle it. In these tests we deliberately * change the idea of a reachable network to exclude the proxy, which will * lead to negative results. We're not trying to test the proxy-resolving * functionality (that would be for e.g. glib-networking's testsuite), so * let's just use the dummy proxy resolver, which always pretends the * passed-in URL is directly resolvable. */ g_setenv ("GIO_USE_PROXY_RESOLVER", "dummy", TRUE); init_test (&net127); init_test (&net10); init_test (&net192); init_test (&netlocal6); init_test (&netfe80); ip4_default = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL); ip6_default = g_inet_address_mask_new_from_string ("::/0", NULL); g_test_add_func ("/network-monitor/default", test_default); g_test_add_func ("/network-monitor/remove_default", test_remove_default); g_test_add_func ("/network-monitor/add_networks", test_add_networks); g_test_add_func ("/network-monitor/remove_networks", test_remove_networks); ret = g_test_run (); cleanup_test (&net127); cleanup_test (&net10); cleanup_test (&net192); cleanup_test (&netlocal6); cleanup_test (&netfe80); g_object_unref (ip4_default); g_object_unref (ip6_default); return ret; }