* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <gio/gio.h>
#ifdef G_OS_UNIX
#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
+#include <gio/gnetworking.h>
#include <gio/gunixconnection.h>
#endif
#include "gnetworkingprivate.h"
+static gboolean ipv6_supported;
+
typedef struct {
GSocket *server;
GSocket *client;
g_socket_close (sock, &error);
g_assert_no_error (error);
+ g_object_unref (sock);
return NULL;
}
#if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
if (v4mapped)
{
- int fd, v6_only;
-
- fd = g_socket_get_fd (server);
- v6_only = 0;
- setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6_only, sizeof (v6_only));
+ g_socket_set_option (data->server, IPPROTO_IPV6, IPV6_V6ONLY, FALSE, NULL);
+ if (! g_socket_speaks_ipv4 (data->server))
+ {
+ g_object_unref (data->server);
+ g_slice_free (IPTestData, data);
+ return NULL;
+ }
}
#endif
g_socket_listen (server, &error);
g_assert_no_error (error);
- data->thread = g_thread_new ("server", server_thread, data, TRUE, &error);
- g_assert_no_error (error);
+ data->thread = g_thread_new ("server", server_thread, data);
return data;
}
data = create_server (family, echo_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,
static void
test_ipv6_async (void)
{
+ if (!ipv6_supported)
+ {
+ g_test_skip ("No support for IPv6");
+ return;
+ }
+
test_ip_async (G_SOCKET_FAMILY_IPV6);
}
data = create_server (family, echo_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,
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 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)
g_socket_close (sock, &error);
g_assert_no_error (error);
+ g_object_unref (sock);
return NULL;
}
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,
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 (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);
+}
+
#ifdef G_OS_UNIX
static void
test_unix_from_fd (void)
}
#endif /* G_OS_UNIX */
+static void
+test_reuse_tcp (void)
+{
+ 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);
+}
+
+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;
+ 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);
+
+ saddr = g_socket_get_local_address (listener, &err);
+ g_assert_no_error (err);
+
+ 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++)
+ {
+ if (g_socket_get_available_bytes (server) > 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);
+}
+
int
main (int argc,
char *argv[])
{
- g_type_init ();
+ 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);
#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/address", test_sockaddr);
#ifdef G_OS_UNIX
g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd);
g_test_add_func ("/socket/unix-connection", test_unix_connection);
g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data);
#endif
+ 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);
return g_test_run();
}