From dea11643a9024874a355b7d7dcd26fd6bff01d6f Mon Sep 17 00:00:00 2001 From: Mike Salmela Date: Sat, 29 Apr 2023 21:04:33 +0300 Subject: Rename confusing socket test filenames The gio/tests/socket-client.c doesn't use GSocketClient, which makes the filename confusing. What the file actually tests is the GSocket. Rename it to socket-testclient.c The corresponding GSocket server test file naming doesn't conflict with other class names, but rename it to socket-testserver.c for consistency. Closes #2855 --- gio/tests/meson.build | 4 +- gio/tests/socket-client.c | 444 ------------------------------------------ gio/tests/socket-common.c | 2 +- gio/tests/socket-server.c | 366 ---------------------------------- gio/tests/socket-testclient.c | 444 ++++++++++++++++++++++++++++++++++++++++++ gio/tests/socket-testserver.c | 366 ++++++++++++++++++++++++++++++++++ 6 files changed, 813 insertions(+), 813 deletions(-) delete mode 100644 gio/tests/socket-client.c delete mode 100644 gio/tests/socket-server.c create mode 100644 gio/tests/socket-testclient.c create mode 100644 gio/tests/socket-testserver.c diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 8da884001..2ff34a5d0 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -590,8 +590,8 @@ test_extra_programs += { 'proxy' : {'install' : false}, 'resolver' : {'install' : false}, 'send-data' : {'install' : false}, - 'socket-server' : {'install' : false}, - 'socket-client' : { + 'socket-testserver' : {'install' : false}, + 'socket-testclient' : { 'extra_sources' : ['gtlsconsoleinteraction.c'], 'install' : false, }, diff --git a/gio/tests/socket-client.c b/gio/tests/socket-client.c deleted file mode 100644 index 025632767..000000000 --- a/gio/tests/socket-client.c +++ /dev/null @@ -1,444 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "gtlsconsoleinteraction.h" - -GMainLoop *loop; - -gboolean verbose = FALSE; -gboolean non_blocking = FALSE; -gboolean use_udp = FALSE; -int cancel_timeout = 0; -int read_timeout = 0; -gboolean unix_socket = FALSE; -gboolean tls = FALSE; - -static GOptionEntry cmd_entries[] = { - {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, - "Cancel any op after the specified amount of seconds", NULL}, - {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, - "Use udp instead of tcp", NULL}, - {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, - "Be verbose", NULL}, - {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, - "Enable non-blocking i/o", NULL}, -#ifdef G_OS_UNIX - {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, - "Use a unix socket instead of IP", NULL}, -#endif - {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, - "Time out reads after the specified number of seconds", NULL}, - {"tls", 'T', 0, G_OPTION_ARG_NONE, &tls, - "Use TLS (SSL)", NULL}, - G_OPTION_ENTRY_NULL -}; - -#include "socket-common.c" - -static gboolean -accept_certificate (GTlsClientConnection *conn, - GTlsCertificate *cert, - GTlsCertificateFlags errors, - gpointer user_data) -{ - g_print ("Certificate would have been rejected ( "); - if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA) - g_print ("unknown-ca "); - if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY) - g_print ("bad-identity "); - if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) - g_print ("not-activated "); - if (errors & G_TLS_CERTIFICATE_EXPIRED) - g_print ("expired "); - if (errors & G_TLS_CERTIFICATE_REVOKED) - g_print ("revoked "); - if (errors & G_TLS_CERTIFICATE_INSECURE) - g_print ("insecure "); - g_print (") but accepting anyway.\n"); - - return TRUE; -} - -static GTlsCertificate * -lookup_client_certificate (GTlsClientConnection *conn, - GError **error) -{ - GList *l, *accepted; - GList *c, *certificates; - GTlsDatabase *database; - GTlsCertificate *certificate = NULL; - GTlsConnection *base; - - accepted = g_tls_client_connection_get_accepted_cas (conn); - for (l = accepted; l != NULL; l = g_list_next (l)) - { - base = G_TLS_CONNECTION (conn); - database = g_tls_connection_get_database (base); - certificates = g_tls_database_lookup_certificates_issued_by (database, l->data, - g_tls_connection_get_interaction (base), - G_TLS_DATABASE_LOOKUP_KEYPAIR, - NULL, error); - if (error && *error) - break; - - if (certificates) - certificate = g_object_ref (certificates->data); - - for (c = certificates; c != NULL; c = g_list_next (c)) - g_object_unref (c->data); - g_list_free (certificates); - } - - for (l = accepted; l != NULL; l = g_list_next (l)) - g_byte_array_unref (l->data); - g_list_free (accepted); - - if (certificate == NULL && error && !*error) - g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED, - "Server requested a certificate, but could not find relevant certificate in database."); - return certificate; -} - -static gboolean -make_connection (const char *argument, - GTlsCertificate *certificate, - GCancellable *cancellable, - GSocket **socket, - GSocketAddress **address, - GIOStream **connection, - GInputStream **istream, - GOutputStream **ostream, - GError **error) -{ - GSocketType socket_type; - GSocketFamily socket_family; - GSocketAddressEnumerator *enumerator; - GSocketConnectable *connectable; - GSocketAddress *src_address; - GTlsInteraction *interaction; - GError *err = NULL; - - if (use_udp) - socket_type = G_SOCKET_TYPE_DATAGRAM; - else - socket_type = G_SOCKET_TYPE_STREAM; - - if (unix_socket) - socket_family = G_SOCKET_FAMILY_UNIX; - else - socket_family = G_SOCKET_FAMILY_IPV4; - - *socket = g_socket_new (socket_family, socket_type, 0, error); - if (*socket == NULL) - return FALSE; - - if (read_timeout) - g_socket_set_timeout (*socket, read_timeout); - - if (unix_socket) - { - GSocketAddress *addr; - - addr = socket_address_from_string (argument); - if (addr == NULL) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Could not parse '%s' as unix socket name", argument); - return FALSE; - } - connectable = G_SOCKET_CONNECTABLE (addr); - } - else - { - connectable = g_network_address_parse (argument, 7777, error); - if (connectable == NULL) - return FALSE; - } - - enumerator = g_socket_connectable_enumerate (connectable); - while (TRUE) - { - *address = g_socket_address_enumerator_next (enumerator, cancellable, error); - if (*address == NULL) - { - if (error != NULL && *error == NULL) - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No more addresses to try"); - return FALSE; - } - - if (g_socket_connect (*socket, *address, cancellable, &err)) - break; - g_message ("Connection to %s failed: %s, trying next", socket_address_to_string (*address), err->message); - g_clear_error (&err); - - g_object_unref (*address); - } - g_object_unref (enumerator); - - g_print ("Connected to %s\n", - socket_address_to_string (*address)); - - src_address = g_socket_get_local_address (*socket, error); - if (!src_address) - { - g_prefix_error (error, "Error getting local address: "); - return FALSE; - } - - g_print ("local address: %s\n", - socket_address_to_string (src_address)); - g_object_unref (src_address); - - if (use_udp) - { - *connection = NULL; - *istream = NULL; - *ostream = NULL; - } - else - *connection = G_IO_STREAM (g_socket_connection_factory_create_connection (*socket)); - - if (tls) - { - GIOStream *tls_conn; - - tls_conn = g_tls_client_connection_new (*connection, connectable, error); - if (!tls_conn) - { - g_prefix_error (error, "Could not create TLS connection: "); - return FALSE; - } - - g_signal_connect (tls_conn, "accept-certificate", - G_CALLBACK (accept_certificate), NULL); - - interaction = g_tls_console_interaction_new (); - g_tls_connection_set_interaction (G_TLS_CONNECTION (tls_conn), interaction); - g_object_unref (interaction); - - if (certificate) - g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_conn), certificate); - - g_object_unref (*connection); - *connection = G_IO_STREAM (tls_conn); - - if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), - cancellable, error)) - { - g_prefix_error (error, "Error during TLS handshake: "); - return FALSE; - } - } - g_object_unref (connectable); - - if (*connection) - { - *istream = g_io_stream_get_input_stream (*connection); - *ostream = g_io_stream_get_output_stream (*connection); - } - - return TRUE; -} - -int -main (int argc, - char *argv[]) -{ - GSocket *socket = NULL; - GSocketAddress *address = NULL; - GError *error = NULL; - GOptionContext *context; - GCancellable *cancellable; - GIOStream *connection = NULL; - GInputStream *istream = NULL; - GOutputStream *ostream = NULL; - GSocketAddress *src_address = NULL; - GTlsCertificate *certificate = NULL; - gint i; - - address = NULL; - connection = NULL; - - context = g_option_context_new (" [:port] - Test GSocket client stuff"); - g_option_context_add_main_entries (context, cmd_entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - g_printerr ("%s: %s\n", argv[0], error->message); - return 1; - } - - if (argc != 2) - { - g_printerr ("%s: %s\n", argv[0], "Need to specify hostname / unix socket name"); - return 1; - } - - if (use_udp && tls) - { - g_printerr ("DTLS (TLS over UDP) is not supported"); - return 1; - } - - if (cancel_timeout) - { - GThread *thread; - cancellable = g_cancellable_new (); - thread = g_thread_new ("cancel", cancel_thread, cancellable); - g_thread_unref (thread); - } - else - { - cancellable = NULL; - } - - loop = g_main_loop_new (NULL, FALSE); - - for (i = 0; i < 2; i++) - { - if (make_connection (argv[1], certificate, cancellable, &socket, &address, - &connection, &istream, &ostream, &error)) - break; - - if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED)) - { - g_clear_error (&error); - certificate = lookup_client_certificate (G_TLS_CLIENT_CONNECTION (connection), &error); - if (certificate != NULL) - continue; - } - - g_printerr ("%s: %s", argv[0], error->message); - return 1; - } - - /* TODO: Test non-blocking connect/handshake */ - if (non_blocking) - g_socket_set_blocking (socket, FALSE); - - while (TRUE) - { - gchar buffer[4096]; - gssize size; - gsize to_send; - - if (fgets (buffer, sizeof buffer, stdin) == NULL) - break; - - to_send = strlen (buffer); - while (to_send > 0) - { - if (use_udp) - { - ensure_socket_condition (socket, G_IO_OUT, cancellable); - size = g_socket_send_to (socket, address, - buffer, to_send, - cancellable, &error); - } - else - { - ensure_connection_condition (connection, G_IO_OUT, cancellable); - size = g_output_stream_write (ostream, - buffer, to_send, - cancellable, &error); - } - - if (size < 0) - { - if (g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_WOULD_BLOCK)) - { - g_print ("socket send would block, handling\n"); - g_error_free (error); - error = NULL; - continue; - } - else - { - g_printerr ("Error sending to socket: %s\n", - error->message); - return 1; - } - } - - g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); - - if (size == 0) - { - g_printerr ("Unexpected short write\n"); - return 1; - } - - to_send -= size; - } - - if (use_udp) - { - ensure_socket_condition (socket, G_IO_IN, cancellable); - size = g_socket_receive_from (socket, &src_address, - buffer, sizeof buffer, - cancellable, &error); - } - else - { - ensure_connection_condition (connection, G_IO_IN, cancellable); - size = g_input_stream_read (istream, - buffer, sizeof buffer, - cancellable, &error); - } - - if (size < 0) - { - g_printerr ("Error receiving from socket: %s\n", - error->message); - return 1; - } - - if (size == 0) - break; - - g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); - if (use_udp) - g_print (" from %s", socket_address_to_string (src_address)); - g_print ("\n"); - - if (verbose) - g_print ("-------------------------\n" - "%.*s" - "-------------------------\n", - (int)size, buffer); - - } - - g_print ("closing socket\n"); - - if (connection) - { - if (!g_io_stream_close (connection, cancellable, &error)) - { - g_printerr ("Error closing connection: %s\n", - error->message); - return 1; - } - g_object_unref (connection); - } - else - { - if (!g_socket_close (socket, &error)) - { - g_printerr ("Error closing socket: %s\n", - error->message); - return 1; - } - } - - g_object_unref (socket); - g_object_unref (address); - - return 0; -} diff --git a/gio/tests/socket-common.c b/gio/tests/socket-common.c index 18d083a30..b740f68e7 100644 --- a/gio/tests/socket-common.c +++ b/gio/tests/socket-common.c @@ -1,4 +1,4 @@ -/* #included into both socket-client.c and socket-server.c */ +/* #included into both socket-testclient.c and socket-testserver.c */ #ifdef G_OS_UNIX static const char *unix_socket_address_types[] = { diff --git a/gio/tests/socket-server.c b/gio/tests/socket-server.c deleted file mode 100644 index 61715b02d..000000000 --- a/gio/tests/socket-server.c +++ /dev/null @@ -1,366 +0,0 @@ -#include -#include -#include -#include -#include - -GMainLoop *loop; - -int default_port = 7777; -gboolean verbose = FALSE; -gboolean dont_reuse_address = FALSE; -gboolean non_blocking = FALSE; -gboolean use_udp = FALSE; -int cancel_timeout = 0; -int read_timeout = 0; -int delay = 0; -gboolean unix_socket = FALSE; -const char *tls_cert_file = NULL; - -static GOptionEntry cmd_entries[] = { - {"port", 'p', 0, G_OPTION_ARG_INT, &default_port, - "Local port to bind to", NULL}, - {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, - "Cancel any op after the specified amount of seconds", NULL}, - {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, - "Use udp instead of tcp", NULL}, - {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, - "Be verbose", NULL}, - {"no-reuse", 0, 0, G_OPTION_ARG_NONE, &dont_reuse_address, - "Don't SOADDRREUSE", NULL}, - {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, - "Enable non-blocking i/o", NULL}, -#ifdef G_OS_UNIX - {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, - "Use a unix socket instead of IP", NULL}, -#endif - {"delay", 'd', 0, G_OPTION_ARG_INT, &delay, - "Delay responses by the specified number of seconds", NULL}, - {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, - "Time out reads after the specified number of seconds", NULL}, - {"tls", 'T', 0, G_OPTION_ARG_STRING, &tls_cert_file, - "Use TLS (SSL) with indicated server certificate", "CERTFILE"}, - G_OPTION_ENTRY_NULL -}; - -#include "socket-common.c" - -int -main (int argc, - char *argv[]) -{ - GSocket *socket, *new_socket, *recv_socket; - GSocketAddress *src_address; - GSocketAddress *address = NULL; - GSocketType socket_type; - GSocketFamily socket_family; - GError *error = NULL; - GOptionContext *context; - GCancellable *cancellable; - char *display_addr; - GTlsCertificate *tlscert = NULL; - GIOStream *connection; - GInputStream *istream; - GOutputStream *ostream; - - context = g_option_context_new (" - Test GSocket server stuff"); - g_option_context_add_main_entries (context, cmd_entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - g_printerr ("%s: %s\n", argv[0], error->message); - return 1; - } - - if (unix_socket && argc != 2) - { - g_printerr ("%s: %s\n", argv[0], "Need to specify unix socket name"); - return 1; - } - - if (cancel_timeout) - { - GThread *thread; - cancellable = g_cancellable_new (); - thread = g_thread_new ("cancel", cancel_thread, cancellable); - g_thread_unref (thread); - } - else - { - cancellable = NULL; - } - - if (tls_cert_file) - { - if (use_udp) - { - g_printerr ("DTLS (TLS over UDP) is not supported"); - return 1; - } - - tlscert = g_tls_certificate_new_from_file (tls_cert_file, &error); - if (!tlscert) - { - g_printerr ("Could not read server certificate '%s': %s\n", - tls_cert_file, error->message); - return 1; - } - } - - loop = g_main_loop_new (NULL, FALSE); - - if (use_udp) - socket_type = G_SOCKET_TYPE_DATAGRAM; - else - socket_type = G_SOCKET_TYPE_STREAM; - - if (unix_socket) - socket_family = G_SOCKET_FAMILY_UNIX; - else - socket_family = G_SOCKET_FAMILY_IPV4; - - socket = g_socket_new (socket_family, socket_type, 0, &error); - - if (socket == NULL) - { - g_printerr ("%s: %s\n", argv[0], error->message); - return 1; - } - - if (non_blocking) - g_socket_set_blocking (socket, FALSE); - - if (unix_socket) - { - src_address = socket_address_from_string (argv[1]); - if (src_address == NULL) - { - g_printerr ("%s: Could not parse '%s' as unix socket name\n", argv[0], argv[1]); - return 1; - } - } - else - { - src_address = g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), default_port); - } - - if (!g_socket_bind (socket, src_address, !dont_reuse_address, &error)) - { - g_printerr ("Can't bind socket: %s\n", error->message); - return 1; - } - g_object_unref (src_address); - - if (!use_udp) - { - if (!g_socket_listen (socket, &error)) - { - g_printerr ("Can't listen on socket: %s\n", error->message); - return 1; - } - - address = g_socket_get_local_address (socket, &error); - if (!address) - { - g_printerr ("Error getting local address: %s\n", - error->message); - return 1; - } - display_addr = socket_address_to_string (address); - g_print ("listening on %s...\n", display_addr); - g_free (display_addr); - - ensure_socket_condition (socket, G_IO_IN, cancellable); - new_socket = g_socket_accept (socket, cancellable, &error); - if (!new_socket) - { - g_printerr ("Error accepting socket: %s\n", - error->message); - return 1; - } - - if (non_blocking) - g_socket_set_blocking (new_socket, FALSE); - if (read_timeout) - g_socket_set_timeout (new_socket, read_timeout); - - address = g_socket_get_remote_address (new_socket, &error); - if (!address) - { - g_printerr ("Error getting remote address: %s\n", - error->message); - return 1; - } - - display_addr = socket_address_to_string (address); - g_print ("got a new connection from %s\n", display_addr); - g_free(display_addr); - g_object_unref (address); - - recv_socket = new_socket; - - connection = G_IO_STREAM (g_socket_connection_factory_create_connection (recv_socket)); - g_object_unref (new_socket); - } - else - { - recv_socket = socket; - connection = NULL; - } - - if (tlscert) - { - GIOStream *tls_conn; - - tls_conn = g_tls_server_connection_new (connection, tlscert, &error); - if (!tls_conn) - { - g_printerr ("Could not create TLS connection: %s\n", - error->message); - return 1; - } - - if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), - cancellable, &error)) - { - g_printerr ("Error during TLS handshake: %s\n", - error->message); - return 1; - } - - g_object_unref (connection); - connection = tls_conn; - } - - if (connection) - { - istream = g_io_stream_get_input_stream (connection); - ostream = g_io_stream_get_output_stream (connection); - } - else - { - g_assert (use_udp); - istream = NULL; - ostream = NULL; - } - - while (TRUE) - { - gchar buffer[4096]; - gssize size; - gsize to_send; - - if (use_udp) - { - ensure_socket_condition (recv_socket, G_IO_IN, cancellable); - size = g_socket_receive_from (recv_socket, &address, - buffer, sizeof buffer, - cancellable, &error); - } - else - { - ensure_connection_condition (connection, G_IO_IN, cancellable); - size = g_input_stream_read (istream, - buffer, sizeof buffer, - cancellable, &error); - } - - if (size < 0) - { - g_printerr ("Error receiving from socket: %s\n", - error->message); - return 1; - } - - if (size == 0) - break; - - g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); - if (use_udp) - g_print (" from %s", socket_address_to_string (address)); - g_print ("\n"); - - if (verbose) - g_print ("-------------------------\n" - "%.*s\n" - "-------------------------\n", - (int)size, buffer); - - to_send = size; - - if (delay) - { - if (verbose) - g_print ("delaying %d seconds before response\n", delay); - g_usleep (1000 * 1000 * delay); - } - - while (to_send > 0) - { - if (use_udp) - { - ensure_socket_condition (recv_socket, G_IO_OUT, cancellable); - size = g_socket_send_to (recv_socket, address, - buffer, to_send, cancellable, &error); - } - else - { - ensure_connection_condition (connection, G_IO_OUT, cancellable); - size = g_output_stream_write (ostream, - buffer, to_send, - cancellable, &error); - } - - if (size < 0) - { - if (g_error_matches (error, - G_IO_ERROR, - G_IO_ERROR_WOULD_BLOCK)) - { - g_print ("socket send would block, handling\n"); - g_error_free (error); - error = NULL; - continue; - } - else - { - g_printerr ("Error sending to socket: %s\n", - error->message); - return 1; - } - } - - g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); - - if (size == 0) - { - g_printerr ("Unexpected short write\n"); - return 1; - } - - to_send -= size; - } - } - - g_print ("connection closed\n"); - - if (connection) - { - if (!g_io_stream_close (connection, NULL, &error)) - { - g_printerr ("Error closing connection stream: %s\n", - error->message); - return 1; - } - g_object_unref (connection); - } - - if (!g_socket_close (socket, &error)) - { - g_printerr ("Error closing socket: %s\n", - error->message); - return 1; - } - g_object_unref (socket); - - return 0; -} diff --git a/gio/tests/socket-testclient.c b/gio/tests/socket-testclient.c new file mode 100644 index 000000000..025632767 --- /dev/null +++ b/gio/tests/socket-testclient.c @@ -0,0 +1,444 @@ +#include +#include +#include +#include +#include +#include + +#include "gtlsconsoleinteraction.h" + +GMainLoop *loop; + +gboolean verbose = FALSE; +gboolean non_blocking = FALSE; +gboolean use_udp = FALSE; +int cancel_timeout = 0; +int read_timeout = 0; +gboolean unix_socket = FALSE; +gboolean tls = FALSE; + +static GOptionEntry cmd_entries[] = { + {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, + "Cancel any op after the specified amount of seconds", NULL}, + {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, + "Use udp instead of tcp", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Be verbose", NULL}, + {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, + "Enable non-blocking i/o", NULL}, +#ifdef G_OS_UNIX + {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, + "Use a unix socket instead of IP", NULL}, +#endif + {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, + "Time out reads after the specified number of seconds", NULL}, + {"tls", 'T', 0, G_OPTION_ARG_NONE, &tls, + "Use TLS (SSL)", NULL}, + G_OPTION_ENTRY_NULL +}; + +#include "socket-common.c" + +static gboolean +accept_certificate (GTlsClientConnection *conn, + GTlsCertificate *cert, + GTlsCertificateFlags errors, + gpointer user_data) +{ + g_print ("Certificate would have been rejected ( "); + if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA) + g_print ("unknown-ca "); + if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY) + g_print ("bad-identity "); + if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) + g_print ("not-activated "); + if (errors & G_TLS_CERTIFICATE_EXPIRED) + g_print ("expired "); + if (errors & G_TLS_CERTIFICATE_REVOKED) + g_print ("revoked "); + if (errors & G_TLS_CERTIFICATE_INSECURE) + g_print ("insecure "); + g_print (") but accepting anyway.\n"); + + return TRUE; +} + +static GTlsCertificate * +lookup_client_certificate (GTlsClientConnection *conn, + GError **error) +{ + GList *l, *accepted; + GList *c, *certificates; + GTlsDatabase *database; + GTlsCertificate *certificate = NULL; + GTlsConnection *base; + + accepted = g_tls_client_connection_get_accepted_cas (conn); + for (l = accepted; l != NULL; l = g_list_next (l)) + { + base = G_TLS_CONNECTION (conn); + database = g_tls_connection_get_database (base); + certificates = g_tls_database_lookup_certificates_issued_by (database, l->data, + g_tls_connection_get_interaction (base), + G_TLS_DATABASE_LOOKUP_KEYPAIR, + NULL, error); + if (error && *error) + break; + + if (certificates) + certificate = g_object_ref (certificates->data); + + for (c = certificates; c != NULL; c = g_list_next (c)) + g_object_unref (c->data); + g_list_free (certificates); + } + + for (l = accepted; l != NULL; l = g_list_next (l)) + g_byte_array_unref (l->data); + g_list_free (accepted); + + if (certificate == NULL && error && !*error) + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED, + "Server requested a certificate, but could not find relevant certificate in database."); + return certificate; +} + +static gboolean +make_connection (const char *argument, + GTlsCertificate *certificate, + GCancellable *cancellable, + GSocket **socket, + GSocketAddress **address, + GIOStream **connection, + GInputStream **istream, + GOutputStream **ostream, + GError **error) +{ + GSocketType socket_type; + GSocketFamily socket_family; + GSocketAddressEnumerator *enumerator; + GSocketConnectable *connectable; + GSocketAddress *src_address; + GTlsInteraction *interaction; + GError *err = NULL; + + if (use_udp) + socket_type = G_SOCKET_TYPE_DATAGRAM; + else + socket_type = G_SOCKET_TYPE_STREAM; + + if (unix_socket) + socket_family = G_SOCKET_FAMILY_UNIX; + else + socket_family = G_SOCKET_FAMILY_IPV4; + + *socket = g_socket_new (socket_family, socket_type, 0, error); + if (*socket == NULL) + return FALSE; + + if (read_timeout) + g_socket_set_timeout (*socket, read_timeout); + + if (unix_socket) + { + GSocketAddress *addr; + + addr = socket_address_from_string (argument); + if (addr == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Could not parse '%s' as unix socket name", argument); + return FALSE; + } + connectable = G_SOCKET_CONNECTABLE (addr); + } + else + { + connectable = g_network_address_parse (argument, 7777, error); + if (connectable == NULL) + return FALSE; + } + + enumerator = g_socket_connectable_enumerate (connectable); + while (TRUE) + { + *address = g_socket_address_enumerator_next (enumerator, cancellable, error); + if (*address == NULL) + { + if (error != NULL && *error == NULL) + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No more addresses to try"); + return FALSE; + } + + if (g_socket_connect (*socket, *address, cancellable, &err)) + break; + g_message ("Connection to %s failed: %s, trying next", socket_address_to_string (*address), err->message); + g_clear_error (&err); + + g_object_unref (*address); + } + g_object_unref (enumerator); + + g_print ("Connected to %s\n", + socket_address_to_string (*address)); + + src_address = g_socket_get_local_address (*socket, error); + if (!src_address) + { + g_prefix_error (error, "Error getting local address: "); + return FALSE; + } + + g_print ("local address: %s\n", + socket_address_to_string (src_address)); + g_object_unref (src_address); + + if (use_udp) + { + *connection = NULL; + *istream = NULL; + *ostream = NULL; + } + else + *connection = G_IO_STREAM (g_socket_connection_factory_create_connection (*socket)); + + if (tls) + { + GIOStream *tls_conn; + + tls_conn = g_tls_client_connection_new (*connection, connectable, error); + if (!tls_conn) + { + g_prefix_error (error, "Could not create TLS connection: "); + return FALSE; + } + + g_signal_connect (tls_conn, "accept-certificate", + G_CALLBACK (accept_certificate), NULL); + + interaction = g_tls_console_interaction_new (); + g_tls_connection_set_interaction (G_TLS_CONNECTION (tls_conn), interaction); + g_object_unref (interaction); + + if (certificate) + g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_conn), certificate); + + g_object_unref (*connection); + *connection = G_IO_STREAM (tls_conn); + + if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), + cancellable, error)) + { + g_prefix_error (error, "Error during TLS handshake: "); + return FALSE; + } + } + g_object_unref (connectable); + + if (*connection) + { + *istream = g_io_stream_get_input_stream (*connection); + *ostream = g_io_stream_get_output_stream (*connection); + } + + return TRUE; +} + +int +main (int argc, + char *argv[]) +{ + GSocket *socket = NULL; + GSocketAddress *address = NULL; + GError *error = NULL; + GOptionContext *context; + GCancellable *cancellable; + GIOStream *connection = NULL; + GInputStream *istream = NULL; + GOutputStream *ostream = NULL; + GSocketAddress *src_address = NULL; + GTlsCertificate *certificate = NULL; + gint i; + + address = NULL; + connection = NULL; + + context = g_option_context_new (" [:port] - Test GSocket client stuff"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc != 2) + { + g_printerr ("%s: %s\n", argv[0], "Need to specify hostname / unix socket name"); + return 1; + } + + if (use_udp && tls) + { + g_printerr ("DTLS (TLS over UDP) is not supported"); + return 1; + } + + if (cancel_timeout) + { + GThread *thread; + cancellable = g_cancellable_new (); + thread = g_thread_new ("cancel", cancel_thread, cancellable); + g_thread_unref (thread); + } + else + { + cancellable = NULL; + } + + loop = g_main_loop_new (NULL, FALSE); + + for (i = 0; i < 2; i++) + { + if (make_connection (argv[1], certificate, cancellable, &socket, &address, + &connection, &istream, &ostream, &error)) + break; + + if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED)) + { + g_clear_error (&error); + certificate = lookup_client_certificate (G_TLS_CLIENT_CONNECTION (connection), &error); + if (certificate != NULL) + continue; + } + + g_printerr ("%s: %s", argv[0], error->message); + return 1; + } + + /* TODO: Test non-blocking connect/handshake */ + if (non_blocking) + g_socket_set_blocking (socket, FALSE); + + while (TRUE) + { + gchar buffer[4096]; + gssize size; + gsize to_send; + + if (fgets (buffer, sizeof buffer, stdin) == NULL) + break; + + to_send = strlen (buffer); + while (to_send > 0) + { + if (use_udp) + { + ensure_socket_condition (socket, G_IO_OUT, cancellable); + size = g_socket_send_to (socket, address, + buffer, to_send, + cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_OUT, cancellable); + size = g_output_stream_write (ostream, + buffer, to_send, + cancellable, &error); + } + + if (size < 0) + { + if (g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK)) + { + g_print ("socket send would block, handling\n"); + g_error_free (error); + error = NULL; + continue; + } + else + { + g_printerr ("Error sending to socket: %s\n", + error->message); + return 1; + } + } + + g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); + + if (size == 0) + { + g_printerr ("Unexpected short write\n"); + return 1; + } + + to_send -= size; + } + + if (use_udp) + { + ensure_socket_condition (socket, G_IO_IN, cancellable); + size = g_socket_receive_from (socket, &src_address, + buffer, sizeof buffer, + cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_IN, cancellable); + size = g_input_stream_read (istream, + buffer, sizeof buffer, + cancellable, &error); + } + + if (size < 0) + { + g_printerr ("Error receiving from socket: %s\n", + error->message); + return 1; + } + + if (size == 0) + break; + + g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); + if (use_udp) + g_print (" from %s", socket_address_to_string (src_address)); + g_print ("\n"); + + if (verbose) + g_print ("-------------------------\n" + "%.*s" + "-------------------------\n", + (int)size, buffer); + + } + + g_print ("closing socket\n"); + + if (connection) + { + if (!g_io_stream_close (connection, cancellable, &error)) + { + g_printerr ("Error closing connection: %s\n", + error->message); + return 1; + } + g_object_unref (connection); + } + else + { + if (!g_socket_close (socket, &error)) + { + g_printerr ("Error closing socket: %s\n", + error->message); + return 1; + } + } + + g_object_unref (socket); + g_object_unref (address); + + return 0; +} diff --git a/gio/tests/socket-testserver.c b/gio/tests/socket-testserver.c new file mode 100644 index 000000000..61715b02d --- /dev/null +++ b/gio/tests/socket-testserver.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include + +GMainLoop *loop; + +int default_port = 7777; +gboolean verbose = FALSE; +gboolean dont_reuse_address = FALSE; +gboolean non_blocking = FALSE; +gboolean use_udp = FALSE; +int cancel_timeout = 0; +int read_timeout = 0; +int delay = 0; +gboolean unix_socket = FALSE; +const char *tls_cert_file = NULL; + +static GOptionEntry cmd_entries[] = { + {"port", 'p', 0, G_OPTION_ARG_INT, &default_port, + "Local port to bind to", NULL}, + {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, + "Cancel any op after the specified amount of seconds", NULL}, + {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, + "Use udp instead of tcp", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Be verbose", NULL}, + {"no-reuse", 0, 0, G_OPTION_ARG_NONE, &dont_reuse_address, + "Don't SOADDRREUSE", NULL}, + {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, + "Enable non-blocking i/o", NULL}, +#ifdef G_OS_UNIX + {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, + "Use a unix socket instead of IP", NULL}, +#endif + {"delay", 'd', 0, G_OPTION_ARG_INT, &delay, + "Delay responses by the specified number of seconds", NULL}, + {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, + "Time out reads after the specified number of seconds", NULL}, + {"tls", 'T', 0, G_OPTION_ARG_STRING, &tls_cert_file, + "Use TLS (SSL) with indicated server certificate", "CERTFILE"}, + G_OPTION_ENTRY_NULL +}; + +#include "socket-common.c" + +int +main (int argc, + char *argv[]) +{ + GSocket *socket, *new_socket, *recv_socket; + GSocketAddress *src_address; + GSocketAddress *address = NULL; + GSocketType socket_type; + GSocketFamily socket_family; + GError *error = NULL; + GOptionContext *context; + GCancellable *cancellable; + char *display_addr; + GTlsCertificate *tlscert = NULL; + GIOStream *connection; + GInputStream *istream; + GOutputStream *ostream; + + context = g_option_context_new (" - Test GSocket server stuff"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (unix_socket && argc != 2) + { + g_printerr ("%s: %s\n", argv[0], "Need to specify unix socket name"); + return 1; + } + + if (cancel_timeout) + { + GThread *thread; + cancellable = g_cancellable_new (); + thread = g_thread_new ("cancel", cancel_thread, cancellable); + g_thread_unref (thread); + } + else + { + cancellable = NULL; + } + + if (tls_cert_file) + { + if (use_udp) + { + g_printerr ("DTLS (TLS over UDP) is not supported"); + return 1; + } + + tlscert = g_tls_certificate_new_from_file (tls_cert_file, &error); + if (!tlscert) + { + g_printerr ("Could not read server certificate '%s': %s\n", + tls_cert_file, error->message); + return 1; + } + } + + loop = g_main_loop_new (NULL, FALSE); + + if (use_udp) + socket_type = G_SOCKET_TYPE_DATAGRAM; + else + socket_type = G_SOCKET_TYPE_STREAM; + + if (unix_socket) + socket_family = G_SOCKET_FAMILY_UNIX; + else + socket_family = G_SOCKET_FAMILY_IPV4; + + socket = g_socket_new (socket_family, socket_type, 0, &error); + + if (socket == NULL) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (non_blocking) + g_socket_set_blocking (socket, FALSE); + + if (unix_socket) + { + src_address = socket_address_from_string (argv[1]); + if (src_address == NULL) + { + g_printerr ("%s: Could not parse '%s' as unix socket name\n", argv[0], argv[1]); + return 1; + } + } + else + { + src_address = g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), default_port); + } + + if (!g_socket_bind (socket, src_address, !dont_reuse_address, &error)) + { + g_printerr ("Can't bind socket: %s\n", error->message); + return 1; + } + g_object_unref (src_address); + + if (!use_udp) + { + if (!g_socket_listen (socket, &error)) + { + g_printerr ("Can't listen on socket: %s\n", error->message); + return 1; + } + + address = g_socket_get_local_address (socket, &error); + if (!address) + { + g_printerr ("Error getting local address: %s\n", + error->message); + return 1; + } + display_addr = socket_address_to_string (address); + g_print ("listening on %s...\n", display_addr); + g_free (display_addr); + + ensure_socket_condition (socket, G_IO_IN, cancellable); + new_socket = g_socket_accept (socket, cancellable, &error); + if (!new_socket) + { + g_printerr ("Error accepting socket: %s\n", + error->message); + return 1; + } + + if (non_blocking) + g_socket_set_blocking (new_socket, FALSE); + if (read_timeout) + g_socket_set_timeout (new_socket, read_timeout); + + address = g_socket_get_remote_address (new_socket, &error); + if (!address) + { + g_printerr ("Error getting remote address: %s\n", + error->message); + return 1; + } + + display_addr = socket_address_to_string (address); + g_print ("got a new connection from %s\n", display_addr); + g_free(display_addr); + g_object_unref (address); + + recv_socket = new_socket; + + connection = G_IO_STREAM (g_socket_connection_factory_create_connection (recv_socket)); + g_object_unref (new_socket); + } + else + { + recv_socket = socket; + connection = NULL; + } + + if (tlscert) + { + GIOStream *tls_conn; + + tls_conn = g_tls_server_connection_new (connection, tlscert, &error); + if (!tls_conn) + { + g_printerr ("Could not create TLS connection: %s\n", + error->message); + return 1; + } + + if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), + cancellable, &error)) + { + g_printerr ("Error during TLS handshake: %s\n", + error->message); + return 1; + } + + g_object_unref (connection); + connection = tls_conn; + } + + if (connection) + { + istream = g_io_stream_get_input_stream (connection); + ostream = g_io_stream_get_output_stream (connection); + } + else + { + g_assert (use_udp); + istream = NULL; + ostream = NULL; + } + + while (TRUE) + { + gchar buffer[4096]; + gssize size; + gsize to_send; + + if (use_udp) + { + ensure_socket_condition (recv_socket, G_IO_IN, cancellable); + size = g_socket_receive_from (recv_socket, &address, + buffer, sizeof buffer, + cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_IN, cancellable); + size = g_input_stream_read (istream, + buffer, sizeof buffer, + cancellable, &error); + } + + if (size < 0) + { + g_printerr ("Error receiving from socket: %s\n", + error->message); + return 1; + } + + if (size == 0) + break; + + g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); + if (use_udp) + g_print (" from %s", socket_address_to_string (address)); + g_print ("\n"); + + if (verbose) + g_print ("-------------------------\n" + "%.*s\n" + "-------------------------\n", + (int)size, buffer); + + to_send = size; + + if (delay) + { + if (verbose) + g_print ("delaying %d seconds before response\n", delay); + g_usleep (1000 * 1000 * delay); + } + + while (to_send > 0) + { + if (use_udp) + { + ensure_socket_condition (recv_socket, G_IO_OUT, cancellable); + size = g_socket_send_to (recv_socket, address, + buffer, to_send, cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_OUT, cancellable); + size = g_output_stream_write (ostream, + buffer, to_send, + cancellable, &error); + } + + if (size < 0) + { + if (g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK)) + { + g_print ("socket send would block, handling\n"); + g_error_free (error); + error = NULL; + continue; + } + else + { + g_printerr ("Error sending to socket: %s\n", + error->message); + return 1; + } + } + + g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); + + if (size == 0) + { + g_printerr ("Unexpected short write\n"); + return 1; + } + + to_send -= size; + } + } + + g_print ("connection closed\n"); + + if (connection) + { + if (!g_io_stream_close (connection, NULL, &error)) + { + g_printerr ("Error closing connection stream: %s\n", + error->message); + return 1; + } + g_object_unref (connection); + } + + if (!g_socket_close (socket, &error)) + { + g_printerr ("Error closing socket: %s\n", + error->message); + return 1; + } + g_object_unref (socket); + + return 0; +} -- cgit v1.2.1