+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);
+ 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);
+
+ 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);
+
+ if (data == NULL)
+ {
+ g_test_message ("Test not run: not supported by the OS");
+ 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;
+
+ data = create_server (G_SOCKET_FAMILY_IPV4, echo_server_thread, FALSE);
+ 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 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);
+}
+