summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am9
-rw-r--r--tests/ssl-test.c322
2 files changed, 329 insertions, 2 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e146a9aa..fa46ff01 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -6,7 +6,6 @@ INCLUDES = \
LIBS = $(top_builddir)/libsoup/libsoup-$(SOUP_API_VERSION).la
noinst_PROGRAMS = \
- auth-test \
date \
dict \
dns \
@@ -17,6 +16,8 @@ noinst_PROGRAMS = \
simple-httpd \
simple-proxy \
uri-parsing \
+ $(APACHE_TESTS) \
+ $(SSL_TESTS) \
$(XMLRPC_TESTS)
auth_test_SOURCES = auth-test.c apache-wrapper.c apache-wrapper.h
@@ -29,17 +30,21 @@ header_parsing_SOURCES = header-parsing.c
revserver_SOURCES = revserver.c
simple_httpd_SOURCES = simple-httpd.c
simple_proxy_SOURCES = simple-proxy.c
+ssl_test_SOURCES = ssl-test.c
uri_parsing_SOURCES = uri-parsing.c
xmlrpc_test_SOURCES = xmlrpc-test.c apache-wrapper.c apache-wrapper.h
if HAVE_APACHE
APACHE_TESTS = auth-test
endif
+if HAVE_SSL
+SSL_TESTS = ssl-test
+endif
if HAVE_XMLRPC_EPI_PHP
XMLRPC_TESTS = xmlrpc-test
endif
-TESTS = date header-parsing uri-parsing $(APACHE_TESTS) $(XMLRPC_TESTS)
+TESTS = date header-parsing uri-parsing $(APACHE_TESTS) $(SSL_TESTS) $(XMLRPC_TESTS)
EXTRA_DIST = \
libsoup.supp \
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
new file mode 100644
index 00000000..211c94a1
--- /dev/null
+++ b/tests/ssl-test.c
@@ -0,0 +1,322 @@
+#include <gnutls/gnutls.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libsoup/soup-socket.h"
+#include "libsoup/soup-ssl.h"
+
+#define BUFSIZE 1024
+#define DH_BITS 1024
+
+GMainLoop *loop;
+gnutls_dh_params_t dh_params;
+
+/* SERVER */
+
+/* Read @bufsize bytes into @buf from @session. */
+static void
+server_read (gnutls_session_t session, char *buf, int bufsize)
+{
+ int total, nread;
+
+ total = 0;
+ while (total < bufsize) {
+ nread = gnutls_record_recv (session, buf + total,
+ bufsize - total);
+ if (nread <= 0)
+ g_error ("server read failed at position %d", total);
+ total += nread;
+ }
+}
+
+/* Write @bufsize bytes from @buf to @session, forcing 3 rehandshakes
+ * along the way. (We do an odd number of rehandshakes to make sure
+ * they occur at weird times relative to the client's read buffer
+ * size.)
+ */
+static void
+server_write (gnutls_session_t session, char *buf, int bufsize)
+{
+ int total, nwrote;
+ int next_rehandshake = bufsize / 3;
+
+ total = 0;
+ while (total < bufsize) {
+ if (total >= next_rehandshake) {
+ if (gnutls_rehandshake (session) < 0)
+ g_error ("client refused rehandshake at position %d", total);
+ if (gnutls_handshake (session) < 0)
+ g_error ("server rehandshake failed at position %d", total);
+ next_rehandshake = MIN (bufsize, next_rehandshake + bufsize / 3);
+ }
+
+ nwrote = gnutls_record_send (session, buf + total,
+ next_rehandshake - total);
+ if (nwrote <= 0)
+ g_error ("server write failed at position %d: %d", total, nwrote);
+ total += nwrote;
+ }
+}
+
+const char *ssl_cert_file = "test-cert.pem";
+const char *ssl_key_file = "test-key.pem";
+
+static gpointer
+server_thread (gpointer user_data)
+{
+ int listener = GPOINTER_TO_INT (user_data), client;
+ gnutls_certificate_credentials creds;
+ gnutls_session_t session;
+ struct sockaddr_in sin;
+ int len;
+ char buf[BUFSIZE];
+ int status;
+
+ gnutls_certificate_allocate_credentials (&creds);
+ if (gnutls_certificate_set_x509_key_file (creds,
+ ssl_cert_file, ssl_key_file,
+ GNUTLS_X509_FMT_PEM) != 0) {
+ g_error ("Failed to set SSL certificate and key files "
+ "(%s, %s).", ssl_cert_file, ssl_key_file);
+ }
+ gnutls_certificate_set_dh_params (creds, dh_params);
+
+ /* Create a new session */
+ gnutls_init (&session, GNUTLS_SERVER);
+ gnutls_set_default_priority (session);
+ gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, creds);
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ /* Wait for client thread to connect */
+ len = sizeof (sin);
+ client = accept (listener, (struct sockaddr *) &sin, (void *)&len);
+ gnutls_transport_set_ptr (session, GINT_TO_POINTER (client));
+
+ /* Initial handshake */
+ status = gnutls_handshake (session);
+ if (status < 0)
+ g_error ("initial handshake failed: %d", status);
+
+ /* Synchronous client test. */
+ server_read (session, buf, BUFSIZE);
+ server_write (session, buf, BUFSIZE);
+
+ /* Async client test. */
+ server_read (session, buf, BUFSIZE);
+ server_write (session, buf, BUFSIZE);
+
+ /* That's all, folks. */
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+ gnutls_deinit (session);
+ close (client);
+
+ return NULL;
+}
+
+/* async client code */
+
+typedef struct {
+ char writebuf[BUFSIZE], readbuf[BUFSIZE];
+ int total;
+} AsyncData;
+
+static void
+async_read (SoupSocket *sock, gpointer user_data)
+{
+ AsyncData *data = user_data;
+ SoupSocketIOStatus status;
+ gsize n;
+
+ do {
+ status = soup_socket_read (sock, data->readbuf + data->total,
+ BUFSIZE - data->total, &n);
+ if (status == SOUP_SOCKET_OK)
+ data->total += n;
+ } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE);
+
+ if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF)
+ g_error ("Async read got status %d", status);
+ else if (status == SOUP_SOCKET_WOULD_BLOCK)
+ return;
+
+ if (memcmp (data->writebuf, data->readbuf, BUFSIZE) != 0)
+ g_error ("Sync read didn't match write");
+
+ g_main_loop_quit (loop);
+}
+
+static void
+async_write (SoupSocket *sock, gpointer user_data)
+{
+ AsyncData *data = user_data;
+ SoupSocketIOStatus status;
+ gsize n;
+
+ do {
+ status = soup_socket_write (sock, data->writebuf + data->total,
+ BUFSIZE - data->total, &n);
+ if (status == SOUP_SOCKET_OK)
+ data->total += n;
+ } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE);
+
+ if (status == SOUP_SOCKET_ERROR || status == SOUP_SOCKET_EOF)
+ g_error ("Async write got status %d", status);
+ else if (status == SOUP_SOCKET_WOULD_BLOCK)
+ return;
+
+ data->total = 0;
+ async_read (sock, user_data);
+}
+
+static gboolean
+start_writing (gpointer user_data)
+{
+ SoupSocket *sock = user_data;
+ AsyncData *data;
+ int i;
+
+ data = g_new (AsyncData, 1);
+ for (i = 0; i < BUFSIZE; i++)
+ data->writebuf[i] = i & 0xFF;
+
+ g_signal_connect (sock, "writable",
+ G_CALLBACK (async_write), data);
+ g_signal_connect (sock, "readable",
+ G_CALLBACK (async_read), data);
+
+ async_write (sock, data);
+ return FALSE;
+}
+
+int debug;
+
+static void
+debug_log (int level, const char *str)
+{
+ fputs (str, stderr);
+}
+
+int
+main (int argc, char **argv)
+{
+ int opt, listener, sin_len, port, i;
+ struct sockaddr_in sin;
+ GThread *server;
+ char writebuf[BUFSIZE], readbuf[BUFSIZE];
+ SoupSocket *sock;
+ gsize n, total;
+ SoupSocketIOStatus status;
+
+ g_type_init ();
+ g_thread_init (NULL);
+
+ while ((opt = getopt (argc, argv, "c:d:k:")) != -1) {
+ switch (opt) {
+ case 'c':
+ ssl_cert_file = optarg;
+ break;
+ case 'd':
+ debug = atoi (optarg);
+ break;
+ case 'k':
+ ssl_key_file = optarg;
+ break;
+
+ case '?':
+ fprintf (stderr, "Usage: %s [-d debuglevel] [-c ssl-cert-file] [-k ssl-key-file]\n",
+ argv[0]);
+ break;
+ }
+ }
+
+ if (debug) {
+ gnutls_global_set_log_function (debug_log);
+ gnutls_global_set_log_level (debug);
+ }
+
+ /* Create server socket */
+ listener = socket (AF_INET, SOCK_STREAM, 0);
+ if (listener == -1) {
+ perror ("creating listening socket");
+ exit (1);
+ }
+
+ memset (&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind (listener, (struct sockaddr *) &sin, sizeof (sin)) == -1) {
+ perror ("binding listening socket");
+ exit (1);
+ }
+
+ if (listen (listener, 1) == -1) {
+ perror ("listening on socket");
+ exit (1);
+ }
+
+ sin_len = sizeof (sin);
+ getsockname (listener, (struct sockaddr *)&sin, (void *)&sin_len);
+ port = ntohs (sin.sin_port);
+
+ /* Now spawn server thread */
+ server = g_thread_create (server_thread, GINT_TO_POINTER (listener),
+ FALSE, NULL);
+
+ /* And create the client */
+ sock = soup_socket_client_new_sync ("127.0.0.1", port,
+ soup_ssl_get_client_credentials (NULL),
+ &status);
+ if (status != SOUP_STATUS_OK) {
+ g_error ("Could not create client socket: %s",
+ soup_status_get_phrase (status));
+ }
+
+ soup_socket_start_ssl (sock);
+
+ /* Synchronous client test */
+ for (i = 0; i < BUFSIZE; i++)
+ writebuf[i] = i & 0xFF;
+
+ total = 0;
+ while (total < BUFSIZE) {
+ status = soup_socket_write (sock, writebuf + total,
+ BUFSIZE - total, &n);
+ if (status != SOUP_SOCKET_OK)
+ g_error ("Sync write got status %d", status);
+ total += n;
+ }
+
+ total = 0;
+ while (total < BUFSIZE) {
+ status = soup_socket_read (sock, readbuf + total,
+ BUFSIZE - total, &n);
+ if (status != SOUP_SOCKET_OK)
+ g_error ("Sync read got status %d", status);
+ total += n;
+ }
+
+ if (memcmp (writebuf, readbuf, BUFSIZE) != 0)
+ g_error ("Sync read didn't match write");
+
+ printf ("SYNCHRONOUS SSL TEST PASSED\n");
+
+ /* Switch socket to async and do it again */
+
+ g_object_set (sock,
+ SOUP_SOCKET_FLAG_NONBLOCKING, TRUE,
+ NULL);
+
+ g_idle_add (start_writing, sock);
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+
+ printf ("ASYNCHRONOUS SSL TEST PASSED\n");
+
+ /* Success */
+ return 0;
+}