/* GLib testing framework examples and tests * * Copyright (C) 2008-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 #include #include "glib-private.h" #include #include #ifdef G_OS_UNIX #include #include #include #include #include #endif #ifdef G_OS_WIN32 #include "giowin32-afunix.h" #include #endif #include "gnetworkingprivate.h" static gboolean ipv6_supported; typedef struct { GSocket *server; GSocket *client; GSocketFamily family; GThread *thread; GMainLoop *loop; GCancellable *cancellable; /* to shut down dgram echo server thread */ } IPTestData; static gpointer echo_server_dgram_thread (gpointer user_data) { IPTestData *data = user_data; GSocketAddress *sa; GCancellable *cancellable = data->cancellable; GSocket *sock; GError *error = NULL; gssize nread, nwrote; gchar buf[128]; sock = data->server; while (TRUE) { nread = g_socket_receive_from (sock, &sa, buf, sizeof (buf), cancellable, &error); if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) break; g_assert_no_error (error); g_assert_cmpint (nread, >=, 0); nwrote = g_socket_send_to (sock, sa, buf, nread, cancellable, &error); if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) break; g_assert_no_error (error); g_assert_cmpint (nwrote, ==, nread); g_object_unref (sa); } g_clear_error (&error); return NULL; } static gpointer echo_server_thread (gpointer user_data) { IPTestData *data = user_data; GSocket *sock; GError *error = NULL; gssize nread, nwrote; gchar buf[128]; sock = g_socket_accept (data->server, NULL, &error); g_assert_no_error (error); while (TRUE) { nread = g_socket_receive (sock, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (nread, >=, 0); if (nread == 0) break; nwrote = g_socket_send (sock, buf, nread, NULL, &error); g_assert_no_error (error); g_assert_cmpint (nwrote, ==, nread); } g_socket_close (sock, &error); g_assert_no_error (error); g_object_unref (sock); return NULL; } static IPTestData * create_server_full (GSocketFamily family, GSocketType socket_type, GThreadFunc server_thread, gboolean v4mapped, GError **error) { IPTestData *data; GSocket *server; GSocketAddress *addr; GInetAddress *iaddr; data = g_slice_new (IPTestData); data->family = family; data->server = server = g_socket_new (family, socket_type, G_SOCKET_PROTOCOL_DEFAULT, error); if (server == NULL) goto error; g_assert_cmpint (g_socket_get_family (server), ==, family); g_assert_cmpint (g_socket_get_socket_type (server), ==, socket_type); g_assert_cmpint (g_socket_get_protocol (server), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (server))); #endif g_socket_set_blocking (server, TRUE); #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) if (v4mapped) { g_socket_set_option (data->server, IPPROTO_IPV6, IPV6_V6ONLY, FALSE, NULL); if (!g_socket_speaks_ipv4 (data->server)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "IPv6-only server cannot speak IPv4"); goto error; } } #endif if (v4mapped) iaddr = g_inet_address_new_any (family); else iaddr = g_inet_address_new_loopback (family); addr = g_inet_socket_address_new (iaddr, 0); g_object_unref (iaddr); g_assert_cmpint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)), ==, 0); if (!g_socket_bind (server, addr, TRUE, error)) { g_object_unref (addr); goto error; } g_object_unref (addr); addr = g_socket_get_local_address (server, error); if (addr == NULL) goto error; g_assert_cmpint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)), !=, 0); g_object_unref (addr); if (socket_type == G_SOCKET_TYPE_STREAM) { if (!g_socket_listen (server, error)) goto error; } else { data->cancellable = g_cancellable_new (); } data->thread = g_thread_new ("server", server_thread, data); return data; error: g_clear_object (&data->server); g_slice_free (IPTestData, data); return NULL; } static IPTestData * create_server (GSocketFamily family, GThreadFunc server_thread, gboolean v4mapped, GError **error) { return create_server_full (family, G_SOCKET_TYPE_STREAM, server_thread, v4mapped, error); } static const gchar *testbuf = "0123456789abcdef"; static gboolean test_ip_async_read_ready (GSocket *client, GIOCondition cond, gpointer user_data) { IPTestData *data = user_data; GError *error = NULL; gssize len; gchar buf[128]; g_assert_cmpint (cond, ==, G_IO_IN); len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); g_assert_cmpstr (testbuf, ==, buf); g_main_loop_quit (data->loop); return FALSE; } static gboolean test_ip_async_write_ready (GSocket *client, GIOCondition cond, gpointer user_data) { IPTestData *data = user_data; GError *error = NULL; GSource *source; gssize len; g_assert_cmpint (cond, ==, G_IO_OUT); len = g_socket_send (client, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); source = g_socket_create_source (client, G_IO_IN, NULL); g_source_set_callback (source, (GSourceFunc)test_ip_async_read_ready, data, NULL); g_source_attach (source, NULL); g_source_unref (source); return FALSE; } static gboolean test_ip_async_timed_out (GSocket *client, GIOCondition cond, gpointer user_data) { IPTestData *data = user_data; GError *error = NULL; GSource *source; gssize len; gchar buf[128]; if (data->family == G_SOCKET_FAMILY_IPV4) { g_assert_cmpint (cond, ==, G_IO_IN); len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_cmpint (len, ==, -1); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_clear_error (&error); } source = g_socket_create_source (client, G_IO_OUT, NULL); g_source_set_callback (source, (GSourceFunc)test_ip_async_write_ready, data, NULL); g_source_attach (source, NULL); g_source_unref (source); return FALSE; } static gboolean test_ip_async_connected (GSocket *client, GIOCondition cond, gpointer user_data) { IPTestData *data = user_data; GError *error = NULL; GSource *source; gssize len; gchar buf[128]; g_socket_check_connect_result (client, &error); g_assert_no_error (error); /* We do this after the check_connect_result, since that will give a * more useful assertion in case of error. */ g_assert_cmpint (cond, ==, G_IO_OUT); g_assert (g_socket_is_connected (client)); /* This adds 1 second to "make check", so let's just only do it once. */ if (data->family == G_SOCKET_FAMILY_IPV4) { len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_cmpint (len, ==, -1); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); g_clear_error (&error); source = g_socket_create_source (client, G_IO_IN, NULL); g_source_set_callback (source, (GSourceFunc)test_ip_async_timed_out, data, NULL); g_source_attach (source, NULL); g_source_unref (source); } else test_ip_async_timed_out (client, 0, data); return FALSE; } static gboolean idle_test_ip_async_connected (gpointer user_data) { IPTestData *data = user_data; return test_ip_async_connected (data->client, G_IO_OUT, data); } static void test_ip_async (GSocketFamily family) { IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *addr; GSource *source; gssize len; gchar buf[128]; data = create_server (family, echo_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } g_assert_nonnull (data); addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); data->client = client; g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); g_socket_set_blocking (client, FALSE); g_socket_set_timeout (client, 1); if (g_socket_connect (client, addr, NULL, &error)) { g_assert_no_error (error); g_idle_add (idle_test_ip_async_connected, data); } else { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); g_clear_error (&error); source = g_socket_create_source (client, G_IO_OUT, NULL); g_source_set_callback (source, (GSourceFunc)test_ip_async_connected, data, NULL); g_source_attach (source, NULL); g_source_unref (source); } g_object_unref (addr); data->loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (data->loop); g_main_loop_unref (data->loop); g_socket_shutdown (client, FALSE, TRUE, &error); g_assert_no_error (error); g_thread_join (data->thread); if (family == G_SOCKET_FAMILY_IPV4) { /* Test that reading on a remote-closed socket gets back 0 bytes. */ len = g_socket_receive_with_blocking (client, buf, sizeof (buf), TRUE, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, 0); } else { /* Test that writing to a remote-closed socket gets back CONNECTION_CLOSED. */ len = g_socket_send_with_blocking (client, testbuf, strlen (testbuf) + 1, TRUE, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED); g_assert_cmpint (len, ==, -1); g_clear_error (&error); } g_socket_close (client, &error); g_assert_no_error (error); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (client); g_slice_free (IPTestData, data); } static void test_ipv4_async (void) { test_ip_async (G_SOCKET_FAMILY_IPV4); } static void test_ipv6_async (void) { if (!ipv6_supported) { g_test_skip ("No support for IPv6"); return; } test_ip_async (G_SOCKET_FAMILY_IPV6); } static const gchar testbuf2[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static void test_ip_sync (GSocketFamily family) { IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *addr; gssize len; gchar buf[128]; data = create_server (family, echo_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_assert (g_socket_is_connected (client)); g_object_unref (addr); /* This adds 1 second to "make check", so let's just only do it once. */ if (family == G_SOCKET_FAMILY_IPV4) { len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_cmpint (len, ==, -1); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_clear_error (&error); } len = g_socket_send (client, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); g_assert_cmpstr (testbuf, ==, buf); { GOutputVector v[7] = { { NULL, 0 }, }; v[0].buffer = testbuf2 + 0; v[0].size = 3; v[1].buffer = testbuf2 + 3; v[1].size = 5; v[2].buffer = testbuf2 + 3 + 5; v[2].size = 0; v[3].buffer = testbuf2 + 3 + 5; v[3].size = 6; v[4].buffer = testbuf2 + 3 + 5 + 6; v[4].size = 2; v[5].buffer = testbuf2 + 3 + 5 + 6 + 2; v[5].size = 1; v[6].buffer = testbuf2 + 3 + 5 + 6 + 2 + 1; v[6].size = strlen (testbuf2) - (3 + 5 + 6 + 2 + 1); len = g_socket_send_message (client, NULL, v, G_N_ELEMENTS (v), NULL, 0, 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf2)); memset (buf, 0, sizeof (buf)); len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf2)); g_assert_cmpstr (testbuf2, ==, buf); } g_socket_shutdown (client, FALSE, TRUE, &error); g_assert_no_error (error); g_thread_join (data->thread); if (family == G_SOCKET_FAMILY_IPV4) { /* Test that reading on a remote-closed socket gets back 0 bytes. */ len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, 0); } else { /* Test that writing to a remote-closed socket gets back CONNECTION_CLOSED. */ len = g_socket_send (client, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED); g_assert_cmpint (len, ==, -1); g_clear_error (&error); } g_socket_close (client, &error); g_assert_no_error (error); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (client); g_slice_free (IPTestData, data); } static void test_ipv4_sync (void) { test_ip_sync (G_SOCKET_FAMILY_IPV4); } static void test_ipv6_sync (void) { if (!ipv6_supported) { g_test_skip ("No support for IPv6"); return; } test_ip_sync (G_SOCKET_FAMILY_IPV6); } static void test_ip_sync_dgram (GSocketFamily family) { IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *dest_addr; gssize len; gchar buf[128]; data = create_server_full (family, G_SOCKET_TYPE_DATAGRAM, echo_server_dgram_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } dest_addr = g_socket_get_local_address (data->server, &error); client = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); len = g_socket_send_to (client, dest_addr, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); g_assert_cmpstr (testbuf, ==, buf); { GOutputMessage m[3] = { { NULL, NULL, 0, 0, NULL, 0 }, }; GInputMessage im[3] = { { NULL, NULL, 0, 0, 0, NULL, 0 }, }; GOutputVector v[7] = { { NULL, 0 }, }; GInputVector iv[7] = { { NULL, 0 }, }; v[0].buffer = testbuf2 + 0; v[0].size = 3; v[1].buffer = testbuf2 + 3; v[1].size = 5; v[2].buffer = testbuf2 + 3 + 5; v[2].size = 0; v[3].buffer = testbuf2 + 3 + 5; v[3].size = 6; v[4].buffer = testbuf2 + 3 + 5 + 6; v[4].size = 2; v[5].buffer = testbuf2 + 3 + 5 + 6 + 2; v[5].size = 1; v[6].buffer = testbuf2 + 3 + 5 + 6 + 2 + 1; v[6].size = strlen (testbuf2) - (3 + 5 + 6 + 2 + 1); iv[0].buffer = buf + 0; iv[0].size = 3; iv[1].buffer = buf + 3; iv[1].size = 5; iv[2].buffer = buf + 3 + 5; iv[2].size = 0; iv[3].buffer = buf + 3 + 5; iv[3].size = 6; iv[4].buffer = buf + 3 + 5 + 6; iv[4].size = 2; iv[5].buffer = buf + 3 + 5 + 6 + 2; iv[5].size = 1; iv[6].buffer = buf + 3 + 5 + 6 + 2 + 1; iv[6].size = sizeof (buf) - (3 + 5 + 6 + 2 + 1); len = g_socket_send_message (client, dest_addr, v, G_N_ELEMENTS (v), NULL, 0, 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf2)); memset (buf, 0, sizeof (buf)); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf2)); g_assert_cmpstr (testbuf2, ==, buf); m[0].vectors = &v[0]; m[0].num_vectors = 1; m[0].address = dest_addr; m[1].vectors = &v[0]; m[1].num_vectors = 6; m[1].address = dest_addr; m[2].vectors = &v[6]; m[2].num_vectors = 1; m[2].address = dest_addr; len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, G_N_ELEMENTS (m)); g_assert_cmpint (m[0].bytes_sent, ==, 3); g_assert_cmpint (m[1].bytes_sent, ==, 17); g_assert_cmpint (m[2].bytes_sent, ==, v[6].size); memset (buf, 0, sizeof (buf)); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, 3); memset (buf, 0, sizeof (buf)); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); /* v[0].size + v[1].size + v[2].size + v[3].size + v[4].size + v[5].size */ g_assert_cmpint (len, ==, 17); g_assert (memcmp (testbuf2, buf, 17) == 0); memset (buf, 0, sizeof (buf)); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, v[6].size); g_assert_cmpstr (buf, ==, v[6].buffer); /* reset since we're re-using the message structs */ m[0].bytes_sent = 0; m[1].bytes_sent = 0; m[2].bytes_sent = 0; /* now try receiving multiple messages */ len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, G_N_ELEMENTS (m)); g_assert_cmpint (m[0].bytes_sent, ==, 3); g_assert_cmpint (m[1].bytes_sent, ==, 17); g_assert_cmpint (m[2].bytes_sent, ==, v[6].size); im[0].vectors = &iv[0]; im[0].num_vectors = 1; im[1].vectors = &iv[0]; im[1].num_vectors = 6; im[2].vectors = &iv[6]; im[2].num_vectors = 1; memset (buf, 0, sizeof (buf)); len = g_socket_receive_messages (client, im, G_N_ELEMENTS (im), 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, G_N_ELEMENTS (im)); g_assert_cmpuint (im[0].bytes_received, ==, 3); /* v[0].size + v[1].size + v[2].size + v[3].size + v[4].size + v[5].size */ g_assert_cmpuint (im[1].bytes_received, ==, 17); g_assert_cmpuint (im[2].bytes_received, ==, v[6].size); /* reset since we're re-using the message structs */ m[0].bytes_sent = 0; m[1].bytes_sent = 0; m[2].bytes_sent = 0; /* now try to generate an early return by omitting the destination address on [1] */ m[1].address = NULL; len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, 1); g_assert_cmpint (m[0].bytes_sent, ==, 3); g_assert_cmpint (m[1].bytes_sent, ==, 0); g_assert_cmpint (m[2].bytes_sent, ==, 0); /* reset since we're re-using the message structs */ m[0].bytes_sent = 0; m[1].bytes_sent = 0; m[2].bytes_sent = 0; /* now try to generate an error by omitting all destination addresses */ m[0].address = NULL; m[1].address = NULL; m[2].address = NULL; len = g_socket_send_messages (client, m, G_N_ELEMENTS (m), 0, NULL, &error); /* This error code may vary between platforms and over time; it is not guaranteed API: */ #ifndef G_OS_WIN32 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); #else g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED); #endif g_clear_error (&error); g_assert_cmpint (len, ==, -1); g_assert_cmpint (m[0].bytes_sent, ==, 0); g_assert_cmpint (m[1].bytes_sent, ==, 0); g_assert_cmpint (m[2].bytes_sent, ==, 0); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); g_assert_cmpint (len, ==, 3); } g_cancellable_cancel (data->cancellable); g_thread_join (data->thread); g_socket_close (client, &error); g_assert_no_error (error); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (data->cancellable); g_object_unref (client); g_object_unref (dest_addr); g_slice_free (IPTestData, data); } static void test_ipv4_sync_dgram (void) { test_ip_sync_dgram (G_SOCKET_FAMILY_IPV4); } static void test_ipv6_sync_dgram (void) { if (!ipv6_supported) { g_test_skip ("No support for IPv6"); return; } test_ip_sync_dgram (G_SOCKET_FAMILY_IPV6); } static gpointer cancellable_thread_cb (gpointer data) { GCancellable *cancellable = data; g_usleep (0.1 * G_USEC_PER_SEC); g_cancellable_cancel (cancellable); g_object_unref (cancellable); return NULL; } static void test_ip_sync_dgram_timeouts (GSocketFamily family) { GError *error = NULL; GSocket *client = NULL; GCancellable *cancellable = NULL; GThread *cancellable_thread = NULL; gssize len; #ifdef G_OS_WIN32 GInetAddress *iaddr; GSocketAddress *addr; #endif client = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif #ifdef G_OS_WIN32 /* Winsock can't recv() on unbound udp socket */ iaddr = g_inet_address_new_loopback (family); addr = g_inet_socket_address_new (iaddr, 0); g_object_unref (iaddr); g_socket_bind (client, addr, TRUE, &error); g_object_unref (addr); g_assert_no_error (error); #endif /* No overall timeout: test the per-operation timeouts instead. */ g_socket_set_timeout (client, 0); cancellable = g_cancellable_new (); /* Check for timeouts when no server is running. */ { gint64 start_time; GInputMessage im = { NULL, NULL, 0, 0, 0, NULL, 0 }; GInputVector iv = { NULL, 0 }; guint8 buf[128]; iv.buffer = buf; iv.size = sizeof (buf); im.vectors = &iv; im.num_vectors = 1; memset (buf, 0, sizeof (buf)); /* Try a non-blocking read. */ g_socket_set_blocking (client, FALSE); len = g_socket_receive_messages (client, &im, 1, 0 /* flags */, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); g_assert_cmpint (len, ==, -1); g_clear_error (&error); /* Try a timeout read. Can’t really validate the time taken more than * checking it’s positive. */ g_socket_set_timeout (client, 1); g_socket_set_blocking (client, TRUE); start_time = g_get_monotonic_time (); len = g_socket_receive_messages (client, &im, 1, 0 /* flags */, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_assert_cmpint (len, ==, -1); g_assert_cmpint (g_get_monotonic_time () - start_time, >, 0); g_clear_error (&error); /* Try a blocking read, cancelled from another thread. */ g_socket_set_timeout (client, 0); cancellable_thread = g_thread_new ("cancellable", cancellable_thread_cb, g_object_ref (cancellable)); start_time = g_get_monotonic_time (); len = g_socket_receive_messages (client, &im, 1, 0 /* flags */, cancellable, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert_cmpint (len, ==, -1); g_assert_cmpint (g_get_monotonic_time () - start_time, >, 0); g_clear_error (&error); g_thread_join (cancellable_thread); } g_socket_close (client, &error); g_assert_no_error (error); g_object_unref (client); g_object_unref (cancellable); } static void test_ipv4_sync_dgram_timeouts (void) { test_ip_sync_dgram_timeouts (G_SOCKET_FAMILY_IPV4); } static void test_ipv6_sync_dgram_timeouts (void) { if (!ipv6_supported) { g_test_skip ("No support for IPv6"); return; } test_ip_sync_dgram_timeouts (G_SOCKET_FAMILY_IPV6); } static gpointer graceful_server_thread (gpointer user_data) { IPTestData *data = user_data; GSocket *sock; GError *error = NULL; gssize len; sock = g_socket_accept (data->server, NULL, &error); g_assert_no_error (error); len = g_socket_send (sock, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); return sock; } static void test_close_graceful (void) { GSocketFamily family = G_SOCKET_FAMILY_IPV4; IPTestData *data; GError *error = NULL; GSocket *client, *server; GSocketAddress *addr; gssize len; gchar buf[128]; data = create_server (family, graceful_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_assert (g_socket_is_connected (client)); g_object_unref (addr); server = g_thread_join (data->thread); /* similar to g_tcp_connection_set_graceful_disconnect(), but explicit */ g_socket_shutdown (server, FALSE, TRUE, &error); g_assert_no_error (error); /* we must timeout */ g_socket_condition_wait (client, G_IO_HUP, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_clear_error (&error); /* check that the remaining data is received */ len = g_socket_receive (client, buf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); /* and only then the connection is closed */ len = g_socket_receive (client, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, 0); g_socket_close (server, &error); g_assert_no_error (error); g_socket_close (client, &error); g_assert_no_error (error); g_object_unref (server); g_object_unref (data->server); g_object_unref (client); g_slice_free (IPTestData, data); } #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) static gpointer v4mapped_server_thread (gpointer user_data) { IPTestData *data = user_data; GSocket *sock; GError *error = NULL; GSocketAddress *addr; sock = g_socket_accept (data->server, NULL, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (sock), ==, G_SOCKET_FAMILY_IPV6); addr = g_socket_get_local_address (sock, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_IPV4); g_object_unref (addr); addr = g_socket_get_remote_address (sock, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_IPV4); g_object_unref (addr); g_socket_close (sock, &error); g_assert_no_error (error); g_object_unref (sock); return NULL; } static void test_ipv6_v4mapped (void) { IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *addr, *v4addr; GInetAddress *iaddr; if (!ipv6_supported) { g_test_skip ("No support for IPv6"); return; } data = create_server (G_SOCKET_FAMILY_IPV6, v4mapped_server_thread, TRUE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } client = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); v4addr = g_inet_socket_address_new (iaddr, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr))); g_object_unref (iaddr); g_object_unref (addr); g_socket_connect (client, v4addr, NULL, &error); g_assert_no_error (error); g_assert (g_socket_is_connected (client)); g_thread_join (data->thread); g_socket_close (client, &error); g_assert_no_error (error); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (client); g_object_unref (v4addr); g_slice_free (IPTestData, data); } #endif static void test_timed_wait (void) { IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *addr; gint64 start_time; gint poll_duration; if (!g_test_thorough ()) { g_test_skip ("Not running timing heavy test"); return; } data = create_server (G_SOCKET_FAMILY_IPV4, echo_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_object_unref (addr); start_time = g_get_monotonic_time (); g_socket_condition_timed_wait (client, G_IO_IN, 100000 /* 100 ms */, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); g_clear_error (&error); poll_duration = g_get_monotonic_time () - start_time; g_assert_cmpint (poll_duration, >=, 98000); g_assert_cmpint (poll_duration, <, 112000); g_socket_close (client, &error); g_assert_no_error (error); g_thread_join (data->thread); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (client); g_slice_free (IPTestData, data); } static int duplicate_socket_fd (int fd) { #ifdef G_OS_WIN32 WSAPROTOCOL_INFO info; if (WSADuplicateSocket ((SOCKET)fd, GetCurrentProcessId (), &info)) { gchar *emsg = g_win32_error_message (WSAGetLastError ()); g_test_message ("Error duplicating socket: %s", emsg); g_free (emsg); return -1; } return (int)WSASocket (FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &info, 0, 0); #else return dup (fd); #endif } static void test_fd_reuse (void) { IPTestData *data; GError *error = NULL; GSocket *client; GSocket *client2; GSocketAddress *addr; int fd; gssize len; gchar buf[128]; g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=741707"); data = create_server (G_SOCKET_FAMILY_IPV4, echo_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_assert (g_socket_is_connected (client)); g_object_unref (addr); /* we have to dup otherwise the fd gets closed twice on unref */ fd = duplicate_socket_fd (g_socket_get_fd (client)); client2 = g_socket_new_from_fd (fd, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (client2), ==, g_socket_get_family (client)); g_assert_cmpint (g_socket_get_socket_type (client2), ==, g_socket_get_socket_type (client)); g_assert_cmpint (g_socket_get_protocol (client2), ==, G_SOCKET_PROTOCOL_TCP); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif len = g_socket_send (client2, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); len = g_socket_receive (client2, buf, sizeof (buf), NULL, &error); g_assert_no_error (error); g_assert_cmpint (len, ==, strlen (testbuf) + 1); g_assert_cmpstr (testbuf, ==, buf); g_socket_shutdown (client, FALSE, TRUE, &error); g_assert_no_error (error); /* The semantics of dup()+shutdown() are ambiguous; this call will succeed * on Linux, but return ENOTCONN on OS X. */ g_socket_shutdown (client2, FALSE, TRUE, NULL); g_thread_join (data->thread); g_socket_close (client, &error); g_assert_no_error (error); g_socket_close (client2, &error); g_assert_no_error (error); g_socket_close (data->server, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_fd (client), ==, -1); g_assert_cmpint (g_socket_get_fd (client2), ==, -1); g_assert_cmpint (g_socket_get_fd (data->server), ==, -1); g_object_unref (data->server); g_object_unref (client); g_object_unref (client2); g_slice_free (IPTestData, data); } static void test_sockaddr (void) { struct sockaddr_in6 sin6, gsin6; GSocketAddress *saddr; GInetSocketAddress *isaddr; GInetAddress *iaddr; GError *error = NULL; memset (&sin6, 0, sizeof (sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_loopback; sin6.sin6_port = g_htons (42); sin6.sin6_scope_id = 17; sin6.sin6_flowinfo = 1729; saddr = g_socket_address_new_from_native (&sin6, sizeof (sin6)); g_assert (G_IS_INET_SOCKET_ADDRESS (saddr)); isaddr = G_INET_SOCKET_ADDRESS (saddr); iaddr = g_inet_socket_address_get_address (isaddr); g_assert_cmpint (g_inet_address_get_family (iaddr), ==, G_SOCKET_FAMILY_IPV6); g_assert (g_inet_address_get_is_loopback (iaddr)); g_assert_cmpint (g_inet_socket_address_get_port (isaddr), ==, 42); g_assert_cmpint (g_inet_socket_address_get_scope_id (isaddr), ==, 17); g_assert_cmpint (g_inet_socket_address_get_flowinfo (isaddr), ==, 1729); g_socket_address_to_native (saddr, &gsin6, sizeof (gsin6), &error); g_assert_no_error (error); g_assert (memcmp (&sin6.sin6_addr, &gsin6.sin6_addr, sizeof (struct in6_addr)) == 0); g_assert_cmpint (sin6.sin6_port, ==, gsin6.sin6_port); g_assert_cmpint (sin6.sin6_scope_id, ==, gsin6.sin6_scope_id); g_assert_cmpint (sin6.sin6_flowinfo, ==, gsin6.sin6_flowinfo); g_object_unref (saddr); } static void bind_win32_unixfd (int fd) { #ifdef G_OS_WIN32 gint len, ret; struct sockaddr_un addr; memset (&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; len = g_snprintf (addr.sun_path, sizeof addr.sun_path, "%s" G_DIR_SEPARATOR_S "%d.sock", g_get_tmp_dir (), fd); g_assert_cmpint (len, <=, sizeof addr.sun_path); ret = bind (fd, (struct sockaddr *)&addr, sizeof addr); g_assert_cmpint (ret, ==, 0); g_remove (addr.sun_path); #endif } static void test_unix_from_fd (void) { gint fd; GError *error; GSocket *s; fd = socket (AF_UNIX, SOCK_STREAM, 0); #ifdef G_OS_WIN32 if (fd == -1) { g_test_skip ("AF_UNIX not supported on this Windows system."); return; } #endif g_assert_cmpint (fd, !=, -1); bind_win32_unixfd (fd); error = NULL; s = g_socket_new_from_fd (fd, &error); g_assert_no_error (error); g_assert_cmpint (g_socket_get_family (s), ==, G_SOCKET_FAMILY_UNIX); g_assert_cmpint (g_socket_get_socket_type (s), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (s), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (s))); #endif g_object_unref (s); } static void test_unix_connection (void) { gint fd; GError *error; GSocket *s; GSocketConnection *c; fd = socket (AF_UNIX, SOCK_STREAM, 0); #ifdef G_OS_WIN32 if (fd == -1) { g_test_skip ("AF_UNIX not supported on this Windows system."); return; } #endif g_assert_cmpint (fd, !=, -1); bind_win32_unixfd (fd); error = NULL; s = g_socket_new_from_fd (fd, &error); g_assert_no_error (error); c = g_socket_connection_factory_create_connection (s); g_assert (G_IS_UNIX_CONNECTION (c)); g_object_unref (c); g_object_unref (s); } #ifdef G_OS_UNIX static GSocketConnection * create_connection_for_fd (int fd) { GError *err = NULL; GSocket *socket; GSocketConnection *connection; socket = g_socket_new_from_fd (fd, &err); g_assert_no_error (err); g_assert (G_IS_SOCKET (socket)); connection = g_socket_connection_factory_create_connection (socket); g_assert (G_IS_UNIX_CONNECTION (connection)); g_object_unref (socket); return connection; } #define TEST_DATA "failure to say failure to say 'i love gnome-panel!'." static void test_unix_connection_ancillary_data (void) { GError *err = NULL; gint pv[2], sv[3]; gint status, fd, len; char buffer[1024]; pid_t pid; status = pipe (pv); g_assert_cmpint (status, ==, 0); status = socketpair (PF_UNIX, SOCK_STREAM, 0, sv); g_assert_cmpint (status, ==, 0); pid = fork (); g_assert_cmpint (pid, >=, 0); /* Child: close its copy of the write end of the pipe, receive it * again from the parent over the socket, and write some text to it. * * Parent: send the write end of the pipe (still open for the * parent) over the socket, close it, and read some text from the * read end of the pipe. */ if (pid == 0) { GSocketConnection *connection; close (sv[1]); connection = create_connection_for_fd (sv[0]); status = close (pv[1]); g_assert_cmpint (status, ==, 0); err = NULL; fd = g_unix_connection_receive_fd (G_UNIX_CONNECTION (connection), NULL, &err); g_assert_no_error (err); g_assert_cmpint (fd, >, -1); g_object_unref (connection); do len = write (fd, TEST_DATA, sizeof (TEST_DATA)); while (len == -1 && errno == EINTR); g_assert_cmpint (len, ==, sizeof (TEST_DATA)); exit (0); } else { GSocketConnection *connection; close (sv[0]); connection = create_connection_for_fd (sv[1]); err = NULL; g_unix_connection_send_fd (G_UNIX_CONNECTION (connection), pv[1], NULL, &err); g_assert_no_error (err); g_object_unref (connection); status = close (pv[1]); g_assert_cmpint (status, ==, 0); memset (buffer, 0xff, sizeof buffer); do len = read (pv[0], buffer, sizeof buffer); while (len == -1 && errno == EINTR); g_assert_cmpint (len, ==, sizeof (TEST_DATA)); g_assert_cmpstr (buffer, ==, TEST_DATA); waitpid (pid, &status, 0); g_assert (WIFEXITED (status)); g_assert_cmpint (WEXITSTATUS (status), ==, 0); } /* TODO: add test for g_unix_connection_send_credentials() and * g_unix_connection_receive_credentials(). */ } #endif #ifdef G_OS_WIN32 static void test_handle_not_socket (void) { GError *err = NULL; gchar *name = NULL; HANDLE hReadPipe, hWritePipe, h; int fd; g_assert_true (CreatePipe (&hReadPipe, &hWritePipe, NULL, 2048)); g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (hReadPipe)); g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (hWritePipe)); CloseHandle (hReadPipe); CloseHandle (hWritePipe); h = (HANDLE) _get_osfhandle (1); g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (h)); fd = g_file_open_tmp (NULL, &name, &err); g_assert_no_error (err); h = (HANDLE) _get_osfhandle (fd); g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (h)); g_close (fd, &err); g_assert_no_error (err); g_unlink (name); g_free (name); } #endif static gboolean postmortem_source_cb (GSocket *socket, GIOCondition condition, gpointer user_data) { gboolean *been_here = user_data; g_assert_cmpint (condition, ==, G_IO_NVAL); *been_here = TRUE; return FALSE; } static void test_source_postmortem (void) { GMainContext *context; GSocket *socket; GSource *source; GError *error = NULL; gboolean callback_visited = FALSE; socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); #ifdef G_OS_WIN32 if (error) { g_test_skip_printf ("AF_UNIX not supported on this Windows system: %s", error->message); g_clear_error (&error); return; } #endif g_assert_no_error (error); context = g_main_context_new (); source = g_socket_create_source (socket, G_IO_IN, NULL); g_source_set_callback (source, (GSourceFunc) postmortem_source_cb, &callback_visited, NULL); g_source_attach (source, context); g_source_unref (source); g_socket_close (socket, &error); g_assert_no_error (error); g_object_unref (socket); /* Test that, after a socket is closed, its source callback should be called * exactly once. */ g_main_context_iteration (context, FALSE); g_assert (callback_visited); g_assert (!g_main_context_pending (context)); g_main_context_unref (context); } static void test_reuse_tcp (void) { #if 1 g_test_skip ("Test is currently failing due to a kernel bug; see https://gitlab.gnome.org/GNOME/glib/-/issues/2879"); return; #else GSocket *sock1, *sock2; GError *error = NULL; GInetAddress *iaddr; GSocketAddress *addr; sock1 = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); addr = g_inet_socket_address_new (iaddr, 0); g_object_unref (iaddr); g_socket_bind (sock1, addr, TRUE, &error); g_object_unref (addr); g_assert_no_error (error); g_socket_listen (sock1, &error); g_assert_no_error (error); sock2 = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); addr = g_socket_get_local_address (sock1, &error); g_assert_no_error (error); g_socket_bind (sock2, addr, TRUE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE); g_clear_error (&error); g_object_unref (addr); g_object_unref (sock1); g_object_unref (sock2); #endif } static void test_reuse_udp (void) { GSocket *sock1, *sock2; GError *error = NULL; GInetAddress *iaddr; GSocketAddress *addr; sock1 = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); addr = g_inet_socket_address_new (iaddr, 0); g_object_unref (iaddr); g_socket_bind (sock1, addr, TRUE, &error); g_object_unref (addr); g_assert_no_error (error); sock2 = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); addr = g_socket_get_local_address (sock1, &error); g_assert_no_error (error); g_socket_bind (sock2, addr, TRUE, &error); g_object_unref (addr); g_assert_no_error (error); g_object_unref (sock1); g_object_unref (sock2); } static void test_get_available (gconstpointer user_data) { GSocketType socket_type = GPOINTER_TO_UINT (user_data); GError *err = NULL; GSocket *listener, *server, *client; GInetAddress *addr; GSocketAddress *saddr, *boundaddr; gchar data[] = "0123456789abcdef"; gchar buf[34]; gssize nread; listener = g_socket_new (G_SOCKET_FAMILY_IPV4, socket_type, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); g_assert (G_IS_SOCKET (listener)); client = g_socket_new (G_SOCKET_FAMILY_IPV4, socket_type, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); g_assert (G_IS_SOCKET (client)); if (socket_type == G_SOCKET_TYPE_STREAM) { g_socket_set_option (client, IPPROTO_TCP, TCP_NODELAY, TRUE, &err); g_assert_no_error (err); } addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); saddr = g_inet_socket_address_new (addr, 0); g_socket_bind (listener, saddr, TRUE, &err); g_assert_no_error (err); g_object_unref (saddr); g_object_unref (addr); boundaddr = g_socket_get_local_address (listener, &err); g_assert_no_error (err); addr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); saddr = g_inet_socket_address_new (addr, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (boundaddr))); g_object_unref (addr); g_object_unref (boundaddr); if (socket_type == G_SOCKET_TYPE_STREAM) { g_socket_listen (listener, &err); g_assert_no_error (err); g_socket_connect (client, saddr, NULL, &err); g_assert_no_error (err); server = g_socket_accept (listener, NULL, &err); g_assert_no_error (err); g_socket_set_blocking (server, FALSE); g_object_unref (listener); } else server = listener; g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err); g_assert_no_error (err); while (!g_socket_condition_wait (server, G_IO_IN, NULL, NULL)) ; g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data)); g_socket_send_to (client, saddr, data, sizeof (data), NULL, &err); g_assert_no_error (err); /* We need to wait until the data has actually been copied into the * server socket's buffers, but g_socket_condition_wait() won't help * here since the socket is definitely already readable. So there's * a race condition in checking its available bytes. In the TCP * case, we poll for a bit until the new data shows up. In the UDP * case, there's not much we can do, but at least the failure mode * is passes-when-it-shouldn't, not fails-when-it-shouldn't. */ if (socket_type == G_SOCKET_TYPE_STREAM) { int tries; for (tries = 0; tries < 100; tries++) { gssize res = g_socket_get_available_bytes (server); if ((res == -1) || ((gsize) res > sizeof (data))) break; g_usleep (100000); } g_assert_cmpint (g_socket_get_available_bytes (server), ==, 2 * sizeof (data)); } else { g_usleep (100000); g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data)); } g_assert_cmpint (sizeof (buf), >=, 2 * sizeof (data)); nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err); g_assert_no_error (err); if (socket_type == G_SOCKET_TYPE_STREAM) { g_assert_cmpint (nread, ==, 2 * sizeof (data)); g_assert_cmpint (g_socket_get_available_bytes (server), ==, 0); } else { g_assert_cmpint (nread, ==, sizeof (data)); g_assert_cmpint (g_socket_get_available_bytes (server), ==, sizeof (data)); } nread = g_socket_receive (server, buf, sizeof (buf), NULL, &err); if (socket_type == G_SOCKET_TYPE_STREAM) { g_assert_cmpint (nread, ==, -1); g_assert_error (err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK); g_clear_error (&err); } else { g_assert_cmpint (nread, ==, sizeof (data)); g_assert_no_error (err); } g_assert_cmpint (g_socket_get_available_bytes (server), ==, 0); g_socket_close (server, &err); g_assert_no_error (err); g_object_unref (saddr); g_object_unref (server); g_object_unref (client); } typedef struct { GInputStream *is; GOutputStream *os; const guint8 *write_data; guint8 *read_data; } TestReadWriteData; static gpointer test_read_write_write_thread (gpointer user_data) { TestReadWriteData *data = user_data; gsize bytes_written; GError *error = NULL; gboolean res; res = g_output_stream_write_all (data->os, data->write_data, 1024, &bytes_written, NULL, &error); g_assert_true (res); g_assert_no_error (error); g_assert_cmpint (bytes_written, ==, 1024); return NULL; } static gpointer test_read_write_read_thread (gpointer user_data) { TestReadWriteData *data = user_data; gsize bytes_read; GError *error = NULL; gboolean res; res = g_input_stream_read_all (data->is, data->read_data, 1024, &bytes_read, NULL, &error); g_assert_true (res); g_assert_no_error (error); g_assert_cmpint (bytes_read, ==, 1024); return NULL; } static gpointer test_read_write_writev_thread (gpointer user_data) { TestReadWriteData *data = user_data; gsize bytes_written; GError *error = NULL; gboolean res; GOutputVector vectors[3]; vectors[0].buffer = data->write_data; vectors[0].size = 256; vectors[1].buffer = data->write_data + 256; vectors[1].size = 256; vectors[2].buffer = data->write_data + 512; vectors[2].size = 512; res = g_output_stream_writev_all (data->os, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); g_assert_true (res); g_assert_no_error (error); g_assert_cmpint (bytes_written, ==, 1024); return NULL; } /* test if normal read/write/writev via the GSocket*Streams works on TCP sockets */ static void test_read_write (gconstpointer user_data) { gboolean writev = GPOINTER_TO_INT (user_data); GError *err = NULL; GSocket *listener, *server, *client; GInetAddress *addr; GSocketAddress *saddr, *boundaddr; TestReadWriteData data; guint8 data_write[1024], data_read[1024]; GSocketConnection *server_stream, *client_stream; GThread *write_thread, *read_thread; guint i; listener = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); g_assert (G_IS_SOCKET (listener)); client = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); g_assert (G_IS_SOCKET (client)); addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); saddr = g_inet_socket_address_new (addr, 0); g_socket_bind (listener, saddr, TRUE, &err); g_assert_no_error (err); g_object_unref (saddr); g_object_unref (addr); boundaddr = g_socket_get_local_address (listener, &err); g_assert_no_error (err); g_socket_listen (listener, &err); g_assert_no_error (err); addr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4); saddr = g_inet_socket_address_new (addr, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (boundaddr))); g_object_unref (addr); g_object_unref (boundaddr); g_socket_connect (client, saddr, NULL, &err); g_assert_no_error (err); server = g_socket_accept (listener, NULL, &err); g_assert_no_error (err); g_socket_set_blocking (server, FALSE); g_object_unref (listener); server_stream = g_socket_connection_factory_create_connection (server); g_assert_nonnull (server_stream); client_stream = g_socket_connection_factory_create_connection (client); g_assert_nonnull (client_stream); for (i = 0; i < sizeof (data_write); i++) data_write[i] = i; data.is = g_io_stream_get_input_stream (G_IO_STREAM (server_stream)); data.os = g_io_stream_get_output_stream (G_IO_STREAM (client_stream)); data.read_data = data_read; data.write_data = data_write; if (writev) write_thread = g_thread_new ("writer", test_read_write_writev_thread, &data); else write_thread = g_thread_new ("writer", test_read_write_write_thread, &data); read_thread = g_thread_new ("reader", test_read_write_read_thread, &data); g_thread_join (write_thread); g_thread_join (read_thread); g_assert_cmpmem (data_write, sizeof data_write, data_read, sizeof data_read); g_socket_close (server, &err); g_assert_no_error (err); g_object_unref (server_stream); g_object_unref (client_stream); g_object_unref (saddr); g_object_unref (server); g_object_unref (client); } #ifdef SO_NOSIGPIPE static void test_nosigpipe (void) { GSocket *sock; GError *error = NULL; gint value; sock = g_socket_new (AF_INET, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_get_option (sock, SOL_SOCKET, SO_NOSIGPIPE, &value, &error); g_assert_no_error (error); g_assert_true (value); g_object_unref (sock); } #endif #if G_CREDENTIALS_SUPPORTED static gpointer client_setup_thread (gpointer user_data); static void test_credentials_tcp_client (void) { const GSocketFamily family = G_SOCKET_FAMILY_IPV4; IPTestData *data; GError *error = NULL; GSocket *client; GSocketAddress *addr; GCredentials *creds; data = create_server (family, echo_server_thread, FALSE, &error); if (error != NULL) { g_test_skip_printf ("Failed to create server: %s", error->message); g_clear_error (&error); return; } addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); client = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_object_unref (addr); creds = g_socket_get_credentials (client, &error); if (creds != NULL) { gchar *str = g_credentials_to_string (creds); g_test_message ("Supported on this OS: %s", str); g_free (str); g_clear_object (&creds); } else { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_test_message ("Unsupported on this OS: %s", error->message); g_clear_error (&error); } g_socket_close (client, &error); g_assert_no_error (error); g_thread_join (data->thread); g_socket_close (data->server, &error); g_assert_no_error (error); g_object_unref (data->server); g_object_unref (client); g_slice_free (IPTestData, data); } static void test_credentials_tcp_server (void) { const GSocketFamily family = G_SOCKET_FAMILY_IPV4; IPTestData *data; GSocket *server; GError *error = NULL; GSocketAddress *addr = NULL; GInetAddress *iaddr = NULL; GSocket *sock = NULL; GCredentials *creds; data = g_slice_new0 (IPTestData); data->family = family; data->server = server = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); if (error != NULL) goto skip; g_socket_set_blocking (server, TRUE); iaddr = g_inet_address_new_loopback (family); addr = g_inet_socket_address_new (iaddr, 0); if (!g_socket_bind (server, addr, TRUE, &error)) goto skip; if (!g_socket_listen (server, &error)) goto skip; data->thread = g_thread_new ("client", client_setup_thread, data); sock = g_socket_accept (server, NULL, &error); g_assert_no_error (error); creds = g_socket_get_credentials (sock, &error); if (creds != NULL) { gchar *str = g_credentials_to_string (creds); g_test_message ("Supported on this OS: %s", str); g_free (str); g_clear_object (&creds); } else { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_test_message ("Unsupported on this OS: %s", error->message); g_clear_error (&error); } goto beach; skip: g_test_skip_printf ("Failed to create server: %s", error->message); goto beach; beach: { g_clear_error (&error); g_clear_object (&sock); g_clear_object (&addr); g_clear_object (&iaddr); g_clear_pointer (&data->thread, g_thread_join); g_clear_object (&data->server); g_clear_object (&data->client); g_slice_free (IPTestData, data); } } static gpointer client_setup_thread (gpointer user_data) { IPTestData *data = user_data; GSocketAddress *addr; GSocket *client; GError *error = NULL; addr = g_socket_get_local_address (data->server, &error); g_assert_no_error (error); data->client = client = g_socket_new (data->family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); g_assert_no_error (error); g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); g_object_unref (addr); return NULL; } #ifdef G_OS_WIN32 /* * _g_win32_socketpair: * * Create a pair of connected sockets, similar to POSIX/BSD socketpair(). * * Windows does not (yet) provide a socketpair() function. However, since the * introduction of AF_UNIX sockets, it is possible to implement a fairly close * function. */ static gint _g_win32_socketpair (gint domain, gint type, gint protocol, gint sv[2]) { struct sockaddr_un addr = { 0, }; socklen_t socklen; SOCKET listener = INVALID_SOCKET; SOCKET client = INVALID_SOCKET; SOCKET server = INVALID_SOCKET; gchar *path = NULL; int tmpfd, rv = -1; u_long arg, br; g_return_val_if_fail (sv != NULL, -1); addr.sun_family = AF_UNIX; socklen = sizeof (addr); tmpfd = g_file_open_tmp (NULL, &path, NULL); if (tmpfd == -1) { WSASetLastError (WSAEACCES); goto out; } g_close (tmpfd, NULL); if (strlen (path) >= sizeof (addr.sun_path)) { WSASetLastError (WSAEACCES); goto out; } strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1); listener = socket (domain, type, protocol); if (listener == INVALID_SOCKET) goto out; if (DeleteFile (path) == 0) { if (GetLastError () != ERROR_FILE_NOT_FOUND) goto out; } if (bind (listener, (struct sockaddr *) &addr, socklen) == SOCKET_ERROR) goto out; if (listen (listener, 1) == SOCKET_ERROR) goto out; client = socket (domain, type, protocol); if (client == INVALID_SOCKET) goto out; arg = 1; if (ioctlsocket (client, FIONBIO, &arg) == SOCKET_ERROR) goto out; if (connect (client, (struct sockaddr *) &addr, socklen) == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) goto out; server = accept (listener, NULL, NULL); if (server == INVALID_SOCKET) goto out; arg = 0; if (ioctlsocket (client, FIONBIO, &arg) == SOCKET_ERROR) goto out; if (WSAIoctl (server, SIO_AF_UNIX_GETPEERPID, NULL, 0U, &arg, sizeof (arg), &br, NULL, NULL) == SOCKET_ERROR || arg != GetCurrentProcessId ()) { WSASetLastError (WSAEACCES); goto out; } sv[0] = server; server = INVALID_SOCKET; sv[1] = client; client = INVALID_SOCKET; rv = 0; out: if (listener != INVALID_SOCKET) closesocket (listener); if (client != INVALID_SOCKET) closesocket (client); if (server != INVALID_SOCKET) closesocket (server); DeleteFile (path); g_free (path); return rv; } #endif /* G_OS_WIN32 */ static void test_credentials_unix_socketpair (void) { gint fds[2]; gint status; GSocket *sock[2]; GError *error = NULL; GCredentials *creds; #ifdef G_OS_WIN32 status = _g_win32_socketpair (PF_UNIX, SOCK_STREAM, 0, fds); if (status != 0) { g_test_skip ("AF_UNIX not supported on this Windows system."); return; } #else status = socketpair (PF_UNIX, SOCK_STREAM, 0, fds); #endif g_assert_cmpint (status, ==, 0); sock[0] = g_socket_new_from_fd (fds[0], &error); g_assert_no_error (error); sock[1] = g_socket_new_from_fd (fds[1], &error); g_assert_no_error (error); creds = g_socket_get_credentials (sock[0], &error); if (creds != NULL) { gchar *str = g_credentials_to_string (creds); g_test_message ("Supported on this OS: %s", str); g_free (str); g_clear_object (&creds); } else { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_test_message ("Unsupported on this OS: %s", error->message); g_clear_error (&error); } g_object_unref (sock[0]); g_object_unref (sock[1]); } #endif int main (int argc, char *argv[]) { GSocket *sock; GError *error = NULL; g_test_init (&argc, &argv, NULL); sock = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error); if (sock != NULL) { ipv6_supported = TRUE; g_object_unref (sock); } else { g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_clear_error (&error); } g_test_add_func ("/socket/ipv4_sync", test_ipv4_sync); g_test_add_func ("/socket/ipv4_async", test_ipv4_async); g_test_add_func ("/socket/ipv6_sync", test_ipv6_sync); g_test_add_func ("/socket/ipv6_async", test_ipv6_async); g_test_add_func ("/socket/ipv4_sync/datagram", test_ipv4_sync_dgram); g_test_add_func ("/socket/ipv4_sync/datagram/timeouts", test_ipv4_sync_dgram_timeouts); g_test_add_func ("/socket/ipv6_sync/datagram", test_ipv6_sync_dgram); g_test_add_func ("/socket/ipv6_sync/datagram/timeouts", test_ipv6_sync_dgram_timeouts); #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) g_test_add_func ("/socket/ipv6_v4mapped", test_ipv6_v4mapped); #endif g_test_add_func ("/socket/close_graceful", test_close_graceful); g_test_add_func ("/socket/timed_wait", test_timed_wait); g_test_add_func ("/socket/fd_reuse", test_fd_reuse); g_test_add_func ("/socket/address", test_sockaddr); g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd); g_test_add_func ("/socket/unix-connection", test_unix_connection); #ifdef G_OS_UNIX g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data); #endif #ifdef G_OS_WIN32 g_test_add_func ("/socket/win32-handle-not-socket", test_handle_not_socket); #endif g_test_add_func ("/socket/source-postmortem", test_source_postmortem); g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp); g_test_add_func ("/socket/reuse/udp", test_reuse_udp); g_test_add_data_func ("/socket/get_available/datagram", GUINT_TO_POINTER (G_SOCKET_TYPE_DATAGRAM), test_get_available); g_test_add_data_func ("/socket/get_available/stream", GUINT_TO_POINTER (G_SOCKET_TYPE_STREAM), test_get_available); g_test_add_data_func ("/socket/read_write", GUINT_TO_POINTER (FALSE), test_read_write); g_test_add_data_func ("/socket/read_writev", GUINT_TO_POINTER (TRUE), test_read_write); #ifdef SO_NOSIGPIPE g_test_add_func ("/socket/nosigpipe", test_nosigpipe); #endif #if G_CREDENTIALS_SUPPORTED g_test_add_func ("/socket/credentials/tcp_client", test_credentials_tcp_client); g_test_add_func ("/socket/credentials/tcp_server", test_credentials_tcp_server); g_test_add_func ("/socket/credentials/unix_socketpair", test_credentials_unix_socketpair); #endif return g_test_run(); }