libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 3c7752bab9b90fbf939c95eddc36738d28454b62
parent f9578650704c2260173f5e4aac9c956a9e5a9d6a
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Mon, 30 Sep 2024 02:54:31 +0100

test_upgrade: ported test to the new API

Diffstat:
Mconfigure.ac | 1+
Msrc/tests/Makefile.am | 10+++++++++-
Asrc/tests/upgrade/Makefile.am | 40++++++++++++++++++++++++++++++++++++++++
Asrc/tests/upgrade/test_helpers.h | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tests/upgrade/test_upgrade.c | 2000+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tests/upgrade/tls_test_keys.h | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 2328 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac @@ -7177,6 +7177,7 @@ src/include/Makefile src/mhd2/Makefile src/tests/Makefile src/tests/basic/Makefile +src/tests/upgrade/Makefile src/tests/client_server/Makefile src/examples2/Makefile ]) diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am @@ -1,5 +1,13 @@ # This Makefile.am is in the public domain -SUBDIRS = basic client_server +SUBDIRS = basic + +if MHD_UPGRADE_SUPPORT +SUBDIRS += upgrade +endif + +if RUN_LIBCURL_TESTS +SUBDIRS += client_server +endif .NOTPARALLEL: diff --git a/src/tests/upgrade/Makefile.am b/src/tests/upgrade/Makefile.am @@ -0,0 +1,40 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/incl_priv \ + -I$(top_srcdir)/src/mhd2 \ + -DMHD_CPU_COUNT=$(CPU_COUNT) \ + $(CPPFLAGS_ac) $(MHD_TLS_LIB_CPPFLAGS) + +AM_CFLAGS = $(CFLAGS_ac) + +AM_LDFLAGS = $(LDFLAGS_ac) $(MHD_TLS_LIB_LDFLAGS) + +AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) + +if USE_COVERAGE + AM_CFLAGS += -fprofile-arcs -ftest-coverage +endif + +LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la + +$(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile + @echo ' cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la'; \ + $(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la + +noinst_HEADERS = \ + test_helpers.h + +check_PROGRAMS = \ + test_upgrade \ + test_upgrade_large \ + test_upgrade_vlarge \ + $(EMPTY_ITEM) + +test_upgrade_SOURCES = test_upgrade.c test_helpers.h tls_test_keys.h + +test_upgrade_large_SOURCES = $(test_upgrade_SOURCES) + +test_upgrade_vlarge_SOURCES = $(test_upgrade_SOURCES) + +TESTS = $(check_PROGRAMS) diff --git a/src/tests/upgrade/test_helpers.h b/src/tests/upgrade/test_helpers.h @@ -0,0 +1,95 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016-2024 Karlson2k (Evgeny Grin) + + GNU libmicrohttpd 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. + + GNU libmicrohttpd 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file test_helpers.h + * @brief Static functions and macros helpers for testsuite. + * @author Karlson2k (Evgeny Grin) + */ + +#include <string.h> + +/** + * Check whether program name contains specific @a marker string. + * Only last component in pathname is checked for marker presence, + * all leading directories names (if any) are ignored. Directories + * separators are handled correctly on both non-W32 and W32 + * platforms. + * @param prog_name program name, may include path + * @param marker marker to look for. + * @return zero if any parameter is NULL or empty string or + * @a prog_name ends with slash or @a marker is not found in + * program name, non-zero if @a maker is found in program + * name. + */ +static int +has_in_name (const char *prog_name, const char *marker) +{ + size_t name_pos; + size_t pos; + + if (! prog_name || ! marker || ! prog_name[0] || ! marker[0]) + return 0; + + pos = 0; + name_pos = 0; + while (prog_name[pos]) + { + if ('/' == prog_name[pos]) + name_pos = pos + 1; +#if defined(_WIN32) || defined(__CYGWIN__) + else if ('\\' == prog_name[pos]) + name_pos = pos + 1; +#endif /* _WIN32 || __CYGWIN__ */ + pos++; + } + if (name_pos == pos) + return 0; + return strstr (prog_name + name_pos, marker) != (char *) (void *) 0; +} + + +/** + * Check whether one of strings in array is equal to @a param. + * String @a argv[0] is ignored. + * @param argc number of strings in @a argv, as passed to main function + * @param argv array of strings, as passed to main function + * @param param parameter to look for. + * @return zero if @a argv is NULL, @a param is NULL or empty string, + * @a argc is less then 2 or @a param is not found in @a argv, + * non-zero if one of strings in @a argv is equal to @a param. + */ +static int +has_param (int argc, char *const argv[], const char *param) +{ + int i; + if (! argv || ! param || ! param[0]) + return 0; + + for (i = 1; i < argc; i++) + { + if (argv[i] && (strcmp (argv[i], param) == 0) ) + return ! 0; + } + + (void) has_in_name; /* Mute compiler warning */ + (void) has_param; /* Mute compiler warning */ + return 0; +} diff --git a/src/tests/upgrade/test_upgrade.c b/src/tests/upgrade/test_upgrade.c @@ -0,0 +1,2000 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2016-2020 Christian Grothoff + Copyright (C) 2016-2024 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd 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. + + GNU libmicrohttpd 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/tests/upgrade/test_upgrade.c + * @brief Testcase for libmicrohttpd upgrading a connection + * @author Christian Grothoff + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_STDBOOL_H +# include <stdbool.h> +#endif /* HAVE_STDBOOL_H */ + +#include "sys_sockets_headers.h" +#include "sys_sockets_types.h" +#include "sys_ip_headers.h" +#include "mhd_sockets_macros.h" +#include <fcntl.h> + +#include "microhttpd2.h" + +#include "test_helpers.h" + +#ifdef HTTPS_SUPPORT +# include <gnutls/gnutls.h> +# include "tls_test_keys.h" + +# if defined(HAVE_FORK) && defined(HAVE_WAITPID) +# include <sys/types.h> +# include <sys/wait.h> +# endif /* HAVE_FORK && HAVE_WAITPID */ +#endif /* HTTPS_SUPPORT */ + +#if defined(MHD_POSIX_SOCKETS) +# ifdef MHD_WINSOCK_SOCKETS +# error Both MHD_POSIX_SOCKETS and MHD_WINSOCK_SOCKETS are defined +# endif /* MHD_WINSOCK_SOCKETS */ +#elif ! defined(MHD_WINSOCK_SOCKETS) +# error Neither MHD_POSIX_SOCKETS nor MHD_WINSOCK_SOCKETS are defined +#endif /* MHD_WINSOCK_SOCKETS */ + + +#ifndef mhd_SSTR_LEN +/** + * Determine length of static string / macro strings at compile time. + */ +#define mhd_SSTR_LEN(macro) (sizeof(macro) / sizeof(char) - 1) +#endif /* ! mhd_SSTR_LEN */ + +#if ! defined(SHUT_WR) && defined(SD_SEND) +# define SHUT_WR SD_SEND +#endif + +#if ! defined(SHUT_RD) && defined(SD_RECEIVE) +# define SHUT_RD SD_RECEIVE +#endif + +#if ! defined(SHUT_RDWR) && defined(SD_BOTH) +# define SHUT_RDWR SD_BOTH +#endif + +#if defined(MHD_POSIX_SOCKETS) +# if defined(ENETUNREACH) +# define mhdt_SCKT_HARD_ERR ENETUNREACH +# elif defined(ENOTCONN) +# define mhdt_SCKT_HARD_ERR ENOTCONN +# elif defined(ECONNRESET) +# define mhdt_SCKT_HARD_ERR ECONNRESET +# elif defined(EPIPE) +# define mhdt_SCKT_HARD_ERR EPIPE +# elif defined(EBADF) +# define mhdt_SCKT_HARD_ERR EBADF +# else +# define mhdt_SCKT_HARD_ERR 99 /* Fallback, never used in practice */ +# endif +#else /* MHD_WINSOCK_SOCKETS */ +# define mhdt_SCKT_HARD_ERR WSAENETRESET +#endif + + +MHD_NORETURN_ static void +_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) +{ + fflush (stdout); + if ((NULL != errDesc) && (0 != errDesc[0])) + fprintf (stderr, "%s", errDesc); + else + fprintf (stderr, "System or external library call failed"); + if ((NULL != funcName) && (0 != funcName[0])) + fprintf (stderr, " in %s", funcName); + if (0 < lineNum) + fprintf (stderr, " at line %d", lineNum); + + fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, + strerror (errno)); +#ifdef MHD_WINSOCK_SOCKETS + fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); +#endif /* MHD_WINSOCK_SOCKETS */ + fflush (stderr); + exit (99); +} + + +MHD_NORETURN_ static void +_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) +{ + fflush (stdout); + if ((NULL != errDesc) && (0 != errDesc[0])) + fprintf (stderr, "%s", errDesc); + else + fprintf (stderr, "MHD unexpected error"); + if ((NULL != funcName) && (0 != funcName[0])) + fprintf (stderr, " in %s", funcName); + if (0 < lineNum) + fprintf (stderr, " at line %d", lineNum); + + fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, + strerror (errno)); + + fflush (stderr); + exit (8); +} + + +static void +_testErrorLog_func (const char *errDesc, const char *funcName, int lineNum) +{ + fflush (stdout); + if ((NULL != errDesc) && (0 != errDesc[0])) + fprintf (stderr, "%s", errDesc); + else + fprintf (stderr, "System or external library call resulted in error"); + if ((NULL != funcName) && (0 != funcName[0])) + fprintf (stderr, " in %s", funcName); + if (0 < lineNum) + fprintf (stderr, " at line %d", lineNum); + + fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, + strerror (errno)); +#ifdef MHD_WINSOCK_SOCKETS + fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); +#endif /* MHD_WINSOCK_SOCKETS */ + fflush (stderr); +} + + +#ifdef MHD_HAVE_MHD_FUNC_ +#define externalErrorExit(ignore) \ + _externalErrorExit_func (NULL, MHD_FUNC_, __LINE__) +#define externalErrorExitDesc(errDesc) \ + _externalErrorExit_func (errDesc, MHD_FUNC_, __LINE__) +#define mhdErrorExit(ignore) \ + _mhdErrorExit_func (NULL, MHD_FUNC_, __LINE__) +#define mhdErrorExitDesc(errDesc) \ + _mhdErrorExit_func (errDesc, MHD_FUNC_, __LINE__) +#define testErrorLog(ignore) \ + _testErrorLog_func (NULL, MHD_FUNC_, __LINE__) +#define testErrorLogDesc(errDesc) \ + _testErrorLog_func (errDesc, MHD_FUNC_, __LINE__) +#else /* ! MHD_HAVE_MHD_FUNC_ */ +#define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) +#define externalErrorExitDesc(errDesc) \ + _externalErrorExit_func (errDesc, NULL, __LINE__) +#define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) +#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) +#define testErrorLog(ignore) _testErrorLog_func (NULL, NULL, __LINE__) +#define testErrorLogDesc(errDesc) _testErrorLog_func (errDesc, NULL, __LINE__) +#endif /* ! MHD_HAVE_MHD_FUNC_ */ + +/* ** External parameters ** */ +static bool use_large; + +static bool use_vlarge; + +static bool test_tls; + +static int verbose = 0; + +enum tls_tool +{ + TLS_CLI_NO_TOOL = 0, + TLS_CLI_GNUTLS, + TLS_CLI_OPENSSL, + TLS_LIB_GNUTLS +}; + +static enum tls_tool use_tls_tool; + + +/* ** Internal values ** */ + +/* Could be increased to facilitate debugging */ +static int test_timeout = 5; + +static uint_least16_t global_port; + +static const void *rclient_msg; + +static size_t rclient_msg_size; + +static const void *app_msg; + +static size_t app_msg_size; + +static void *alloc_ptr[2] = {NULL, NULL}; + + +static void +fflush_allstd (void) +{ + fflush (stderr); + fflush (stdout); +} + + +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) +/** + * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to + * talk to our port over a socket in @a sp without having to worry + * about TLS. + * + * @param location where the socket is returned + * @return -1 on error, otherwise PID of TLS child process + */ +static pid_t +gnutlscli_connect (int *sock, + uint16_t port) +{ + pid_t chld; + int sp[2]; + char destination[30]; + + if (0 != socketpair (AF_UNIX, + SOCK_STREAM, + 0, + sp)) + { + testErrorLogDesc ("socketpair() failed"); + return (pid_t) -1; + } + chld = fork (); + if (0 != chld) + { + *sock = sp[1]; + mhd_socket_close (sp[0]); + return chld; + } + mhd_socket_close (sp[1]); + (void) close (0); + (void) close (1); + if (-1 == dup2 (sp[0], 0)) + externalErrorExitDesc ("dup2() failed"); + if (-1 == dup2 (sp[0], 1)) + externalErrorExitDesc ("dup2() failed"); + mhd_socket_close (sp[0]); + if (TLS_CLI_GNUTLS == use_tls_tool) + { + snprintf (destination, + sizeof(destination), + "%u", + (unsigned int) port); + execlp ("gnutls-cli", + "gnutls-cli", + "--insecure", + "-p", + destination, + "127.0.0.1", + (char *) NULL); + } + else if (TLS_CLI_OPENSSL == use_tls_tool) + { + snprintf (destination, + sizeof(destination), + "127.0.0.1:%u", + (unsigned int) port); + execlp ("openssl", + "openssl", + "s_client", + "-connect", + destination, + "-verify", + "1", + (char *) NULL); + } + _exit (1); +} + + +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + + +#if 0 /* Unused code */ +/** + * Change socket to blocking. + * + * @param fd the socket to manipulate + */ +static void +make_blocking (MHD_Socket fd) +{ +#if defined(MHD_POSIX_SOCKETS) + int flags; + + flags = fcntl (fd, F_GETFL); + if (-1 == flags) + externalErrorExitDesc ("fcntl() failed"); + if ((flags & ~O_NONBLOCK) != flags) + if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) + externalErrorExitDesc ("fcntl() failed"); +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long flags = 0; + + if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) + externalErrorExitDesc ("ioctlsocket() failed"); +#endif /* MHD_WINSOCK_SOCKETS */ +} + + +#endif /* Unused code */ + + +/** + * Change socket to non-blocking. + * + * @param fd the socket to manipulate + */ +static void +make_nonblocking (MHD_Socket fd) +{ +#if defined(MHD_POSIX_SOCKETS) + int flags; + + flags = fcntl (fd, F_GETFL); + if (-1 == flags) + externalErrorExitDesc ("fcntl() failed"); + if (O_NONBLOCK != (flags & O_NONBLOCK)) + if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK)) + externalErrorExitDesc ("fcntl() failed"); +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long flags = 1; + + if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) + externalErrorExitDesc ("ioctlsocket() failed"); +#endif /* MHD_WINSOCK_SOCKETS */ +} + + +/** + * Enable TCP_NODELAY on TCP/IP socket. + * + * @param fd the socket to manipulate + */ +static void +make_nodelay (MHD_Socket fd) +{ +#ifdef TCP_NODELAY + const mhd_SCKT_OPT_BOOL on_val = 1; + + if (0 == setsockopt (fd, + IPPROTO_TCP, + TCP_NODELAY, + (const void *) &on_val, + sizeof (on_val))) + return; /* Success exit point */ + +#ifndef MHD_WINSOCK_SOCKETS + fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). " + "errno: %d (%s)\n", (int) errno, strerror (errno)); +#else /* MHD_WINSOCK_SOCKETS */ + fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). " + "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); +#endif /* MHD_WINSOCK_SOCKETS */ + fflush (stderr); +#endif /* TCP_NODELAY */ +} + + +/** + * Wrapper structure for plain&TLS sockets + */ +struct wr_socket +{ + /** + * Real network socket + */ + MHD_Socket fd; + + /** + * Type of this socket + */ + enum wr_type + { + wr_invalid = 0, + wr_plain = 1, + wr_tls = 2 + } t; + + bool is_nonblocking; + + bool eof_recieved; +#ifdef HTTPS_SUPPORT + /** + * TLS credentials + */ + gnutls_certificate_credentials_t tls_crd; + + /** + * TLS session. + */ + gnutls_session_t tls_s; + + /** + * TLS handshake already succeed? + */ + bool tls_connected; +#endif +}; + + +/** + * Get underlying real socket. + * @return FD of real socket + */ +#define wr_fd(s) ((s)->fd) + + +#if 0 /* Unused code */ +static void +wr_make_blocking (struct wr_socket *s) +{ + if (s->is_nonblocking) + make_blocking (s->fd); + s->is_nonblocking = false; +} + + +#endif /* Unused code */ + + +static void +wr_make_nonblocking (struct wr_socket *s) +{ + if (! s->is_nonblocking) + make_nonblocking (s->fd); + s->is_nonblocking = true; +} + + +/** + * Create wr_socket with plain TCP underlying socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_plain_sckt (void) +{ + struct wr_socket *s = malloc (sizeof(struct wr_socket)); + if (NULL == s) + { + testErrorLogDesc ("malloc() failed"); + return NULL; + } + s->t = wr_plain; + s->eof_recieved = false; + s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + s->is_nonblocking = false; + if (MHD_INVALID_SOCKET != s->fd) + { + make_nodelay (s->fd); + return s; /* Success */ + } + testErrorLogDesc ("socket() failed"); + free (s); + return NULL; +} + + +/** + * Create wr_socket with TLS TCP underlying socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_tls_sckt (void) +{ +#ifdef HTTPS_SUPPORT + struct wr_socket *s = malloc (sizeof(struct wr_socket)); + if (NULL == s) + { + testErrorLogDesc ("malloc() failed"); + return NULL; + } + s->t = wr_tls; + s->eof_recieved = false; + s->tls_connected = 0; + s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + s->is_nonblocking = false; + if (MHD_INVALID_SOCKET != s->fd) + { + make_nodelay (s->fd); + if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT)) + { + if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s)) + { + if (GNUTLS_E_SUCCESS == + gnutls_certificate_allocate_credentials (&(s->tls_crd))) + { + if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, + GNUTLS_CRD_CERTIFICATE, + s->tls_crd)) + { +#if (GNUTLS_VERSION_NUMBER + 0 >= 0x030109) && ! defined(_WIN64) + gnutls_transport_set_int (s->tls_s, (int) (s->fd)); +#else /* GnuTLS before 3.1.9 or Win x64 */ + gnutls_transport_set_ptr (s->tls_s, + (gnutls_transport_ptr_t) \ + (intptr_t) (s->fd)); +#endif /* GnuTLS before 3.1.9 or Win x64 */ + return s; + } + else + testErrorLogDesc ("gnutls_credentials_set() failed"); + gnutls_certificate_free_credentials (s->tls_crd); + } + else + testErrorLogDesc ("gnutls_certificate_allocate_credentials() failed"); + } + else + testErrorLogDesc ("gnutls_set_default_priority() failed"); + gnutls_deinit (s->tls_s); + } + else + testErrorLogDesc ("gnutls_init() failed"); + (void) mhd_socket_close (s->fd); + } + else + testErrorLogDesc ("socket() failed"); + free (s); +#endif /* HTTPS_SUPPORT */ + return NULL; +} + + +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + +/** + * Create wr_socket with plain TCP underlying socket + * from already created TCP socket. + * @param plain_sk real TCP socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_from_plain_sckt (MHD_Socket plain_sk) +{ + struct wr_socket *s = malloc (sizeof(struct wr_socket)); + + if (NULL == s) + { + testErrorLogDesc ("malloc() failed"); + return NULL; + } + s->t = wr_plain; + s->eof_recieved = false; + s->fd = plain_sk; + s->is_nonblocking = false; /* The actual mode is unknown */ + wr_make_nonblocking (s); /* Force set mode to have correct status */ + make_nodelay (s->fd); + return s; +} + + +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + +#if 0 /* Disabled code */ +/** + * Check whether shutdown of connection was received from remote + * @param s socket to check + * @return zero if shutdown signal has not been received, + * 1 if shutdown signal was already received + */ +static int +wr_is_eof_received (struct wr_socket *s) +{ + return s->eof_recieved ? 1 : 0; +} + + +#endif /* Disabled code */ + + +enum wr_wait_for_type +{ + WR_WAIT_FOR_RECV = 0, + WR_WAIT_FOR_SEND = 1 +}; + +static bool +wr_wait_socket_ready_noabort_ (struct wr_socket *s, + int timeout_ms, + enum wr_wait_for_type wait_for) +{ + fd_set fds; + int sel_res; + struct timeval tmo; + struct timeval *tmo_ptr; + +#ifndef MHD_WINSOCK_SOCKETS + if (FD_SETSIZE <= s->fd) + externalErrorExitDesc ("Too large FD value"); +#endif /* ! MHD_WINSOCK_SOCKETS */ + FD_ZERO (&fds); + FD_SET (s->fd, &fds); + if (0 <= timeout_ms) + { +#if ! defined(_WIN32) || defined(__CYGWIN__) + tmo.tv_sec = (time_t) (timeout_ms / 1000); +#else /* Native W32 */ + tmo.tv_sec = (long) (timeout_ms / 1000); +#endif /* Native W32 */ + tmo.tv_usec = ((long) (timeout_ms % 1000)) * 1000; + tmo_ptr = &tmo; + } + else + tmo_ptr = NULL; /* No timeout */ + + do + { + if (WR_WAIT_FOR_RECV == wait_for) + sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr); + else + sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr); + } while (0 > sel_res && mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ())); + + if (1 == sel_res) + return true; + + if (0 == sel_res) + fprintf (stderr, "Timeout"); + else + { +#ifndef MHD_WINSOCK_SOCKETS + fprintf (stderr, "Error %d (%s)", (int) errno, strerror (errno)); +#else /* MHD_WINSOCK_SOCKETS */ + fprintf (stderr, "Error (WSAGetLastError code: %d)", + (int) WSAGetLastError ()); +#endif /* MHD_WINSOCK_SOCKETS */ + } + fprintf (stderr, " waiting for socket to be available for %s.\n", + (WR_WAIT_FOR_RECV == wait_for) ? "receiving" : "sending"); + return false; +} + + +static void +wr_wait_socket_ready_ (struct wr_socket *s, + int timeout_ms, + enum wr_wait_for_type wait_for) +{ + if (wr_wait_socket_ready_noabort_ (s, timeout_ms, wait_for)) + return; + + if (WR_WAIT_FOR_RECV == wait_for) + mhdErrorExitDesc ("Client failed to receive the data"); + else + mhdErrorExitDesc ("Client failed to send the data"); +} + + +/** + * Connect socket to specified address. + * @param s socket to use + * @param addr address to connect + * @param length of structure pointed by @a addr + * @param timeout_ms the maximum wait time in milliseconds to send the data, + * no limit if negative value is used + * @return zero on success, -1 otherwise. + */ +static int +wr_connect_tmo (struct wr_socket *s, + const struct sockaddr *addr, + unsigned int length, + int timeout_ms) +{ + if (0 != connect (s->fd, addr, (socklen_t) length)) + { + int err; + bool connect_completed = false; + + err = mhd_SCKT_GET_LERR (); +#if defined(MHD_POSIX_SOCKETS) + while (! connect_completed && (EINTR == err)) + { + connect_completed = (0 == connect (s->fd, addr, (socklen_t) length)); + if (! connect_completed) + { + err = errno; + if (EALREADY == err) + err = EINPROGRESS; + else if (EISCONN == err) + connect_completed = true; + } + } +#endif /* MHD_POSIX_SOCKETS */ + if (! connect_completed && + (mhd_SCKT_ERR_IS_INPROGRESS (err) + || mhd_SCKT_ERR_IS_EAGAIN (err))) /* No modern system uses EAGAIN, except W32 */ + connect_completed = + wr_wait_socket_ready_noabort_ (s, timeout_ms, WR_WAIT_FOR_SEND); + if (! connect_completed) + { + testErrorLogDesc ("connect() failed"); + return -1; + } + } + if (wr_plain == s->t) + return 0; +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + /* Do not try handshake here as + * it requires processing on MHD side and + * when testing with "external" polling, + * test will call MHD processing only + * after return from wr_connect(). */ + s->tls_connected = 0; + return 0; + } +#endif /* HTTPS_SUPPORT */ + testErrorLogDesc ("HTTPS socket connect called, but code does not support" \ + " HTTPS sockets"); + return -1; +} + + +/** + * Connect socket to specified address. + * @param s socket to use + * @param addr address to connect + * @param length of structure pointed by @a addr + * @return zero on success, -1 otherwise. + */ +static int +wr_connect (struct wr_socket *s, + const struct sockaddr *addr, + unsigned int length) +{ + return wr_connect_tmo (s, addr, length, test_timeout * 1000); +} + + +#ifdef HTTPS_SUPPORT +/* Only to be called from wr_send() and wr_recv() ! */ +static bool +wr_handshake_tmo_ (struct wr_socket *s, + int timeout_ms) +{ + int res = gnutls_handshake (s->tls_s); + + while ((GNUTLS_E_AGAIN == res) || (GNUTLS_E_INTERRUPTED == res)) + { + wr_wait_socket_ready_ (s, timeout_ms, + gnutls_record_get_direction (s->tls_s) ? + WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV); + res = gnutls_handshake (s->tls_s); + } + if (GNUTLS_E_SUCCESS == res) + s->tls_connected = true; + else + { + fprintf (stderr, "The error returned by gnutls_handshake() is " + "'%s' ", gnutls_strerror ((int) res)); +#if GNUTLS_VERSION_NUMBER >= 0x020600 + fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) res)); +#else /* GNUTLS_VERSION_NUMBER < 0x020600 */ + fprintf (stderr, "(%d)\n", (int) res); +#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */ + testErrorLogDesc ("gnutls_handshake() failed with hard error"); + mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */ + } + return s->tls_connected; +} + + +#if 0 /* Unused function */ +/* Only to be called from wr_send() and wr_recv() ! */ +static bool +wr_handshake_ (struct wr_socket *s) +{ + return wr_handshake_tmo_ (s, test_timeout * 1000); +} + + +#endif /* Unused function */ + +#endif /* HTTPS_SUPPORT */ + + +/** + * Send data to remote by socket. + * @param s the socket to use + * @param buf the buffer with data to send + * @param len the length of data in @a buf + * @param timeout_ms the maximum wait time in milliseconds to send the data, + * no limit if negative value is used + * @return number of bytes were sent if succeed, + * -1 if failed. Use #mhd_SCKT_GET_LERR() + * to get socket error. + */ +static ssize_t +wr_send_tmo (struct wr_socket *s, + const void *buf, + size_t len, + int timeout_ms) +{ + if (wr_plain == s->t) + { + ssize_t res; + while (! 0) + { + int err; + res = mhd_sys_send (s->fd, buf, len); + if (0 <= res) + break; /* Success */ + err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (err) && ! mhd_SCKT_ERR_IS_EINTR (err)) + break; /* Failure */ + wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND); + } + return res; + } +#ifdef HTTPS_SUPPORT + else if (wr_tls == s->t) + { + ssize_t ret; + if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms)) + return -1; + + while (1) + { + ret = gnutls_record_send (s->tls_s, buf, len); + if (ret >= 0) + return ret; + if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) + break; + wr_wait_socket_ready_ (s, timeout_ms, + gnutls_record_get_direction (s->tls_s) ? + WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV); + } + fprintf (stderr, "The error returned by gnutls_record_send() is " + "'%s' ", gnutls_strerror ((int) ret)); +#if GNUTLS_VERSION_NUMBER >= 0x020600 + fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret)); +#else /* GNUTLS_VERSION_NUMBER < 0x020600 */ + fprintf (stderr, "(%d)\n", (int) ret); +#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */ + testErrorLogDesc ("gnutls_record_send() failed with hard error"); + mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */ + return -1; + } +#endif /* HTTPS_SUPPORT */ + testErrorLogDesc ("HTTPS socket send called, but code does not support" \ + " HTTPS sockets"); + return -1; +} + + +/** + * Send data to remote by socket. + * @param s the socket to use + * @param buf the buffer with data to send + * @param len the length of data in @a buf + * @return number of bytes were sent if succeed, + * -1 if failed. Use #mhd_SCKT_GET_LERR() + * to get socket error. + */ +static ssize_t +wr_send (struct wr_socket *s, + const void *buf, + size_t len) +{ + return wr_send_tmo (s, buf, len, test_timeout * 1000); +} + + +/** + * Receive data from remote by socket. + * @param s the socket to use + * @param buf the buffer to store received data + * @param len the length of @a buf + * @param timeout_ms the maximum wait time in milliseconds to receive the data, + * no limit if negative value is used + * @return number of bytes were received if succeed, + * -1 if failed. Use #mhd_SCKT_GET_LERR() + * to get socket error. + */ +static ssize_t +wr_recv_tmo (struct wr_socket *s, + void *buf, + size_t len, + int timeout_ms) +{ + if (wr_plain == s->t) + { + ssize_t res; + while (! 0) + { + int err; + res = mhd_sys_recv (s->fd, buf, len); + if (0 == res) + s->eof_recieved = true; + if (0 <= res) + break; /* Success */ + err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (err) && ! mhd_SCKT_ERR_IS_EINTR (err)) + break; /* Failure */ + wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV); + } + return res; + } +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + ssize_t ret; + if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms)) + return -1; + + while (1) + { + ret = gnutls_record_recv (s->tls_s, buf, len); + if (0 == ret) + s->eof_recieved = true; + if (ret >= 0) + return ret; + if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) + break; + wr_wait_socket_ready_ (s, timeout_ms, + gnutls_record_get_direction (s->tls_s) ? + WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV); + } + + fprintf (stderr, "The error returned by gnutls_record_recv() is " + "'%s' ", gnutls_strerror ((int) ret)); +#if GNUTLS_VERSION_NUMBER >= 0x020600 + fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret)); +#else /* GNUTLS_VERSION_NUMBER < 0x020600 */ + fprintf (stderr, "(%d)\n", (int) ret); +#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */ + testErrorLogDesc ("gnutls_record_recv() failed with hard error"); + mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */ + return -1; + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + + +/** + * Receive data from remote by socket. + * @param s the socket to use + * @param buf the buffer to store received data + * @param len the length of @a buf + * @return number of bytes were received if succeed, + * -1 if failed. Use #mhd_SCKT_GET_LERR() + * to get socket error. + */ +static ssize_t +wr_recv (struct wr_socket *s, + void *buf, + size_t len) +{ + return wr_recv_tmo (s, buf, len, test_timeout * 1000); +} + + +/** + * Shutdown send/write on the socket. + * @param s the socket to shutdown + * @param how the type of shutdown: SHUT_WR or SHUT_RDWR + * @param timeout_ms the maximum wait time in milliseconds to receive the data, + * no limit if negative value is used + * @return zero on succeed, -1 otherwise + */ +static int +wr_shutdown_tmo (struct wr_socket *s, int how, int timeout_ms) +{ + switch (how) + { + case SHUT_WR: /* Valid value */ + break; + case SHUT_RDWR: /* Valid value */ + break; + case SHUT_RD: + externalErrorExitDesc ("Unsupported 'how' value"); + break; + default: + externalErrorExitDesc ("Invalid 'how' value"); + break; + } + if (wr_plain == s->t) + { + (void) timeout_ms; /* Unused parameter for plain sockets */ + return shutdown (s->fd, how); + } +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + ssize_t ret; + if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms)) + return -1; + + while (1) + { + ret = + gnutls_bye (s->tls_s, + (SHUT_WR == how) ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR); + if (GNUTLS_E_SUCCESS == ret) + { +#if 0 /* Disabled to test pure behaviour */ + if (SHUT_RDWR == how) + (void) shutdown (s->fd, how); /* Also shutdown the underlying transport layer */ +#endif + return 0; + } + if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret)) + break; + wr_wait_socket_ready_ (s, timeout_ms, + gnutls_record_get_direction (s->tls_s) ? + WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV); + } + + fprintf (stderr, "The error returned by gnutls_bye() is " + "'%s' ", gnutls_strerror ((int) ret)); +#if GNUTLS_VERSION_NUMBER >= 0x020600 + fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret)); +#else /* GNUTLS_VERSION_NUMBER < 0x020600 */ + fprintf (stderr, "(%d)\n", (int) ret); +#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */ + testErrorLogDesc ("gnutls_bye() failed with hard error"); + mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */ + return -1; + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + + +/** + * Shutdown the socket. + * @param s the socket to shutdown + * @return zero on succeed, -1 otherwise + */ +static int +wr_shutdown (struct wr_socket *s, int how) +{ + return wr_shutdown_tmo (s, how, test_timeout * 1000); +} + + +/** + * Close socket and release allocated resourced + * @param s the socket to close + * @return zero on succeed, -1 otherwise + */ +static int +wr_close (struct wr_socket *s) +{ + int ret = (mhd_socket_close (s->fd)) ? 0 : -1; +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + gnutls_deinit (s->tls_s); + gnutls_certificate_free_credentials (s->tls_crd); + } +#endif /* HTTPS_SUPPORT */ + free (s); + return ret; +} + + +/** + * String used to identify the test pseudo-protocol + */ +#define mhdt_UPGRADE_PROTOCOL_STR "MHDT_upgrade_test/2.0" + +/** + * Thread we use to run the interaction with the upgraded socket. + */ +static pthread_t pt_server; + +/** + * Thread we use to run the interaction with the upgraded socket. + */ +static pthread_t pt_client; + +/** + * Flag set to true once the client is finished. + */ +static volatile bool client_done; + +/** + * Flag set to true once the app is finished. + */ +static volatile bool app_done; + + +static void +send_all (struct wr_socket *sock, + const void *data, + size_t data_size) +{ + ssize_t ret; + size_t sent; + const uint8_t *const buf = (const uint8_t *) data; + + wr_make_nonblocking (sock); + for (sent = 0; sent < data_size; sent += (size_t) ret) + { + ret = wr_send (sock, + buf + sent, + data_size - sent); + if (0 > ret) + { + if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) || + mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ())) + { + ret = 0; + continue; + } + externalErrorExitDesc ("send() failed"); + } + } +} + + +#define send_all_stext(sk,st) send_all (sk,st,mhd_SSTR_LEN (st)) + + +/** + * Read character-by-character until we + * get 'CRLNCRLN'. + */ +static void +recv_hdr (struct wr_socket *sock) +{ + unsigned int i; + char next; + char c; + ssize_t ret; + + wr_make_nonblocking (sock); + next = '\r'; + i = 0; + while (i < 4) + { + ret = wr_recv (sock, + &c, + 1); + if (0 > ret) + { + if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ())) + continue; + if (mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ())) + continue; + externalErrorExitDesc ("recv() failed"); + } + if (0 == ret) + mhdErrorExitDesc ("The server unexpectedly closed connection"); + if (c == next) + { + i++; + if (next == '\r') + next = '\n'; + else + next = '\r'; + continue; + } + if (c == '\r') + { + i = 1; + next = '\n'; + continue; + } + i = 0; + next = '\r'; + } +} + + +static void +recv_all (struct wr_socket *sock, + const void *data, + size_t data_size) +{ + uint8_t *buf; + ssize_t ret; + size_t rcvd; + + buf = (uint8_t *) malloc (data_size); + if (NULL == buf) + externalErrorExitDesc ("malloc() failed"); + + wr_make_nonblocking (sock); + for (rcvd = 0; rcvd < data_size; rcvd += (size_t) ret) + { + ret = wr_recv (sock, + buf + rcvd, + data_size - rcvd); + if (0 > ret) + { + if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) || + mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ())) + { + ret = 0; + continue; + } + externalErrorExitDesc ("recv() failed"); + } + else if (0 == ret) + { + fprintf (stderr, "Partial only received text. Expected: '%.*s' " + "(length: %ud). Got: '%.*s' (length: %ud). ", + (int) data_size, (const char *) data, (unsigned int) data_size, + (int) rcvd, (const char *) buf, (unsigned int) rcvd); + mhdErrorExitDesc ("The server unexpectedly closed connection"); + } + if ((data_size - rcvd) < (size_t) ret) + externalErrorExitDesc ("recv() returned excessive amount of data"); + if (0 != memcmp (data, buf, rcvd + (size_t) ret)) + { + fprintf (stderr, "Wrong received text. Expected: '%.*s'. " + "Got: '%.*s'. ", + (int) (rcvd + (size_t) ret), (const char *) data, + (int) (rcvd + (size_t) ret), (const char *) buf); + mhdErrorExit (); + } + } + if (0 != memcmp (data, buf, data_size)) + { + fprintf (stderr, "Wrong received text. Expected: '%.*s'. " + "Got: '%.*s'. ", + (int) data_size, (const char *) data, + (int) data_size, (const char *) buf); + mhdErrorExit (); + } + free (buf); +} + + +#define recv_all_stext(sk,st) recv_all (sk,st,mhd_SSTR_LEN (st)) + + +/** + * Shutdown write of the connection to signal end of transmission + * for the remote side + * @param sock the socket to shutdown + */ +static void +send_eof (struct wr_socket *sock) +{ + if (0 != wr_shutdown (sock, /* + ** On Darwin local shutdown of RD cause error + ** if remote side shut down WR before. + wr_is_eof_received (sock) ? SHUT_RDWR : */ + SHUT_WR)) + externalErrorExitDesc ("Failed to shutdown connection"); +} + + +#if 0 /* Unused code */ + +/** + * Receive end of the transmission indication from the remote side + * @param sock the socket to use + */ +static void +receive_eof (struct wr_socket *sock) +{ + uint8_t buf[127]; + ssize_t ret; + size_t rcvd; + bool got_eof = false; + + wr_make_nonblocking (sock); + for (rcvd = 0; rcvd < sizeof(buf); rcvd += (size_t) ret) + { + ret = wr_recv (sock, + buf + rcvd, + sizeof(buf) - rcvd); + if (0 > ret) + { + if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) || + mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ())) + { + ret = 0; + continue; + } + externalErrorExitDesc ("recv() failed"); + } + else if (0 == ret) + { + got_eof = true; + break; + } + } + if (got_eof && (0 == rcvd)) + return; /* Success */ + + if (0 != rcvd) + { + if (sizeof(buf) == rcvd) + { + fprintf (stderr, "Received at least %lu extra bytes while " + "end-of-file is expected.\n", (unsigned long) sizeof(buf)); + mhdErrorExit (); + } + fprintf (stderr, "Received at %lu extra bytes and then %s" + "end-of-file marker.\n", (unsigned long) rcvd, + got_eof ? "" : "NO "); + mhdErrorExit (); + } + if (! got_eof) + mhdErrorExitDesc ("Failed to receive end-of-file marker."); +} + + +#endif /* Unused code */ + +static void +recv_upg_all (struct MHD_UpgradedHandle *urh, + const void *data, + size_t data_size) +{ + uint8_t *buf; + size_t last_rcvd; + size_t rcvd; + + buf = (uint8_t *) malloc (data_size); + if (NULL == buf) + externalErrorExitDesc ("malloc() failed"); + + for (rcvd = 0; rcvd < data_size; rcvd += last_rcvd) + { + if (MHD_SC_OK != + MHD_upgraded_recv (urh, + data_size - rcvd, + buf + rcvd, + &last_rcvd, + 1000 * (unsigned long) test_timeout)) + mhdErrorExitDesc ("MHD_upgraded_recv() failed"); + + if (0 == last_rcvd) + { + fprintf (stderr, "Partial only received text. Expected: '%.*s' " + "(length: %ud). Got: '%.*s' (length: %ud). ", + (int) data_size, (const char *) data, (unsigned int) data_size, + (int) rcvd, (const char *) buf, (unsigned int) rcvd); + mhdErrorExitDesc ("The server unexpectedly closed connection"); + } + if ((data_size - rcvd) < last_rcvd) + externalErrorExitDesc ("MHD_upgraded_recv() returned excessive " \ + "amount of data"); + if (0 != memcmp (data, buf, rcvd + (size_t) last_rcvd)) + { + fprintf (stderr, "Wrong received text. Expected: '%.*s'. " + "Got: '%.*s'. ", + (int) (rcvd + last_rcvd), (const char *) data, + (int) (rcvd + last_rcvd), (const char *) buf); + mhdErrorExit (); + } + } + if (0 != memcmp (data, buf, data_size)) + { + fprintf (stderr, "Wrong received text. Expected: '%.*s'. " + "Got: '%.*s'. ", + (int) data_size, (const char *) data, + (int) data_size, (const char *) buf); + mhdErrorExit (); + } + free (buf); +} + + +#define recv_upg_all_stext(uh,st) recv_upg_all (uh,st,mhd_SSTR_LEN (st)) + + +static void +send_upg_all (struct MHD_UpgradedHandle *urh, + const void *data, + size_t data_size) +{ + size_t sent_size; + + if (MHD_SC_OK != + MHD_upgraded_send (urh, + data_size, + data, + &sent_size, + 1000 * (unsigned long) test_timeout, + MHD_NO)) + mhdErrorExitDesc ("MHD_upgraded_send() failed"); + + if (sent_size != data_size) + mhdErrorExitDesc ("'sent_size' value is wrong"); +} + + +/** + * Receive end of the transmission indication from the remote side + * @param urh the "upgraded" handle to use + */ +static void +receive_upg_eof (struct MHD_UpgradedHandle *urh) +{ + size_t rcvd_sise; + uint8_t buf[1]; + + if (MHD_SC_OK != + MHD_upgraded_recv (urh, + sizeof(buf), + buf, + &rcvd_sise, + 1000 * (unsigned long) test_timeout)) + mhdErrorExitDesc ("MHD_upgraded_recv() failed"); + + if (0 != rcvd_sise) + mhdErrorExitDesc ("EOF marker is not received"); + +} + + +/** + * Main function for the thread that runs the interaction with + * the upgraded socket. + * + * @param cls the handle for the upgrade + */ +static void * +run_usock_server (void *cls) +{ + struct MHD_UpgradedHandle *urh = cls; + + recv_upg_all (urh, + rclient_msg, + rclient_msg_size); + send_upg_all (urh, + app_msg, + app_msg_size); + recv_upg_all_stext (urh, \ + "Finished"); + if (! test_tls) + { + receive_upg_eof (urh); + } + if (MHD_SC_OK != + MHD_upgraded_close (urh)) + mhdErrorExitDesc ("MHD_upgraded_close() failed"); + + app_done = true; + return NULL; +} + + +/** + * Main function for the thread that runs the client-side of the + * interaction with the upgraded socket. + * + * @param cls the client socket + */ +static void * +run_usock_client (void *cls) +{ + struct wr_socket *sock = cls; + + send_all_stext (sock, + "GET / HTTP/1.1\r\n" \ + "Host: localhost\r\n" \ + "Connection: Upgrade\r\n" \ + "Upgrade: " mhdt_UPGRADE_PROTOCOL_STR "\r\n" \ + "\r\n"); + recv_hdr (sock); + send_all (sock, + rclient_msg, + rclient_msg_size); + recv_all (sock, + app_msg, + app_msg_size); + send_all_stext (sock, + "Finished"); + if (! test_tls) + { + send_eof (sock); + } + wr_close (sock); + client_done = true; + return NULL; +} + + +/** + * Function called after a protocol "upgrade" response was sent successfully + * and the connection is being switched to other protocol. + * + * The newly provided handle @a urh can be used to send and receive the data + * by #MHD_upgraded_send() and #MHD_upgraded_recv(). The handle must be closed + * by #MHD_upgraded_close() before destroying the daemon. + * + * "Upgraded" connection will not time out, but still counted for daemon + * global connections limit and for per-IP limit (if set). + * + * Except when in 'thread-per-connection' mode, implementations + * of this function should never block (as it will still be called + * from within the main event loop). + * + * @param cls closure, whatever was given to #MHD_action_upgrade(). + * @param request original HTTP request handle, + * giving the function a last chance + * to inspect the original HTTP request + * @param urh argument for #MHD_upgrade_operation() on this @a response. + * Applications must eventually use this callback to (indirectly) + * perform the close() action on the @a sock. + */ +static void +upgrade_cb (void *cls, + struct MHD_Request *MHD_RESTRICT request, + struct MHD_UpgradedHandle *MHD_RESTRICT urh) +{ + if (NULL != cls) + mhdErrorExitDesc ("'cls' is not NULL"); + if (NULL == request) + mhdErrorExitDesc ("'request' is NULL"); + + if (0 != pthread_create (&pt_server, + NULL, + &run_usock_server, + urh)) + externalErrorExitDesc ("pthread_create() failed"); +} + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). + * If @a upload_size is not zero and response action is provided by this + * callback, then upload will be discarded and the stream (the connection for + * HTTP/1.1) will be closed after sending the response. + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param request the request object + * @param path the requested uri (without arguments after "?") + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param upload_size the size of the message upload content payload, + * #MHD_SIZE_UNKNOWN for chunked uploads (if the + * final chunk has not been processed yet) + * @return action how to proceed, NULL + * if the request must be aborted due to a serious + * error while handling the request (implies closure + * of underling data stream, for HTTP/1.1 it means + * socket closure). + */ +static const struct MHD_Action * +req_handle_upgrade (void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size) +{ + const struct MHD_Action *act; + if (NULL != cls) + mhdErrorExitDesc ("'cls' is not NULL"); + if (NULL == request) + mhdErrorExitDesc ("'request' is NULL"); + if (NULL == path) + mhdErrorExitDesc ("'path' is NULL"); + if (1 != path->len) + mhdErrorExitDesc ("'path->len' is not 1"); + if (0 != memcmp ("/", path->cstr, 1)) + mhdErrorExitDesc ("'path->cstr' is not \"/\""); + if (0 != path->cstr[path->len]) + mhdErrorExitDesc ("'path->cstr' is not zero-terminated"); + if (MHD_HTTP_METHOD_GET != method) + mhdErrorExitDesc ("'method' is not MHD_HTTP_METHOD_GET"); + if (0 != upload_size) + mhdErrorExitDesc ("'upload_size' is not zero"); + + act = MHD_action_upgrade (request, + mhdt_UPGRADE_PROTOCOL_STR, + &upgrade_cb, + NULL, + 0, + NULL); + if (NULL == act) + mhdErrorExitDesc ("MHD_action_upgrade() failed"); + + return act; +} + + +/** + * Test upgrading a connection. + * @return zero if succeed + */ +static unsigned int +test_upgrade (void) +{ + struct MHD_Daemon *d = NULL; + struct wr_socket *sock; + struct sockaddr_in sa; + union MHD_DaemonInfoFixedData dinfo; + +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + pid_t pid = -1; +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + + client_done = false; + app_done = false; + + d = MHD_daemon_create (&req_handle_upgrade, + NULL); + if (NULL == d) + mhdErrorExitDesc ("MHD_daemon_create() failed"); + + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS ( \ + d, \ + MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v6_OPTIONAL, global_port), \ + MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (1)))) + mhdErrorExitDesc ("MHD_DAEMON_SET_OPTIONS() failed"); + + if (MHD_SC_OK != + MHD_daemon_start (d)) + mhdErrorExitDesc ("MHD_daemon_start() failed"); + + if (MHD_SC_OK != + MHD_daemon_get_info_fixed (d, \ + MHD_DAEMON_INFO_FIXED_BIND_PORT, \ + &dinfo)) + mhdErrorExitDesc ("MHD_daemon_get_info_fixed() failed"); + + if (0 == dinfo.v_port) + mhdErrorExitDesc ("MHD_daemon_get_info_fixed() returned wrong data"); + global_port = dinfo.v_port; /* Re-use the same port for the next checks */ + if (! test_tls || (TLS_LIB_GNUTLS == use_tls_tool)) + { + sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); + if (NULL == sock) + externalErrorExitDesc ("Create socket failed"); + wr_make_nonblocking (sock); + sa.sin_family = AF_INET; + sa.sin_port = htons (global_port); +#ifdef INADDR_LOOPBACK + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); +#else /* ! INADDR_LOOPBACK */ + memcpy (&(sa.sin_addr.s_addr), "\x7f\0\0\1", 4); +#endif /* ! INADDR_LOOPBACK */ + if (0 != wr_connect (sock, + (struct sockaddr *) &sa, + sizeof (sa))) + externalErrorExitDesc ("Connect socket failed"); + } + else + { +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + MHD_Socket tls_fork_sock; + uint16_t port; + + port = global_port; + if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, + port))) + externalErrorExitDesc ("gnutlscli_connect() failed"); + + sock = wr_create_from_plain_sckt (tls_fork_sock); + if (NULL == sock) + externalErrorExitDesc ("wr_create_from_plain_sckt() failed"); + + wr_make_nonblocking (sock); +#else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ + externalErrorExitDesc ("Unsupported 'use_tls_tool' value"); +#endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ + } + + if (0 != pthread_create (&pt_client, + NULL, + &run_usock_client, + sock)) + externalErrorExitDesc ("pthread_create() failed"); + // TODO: support external events + if (0 != pthread_join (pt_client, + NULL)) + externalErrorExitDesc ("pthread_join() failed"); + if (0 != pthread_join (pt_server, + NULL)) + externalErrorExitDesc ("pthread_join() failed"); +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + if (test_tls && (TLS_LIB_GNUTLS != use_tls_tool)) + { + if ((pid_t) -1 == waitpid (pid, NULL, 0)) + externalErrorExitDesc ("waitpid() failed"); + } +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + if (! client_done) + externalErrorExitDesc ("The client thread has not signalled " \ + "successful finish"); + if (! app_done) + externalErrorExitDesc ("The application thread has not signalled " \ + "successful finish"); + MHD_daemon_destroy (d); + return 0; +} + + +enum test_msg_type +{ + test_msg_large_app_data, + test_msg_large_rclient_data, + test_msg_vlarge_app_data, + test_msg_vlarge_rclient_data +}; + +/** + * Initialise test message data + * @param buf the pointer to the buffer to fill with the test data + * @param buf_size the size of the @a buf + * @param msg_type the type of the data to fill the @a buf + * @return the @a buf pointer + */ +static void * +init_test_msg (void *buf, size_t buf_size, enum test_msg_type msg_type) +{ + size_t i; + char *const text_buf = (char *) buf; + uint8_t *const bin_buf = (uint8_t *) buf; + if (0 == buf_size) + return buf; + switch (msg_type) + { + case test_msg_large_app_data: + case test_msg_large_rclient_data: + /* Simulate text data */ + for (i = 0; i < buf_size; ++i) + { + size_t pos; + if (test_msg_large_app_data == msg_type) + pos = i + 43; + else + pos = i + 26; + if ((0 == i) || (2 == pos % 100) ) + text_buf[i] = + (char) (unsigned char) ((test_msg_large_app_data == msg_type) ? + ('Z' - pos % ('Z' - 'A' + 1)) : + ('A' + pos % ('Z' - 'A' + 1))); + else if (0 == pos % 100) + text_buf[i] = '.'; + else if (1 == pos % 100) + text_buf[i] = ' '; + else if ((99 != pos % 100) && (2 != pos % 100) && (0 == pos % 5)) + text_buf[i] = ' '; + else if (test_msg_large_app_data == msg_type) + text_buf[i] = (char) (unsigned char) ('z' - pos % ('z' - 'a' + 1)); + else + text_buf[i] = (char) (unsigned char) ('a' + pos % ('z' - 'a' + 1)); + } + break; + case test_msg_vlarge_app_data: + /* Simulate binary data */ + for (i = 0; i < buf_size; ++i) + { + bin_buf[i] = (uint8_t) ((i + 182) & 0xFF); + } + break; + case test_msg_vlarge_rclient_data: + /* Simulate binary data */ + for (i = 0; i < buf_size; ++i) + { + bin_buf[i] = (uint8_t) ((111 - i) & 0xFF); + } + break; + default: + exit (99); + break; + } + return buf; +} + + +/** + * Perform initialisation of variables used in all check in this test + * @return true if succeed, + * false if failed. + */ +static bool +global_test_init (void) +{ + global_port = 0; + + if (use_large || use_vlarge) + { + unsigned int i; + size_t alloc_size; + alloc_size = use_vlarge ? (256U * 1024U) : (17U * 1024U); + for (i = 0; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); ++i) + { + alloc_ptr[i] = malloc (alloc_size); + if (NULL == alloc_ptr[i]) + { + for (--i; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); --i) + { + free (alloc_ptr[i]); + } + return false; + } + } + + rclient_msg_size = alloc_size; + rclient_msg = init_test_msg (alloc_ptr[0], rclient_msg_size, + use_vlarge ? + test_msg_vlarge_rclient_data : + test_msg_large_rclient_data); + app_msg_size = alloc_size; + app_msg = init_test_msg (alloc_ptr[1], app_msg_size, + use_vlarge ? + test_msg_vlarge_app_data : + test_msg_large_app_data); + } + else + { + unsigned int i; + for (i = 0; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); ++i) + alloc_ptr[i] = NULL; + + rclient_msg_size = mhd_SSTR_LEN ("Hello"); + rclient_msg = "Hello"; + app_msg_size = mhd_SSTR_LEN ("World"); + app_msg = "World"; + } + return true; +} + + +/** + * Perform de-initialisation of variables with memory de-allocation if required. + */ +static void +global_test_deinit (void) +{ + unsigned int i; + for (i = ((sizeof(alloc_ptr) / sizeof(alloc_ptr[0])) - 1); + i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); + --i) + { + if (NULL != alloc_ptr[i]) + free (alloc_ptr[i]); + } +} + + +int +main (int argc, + char *const *argv) +{ + unsigned int error_count = 0; + unsigned int res; + + use_vlarge = (0 != has_in_name (argv[0], "_vlarge")); + use_large = (! use_vlarge) && (0 != has_in_name (argv[0], "_large")); + + use_tls_tool = TLS_CLI_NO_TOOL; + test_tls = has_in_name (argv[0], "_tls"); + + verbose = ! (has_param (argc, argv, "-q") || + has_param (argc, argv, "--quiet") || + has_param (argc, argv, "-s") || + has_param (argc, argv, "--silent")); + + if ((((int) ((~((unsigned int) 0U)) >> 1)) / 1000) < test_timeout) + { + fprintf (stderr, "The test timeout value (%d) is too large.\n" + "The test cannot run.\n", test_timeout); + fprintf (stderr, "The maximum allowed timeout value is %d.\n", + (((int) ((~((unsigned int) 0U)) >> 1)) / 1000)); + return 3; + } + + if (test_tls) + { + use_tls_tool = TLS_LIB_GNUTLS; /* Should be always available as MHD uses it when TLS is supported. */ +#ifdef HTTPS_SUPPORT + if (has_param (argc, argv, "--use-gnutls-cli")) + use_tls_tool = TLS_CLI_GNUTLS; + else if (has_param (argc, argv, "--use-openssl")) + use_tls_tool = TLS_CLI_OPENSSL; + else if (has_param (argc, argv, "--use-gnutls-lib")) + use_tls_tool = TLS_LIB_GNUTLS; +#if defined(HAVE_FORK) && defined(HAVE_WAITPID) + else if (0 == system ("gnutls-cli --version 1> /dev/null 2> /dev/null")) + use_tls_tool = TLS_CLI_GNUTLS; + else if (0 == system ("openssl version 1> /dev/null 2> /dev/null")) + use_tls_tool = TLS_CLI_OPENSSL; +#endif /* HAVE_FORK && HAVE_WAITPID */ + if (verbose) + { + switch (use_tls_tool) + { + case TLS_CLI_GNUTLS: + printf ("GnuTLS-CLI will be used for testing.\n"); + break; + case TLS_CLI_OPENSSL: + printf ("Command line version of OpenSSL will be used for testing.\n"); + break; + case TLS_LIB_GNUTLS: + printf ("GnuTLS library will be used for testing.\n"); + break; + case TLS_CLI_NO_TOOL: + default: + externalErrorExitDesc ("Wrong 'use_tls_tool' value"); + } + } + if ( (TLS_LIB_GNUTLS == use_tls_tool) && + (GNUTLS_E_SUCCESS != gnutls_global_init ()) ) + externalErrorExitDesc ("gnutls_global_init() failed"); + +#else /* ! HTTPS_SUPPORT */ + fprintf (stderr, "HTTPS support was disabled by configure.\n"); + return 77; +#endif /* ! HTTPS_SUPPORT */ + } + + if (! global_test_init ()) + { +#ifdef HTTPS_SUPPORT + if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool)) + gnutls_global_deinit (); +#endif /* HTTPS_SUPPORT */ + fprintf (stderr, "Failed to initialise the test.\n"); + return 99; + } + + /* run tests */ + if (verbose) + printf ("Starting HTTP \"Upgrade\" tests with %s connections and " + "%s size messages.\n", + test_tls ? "TLS" : "plain", + use_large ? "large" : (use_vlarge ? "very large" : "basic")); + res = test_upgrade (); + fflush_allstd (); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: HTTP Upgrade, return code %u.\n", + res); + else if (verbose) + printf ("PASSED: HTTP Upgrade.\n"); + + // TODO: add thread-per-connection testing + // TODO: add external events testing + + /* report result */ + if (0 != error_count) + fprintf (stderr, + "Error (code: %u)\n", + error_count); + + global_test_deinit (); +#ifdef HTTPS_SUPPORT + if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool)) + gnutls_global_deinit (); +#endif /* HTTPS_SUPPORT */ + + return error_count != 0; /* 0 == pass */ +} diff --git a/src/tests/upgrade/tls_test_keys.h b/src/tests/upgrade/tls_test_keys.h @@ -0,0 +1,183 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors) + Copyright (C) 2021-2022 Evgeny Grin (Karlson2k) + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MHD_TLS_TEST_KEYS_H +#define MHD_TLS_TEST_KEYS_H + +/* Test Certificates */ + +/* Certificate Authority cert */ +static const char ca_cert_pem[] = + "-----BEGIN CERTIFICATE-----\n\ +MIIGITCCBAmgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCUlUx\n\ +DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRswGQYDVQQKDBJ0ZXN0\n\ +LWxpYm1pY3JvaHR0cGQxITAfBgkqhkiG9w0BCQEWEm5vYm9keUBleGFtcGxlLm9y\n\ +ZzEQMA4GA1UEAwwHdGVzdC1DQTAgFw0yMTA0MDcxNzM2MThaGA8yMTIxMDMxNDE3\n\ +MzYxOFowgYExCzAJBgNVBAYTAlJVMQ8wDQYDVQQIDAZNb3Njb3cxDzANBgNVBAcM\n\ +Bk1vc2NvdzEbMBkGA1UECgwSdGVzdC1saWJtaWNyb2h0dHBkMSEwHwYJKoZIhvcN\n\ +AQkBFhJub2JvZHlAZXhhbXBsZS5vcmcxEDAOBgNVBAMMB3Rlc3QtQ0EwggIiMA0G\n\ +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdaWupA4qZjCBNkJoJOm5xnCaizl36\n\ +ZLUwp4xBL/YfXPWE3LkmAREiVI/YnAb8l6G7CJnz8dTsOJWkNXG6T1KVP5/2RvBI\n\ +IaaaufRIAl7hEnj1j9E2hQlV2fxF2ZNhz+nqi0LqKV4LJSpclkXADf2FA9HsVRP/\n\ +B7zYh+DP0fSU8V6bsu8XCeRGshroAPrc8rH8lFEEXpNLNIqQr8yKx6SmdB6hfja6\n\ +6SQ0++qBhl0aJtn4LHWZohgjBmkIaGFPYIJLgxQ/xyp2Grz2q7lGKJ+zBkBF8iOP\n\ +t3x+F1hSCBnr/DGYWmjEm5tYm+7pyuriPddXdCc8+qa2LxMZo3EXxLo5YISpPCyw\n\ +Z7V3YAOZTr3m1C24LiYvPehCq1CTIkhhmqtlVJXU7ISD48cx9y+5Pi34wtbTI/gN\n\ +x4voyTLAfyavKMmIpxxIRsWldiF2n06HdvCRVdihDQUad10ygTmWf1J/s2ZETAtH\n\ +QaSd7MD389t6nQFtTIXigsNKnnDPlrtxt7rOLvLQeR0K04Gzrf/scheOanRAfOXH\n\ +KNBFU7YkDFG8rqizlC65rx9qeXFYXQcHZTuqxK7tgZnSgJat3E70VbTSCsEEG7eR\n\ +bNX/fChUKAIIpWaiW6HDlKLl6m2y+BzM91umBsKOqTvntMVFBSF9pVYlXK854aIR\n\ +q8A2Xujd012seQIDAQABo4GfMIGcMAsGA1UdDwQEAwICpDASBgNVHRMBAf8ECDAG\n\ +AQH/AgEBMB0GA1UdDgQWBBRYdUPApWoxw4U13Rqsjf9AHdbpLDATBgNVHSUEDDAK\n\ +BggrBgEFBQcDATAkBglghkgBhvhCAQ0EFxYVVGVzdCBsaWJtaWNyb2h0dHBkIENB\n\ +MB8GA1UdIwQYMBaAFFh1Q8ClajHDhTXdGqyN/0Ad1uksMA0GCSqGSIb3DQEBCwUA\n\ +A4ICAQBvrrcTKVeI1EYnXo4BQD4oCvf9z1fYQmL21EbHwgjg1nmaPkvStgWAc5p1\n\ +kKwySrpEMKXfu68X76RccXZyWWIamEjz2OCWYZgjX6d6FpjhLphL8WxXDy5C9eay\n\ +ixN7+URz2XQoi22wqR+tCPDhrIzcMPyMkx/6gRgcYeDnaFrkdSeSsKsID4plfcIj\n\ +ISWJDvv+IAgrtsG1NVHnGwpAv0od3A8/4/fR6PPyewaU3aydvjZ7Au8O9DGDjlU9\n\ +9HdlOkkY6GVJ1pfGZib7cV7lhy0D2kj1g9xZh97YjpoUfppPl9r+6A8gDm0hXlAD\n\ +TlzNYlwTb681ZEoSd9PiLEY8HETssHlays2dYXdcNwAEp69iIHz8q1Q98Be9LScl\n\ +WEzgaOT9U7lpIw/MWbELoMsC+Ecs1cVWBIuiIq8aSG2kRr1x3S8yVXbAohAXif2s\n\ +E6puieM/VJ25iaNhkbLmDkk58QVVmn9NZNv6ETxuSQMp9e0EwbVlj68vzClQ91Y/\n\ +nmAiGcLFUEwB9G0szv9+vR+oDW4IkvdFZSUbcICd2cnynnwAD395onqS4hEZO1xM\n\ +Gy5ZldbTMTjgn7fChNopz15ChPBnwFIjhm+S0CyiLRQAowfknRVq2IBkj7/5kOWg\n\ +4mcxcq76HoQWK/8X/8RFL1eFVAvY7TNHYJ0RS51DMuwCNQictA==\n\ +-----END CERTIFICATE-----"; + + +/* test server key */ +static const char srv_signed_key_pem[] = + "-----BEGIN PRIVATE KEY-----\n\ +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCff7amw9zNSE+h\n\ +rOMhBrzbbsJluUP3gmd8nOKY5MUimoPkxmAXfp2L0il+MPZT/ZEmo11q0k6J2jfG\n\ +UBQ+oZW9ahNZ9gCDjbYlBblo/mqTai+LdeLO3qk53d0zrZKXvCO6sA3uKpG2WR+g\n\ ++sNKxfYpIHCpanqBU6O+degIV/+WKy3nQ2Fwp7K5HUNj1u0pg0QQ18yf68LTnKFU\n\ +HFjZmmaaopWki5wKSBieHivzQy6w+04HSTogHHRK/y/UcoJNSG7xnHmoPPo1vLT8\n\ +CMRIYnSSgU3wJ43XBJ80WxrC2dcoZjV2XZz+XdQwCD4ZrC1ihykcAmiQA+sauNm7\n\ +dztOMkGzAgMBAAECggEAIbKDzlvXDG/YkxnJqrKXt+yAmak4mNQuNP+YSCEdHSBz\n\ ++SOILa6MbnvqVETX5grOXdFp7SWdfjZiTj2g6VKOJkSA7iKxHRoVf2DkOTB3J8np\n\ +XZd8YaRdMGKVV1O2guQ20Dxd1RGdU18k9YfFNsj4Jtw5sTFTzHr1P0n9ybV9xCXp\n\ +znSxVfRg8U6TcMHoRDJR9EMKQMO4W3OQEmreEPoGt2/+kMuiHjclxLtbwDxKXTLP\n\ +pD0gdg3ibvlufk/ccKl/yAglDmd0dfW22oS7NgvRKUve7tzDxY1Q6O5v8BCnLFSW\n\ +D+z4hS1PzooYRXRkM0xYudvPkryPyu+1kEpw3fNsoQKBgQDRfXJo82XQvlX8WPdZ\n\ +Ts3PfBKKMVu3Wf8J3SYpuvYT816qR3ot6e4Ivv5ZCQkdDwzzBKe2jAv6JddMJIhx\n\ +pkGHc0KKOodd9HoBewOd8Td++hapJAGaGblhL5beIidLKjXDjLqtgoHRGlv5Cojo\n\ +zHa7Viel1eOPPcBumhp83oJ+mQKBgQDC6PmdETZdrW3QPm7ZXxRzF1vvpC55wmPg\n\ +pRfTRM059jzRzAk0QiBgVp3yk2a6Ob3mB2MLfQVDgzGf37h2oO07s5nspSFZTFnM\n\ +KgSjFy0xVOAVDLe+0VpbmLp1YUTYvdCNowaoTE7++5rpePUDu3BjAifx07/yaSB+\n\ +W+YPOfOuKwKBgQCGK6g5G5qcJSuBIaHZ6yTZvIdLRu2M8vDral5k3793a6m3uWvB\n\ +OFAh/eF9ONJDcD5E7zhTLEMHhXDs7YEN+QODMwjs6yuDu27gv97DK5j1lEsrLUpx\n\ +XgRjAE3KG2m7NF+WzO1K74khWZaKXHrvTvTEaxudlO3X8h7rN3u7ee9uEQKBgQC2\n\ +wI1zeTUZhsiFTlTPWfgppchdHPs6zUqq0wFQ5Zzr8Pa72+zxY+NJkU2NqinTCNsG\n\ +ePykQ/gQgk2gUrt595AYv2De40IuoYk9BlTMuql0LNniwsbykwd/BOgnsSlFdEy8\n\ +0RQn70zOhgmNSg2qDzDklJvxghLi7zE5aV9//V1/ewKBgFRHHZN1a8q/v8AAOeoB\n\ +ROuXfgDDpxNNUKbzLL5MO5odgZGi61PBZlxffrSOqyZoJkzawXycNtoBP47tcVzT\n\ +QPq5ZOB3kjHTcN7dRLmPWjji9h4O3eHCX67XaPVMSWiMuNtOZIg2an06+jxGFhLE\n\ +qdJNJ1DkyUc9dN2cliX4R+rG\n\ +-----END PRIVATE KEY-----"; + +/* test server CA signed certificates */ +static const char srv_signed_cert_pem[] = + "-----BEGIN CERTIFICATE-----\n\ +MIIFSzCCAzOgAwIBAgIBBDANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCUlUx\n\ +DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRswGQYDVQQKDBJ0ZXN0\n\ +LWxpYm1pY3JvaHR0cGQxITAfBgkqhkiG9w0BCQEWEm5vYm9keUBleGFtcGxlLm9y\n\ +ZzEQMA4GA1UEAwwHdGVzdC1DQTAgFw0yMjA0MjAxODQzMDJaGA8yMTIyMDMyNjE4\n\ +NDMwMlowZTELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwG\n\ +TW9zY293MRswGQYDVQQKDBJ0ZXN0LWxpYm1pY3JvaHR0cGQxFzAVBgNVBAMMDnRl\n\ +c3QtbWhkc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn3+2\n\ +psPczUhPoazjIQa8227CZblD94JnfJzimOTFIpqD5MZgF36di9IpfjD2U/2RJqNd\n\ +atJOido3xlAUPqGVvWoTWfYAg422JQW5aP5qk2ovi3Xizt6pOd3dM62Sl7wjurAN\n\ +7iqRtlkfoPrDSsX2KSBwqWp6gVOjvnXoCFf/list50NhcKeyuR1DY9btKYNEENfM\n\ +n+vC05yhVBxY2ZpmmqKVpIucCkgYnh4r80MusPtOB0k6IBx0Sv8v1HKCTUhu8Zx5\n\ +qDz6Nby0/AjESGJ0koFN8CeN1wSfNFsawtnXKGY1dl2c/l3UMAg+GawtYocpHAJo\n\ +kAPrGrjZu3c7TjJBswIDAQABo4HmMIHjMAsGA1UdDwQEAwIFoDAMBgNVHRMBAf8E\n\ +AjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMDEGA1UdEQQqMCiCDnRlc3QtbWhk\n\ +c2VydmVyhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBQ57Z06WJae\n\ +8fJIHId4QGx/HsRgDDAoBglghkgBhvhCAQ0EGxYZVGVzdCBsaWJtaWNyb2h0dHBk\n\ +IHNlcnZlcjARBglghkgBhvhCAQEEBAMCBkAwHwYDVR0jBBgwFoAUWHVDwKVqMcOF\n\ +Nd0arI3/QB3W6SwwDQYJKoZIhvcNAQELBQADggIBAI7Lggm/XzpugV93H5+KV48x\n\ +X+Ct8unNmPCSzCaI5hAHGeBBJpvD0KME5oiJ5p2wfCtK5Dt9zzf0S0xYdRKqU8+N\n\ +aKIvPoU1hFixXLwTte1qOp6TviGvA9Xn2Fc4n36dLt6e9aiqDnqPbJgBwcVO82ll\n\ +HJxVr3WbrAcQTB3irFUMqgAke/Cva9Bw79VZgX4ghb5EnejDzuyup4pHGzV10Myv\n\ +hdg+VWZbAxpCe0S4eKmstZC7mWsFCLeoRTf/9Pk1kQ6+azbTuV/9QOBNfFi8QNyb\n\ +18jUjmm8sc2HKo8miCGqb2sFqaGD918hfkWmR+fFkzQ3DZQrT+eYbKq2un3k0pMy\n\ +UySy8SRn1eadfab+GwBVb68I9TrPRMrJsIzysNXMX4iKYl2fFE/RSNnaHtPw0C8y\n\ +B7memyxPRl+H2xg6UjpoKYh3+8e44/XKm0rNIzXjrwA8f8gnw2TbqmMDkj1YqGnC\n\ +SCj5A27zUzaf2pT/YsnQXIWOJjVvbEI+YKj34wKWyTrXA093y8YI8T3mal7Kr9YM\n\ +WiIyPts0/aVeziM0Gunglz+8Rj1VesL52FTurobqusPgM/AME82+qb/qnxuPaCKj\n\ +OT1qAbIblaRuWqCsid8BzP7ZQiAnAWgMRSUg1gzDwSwRhrYQRRWAyn/Qipzec+27\n\ +/w0gW9EVWzFhsFeGEssi\n\ +-----END CERTIFICATE-----"; + +/* test server self signed certificates */ +static const char srv_self_signed_cert_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIDJzCCAg+gAwIBAgIUOKf6e6Heee2XA+yF5St3t+fVM40wDQYJKoZIhvcNAQEF\n" + "BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTIyMTAxMDA4MzQ0N1oYDzIxMjIw\n" + "OTE2MDgzNDQ3WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB\n" + "AQUAA4IBDwAwggEKAoIBAQClivgF8Xq0ekQli++0l7Q5JFwJCuLf04Cb1UKIS80U\n" + "CfphFd1ILJepNw4bWR3OV1sRI1vFiw6LnCz53vOwVNyiZ+sMGi4bDX4AV9Xd+F83\n" + "xhG8AjOmKTayW0TxSIvt47Qd5S/4fgraxMtvqrRRBen30iKOwX7uNF/4dYb9vdin\n" + "OldV/e8uzbqSurMGkNDznOeSaNBmdO/7x0VMFZM2hwmHyiiw75/j4BhUlLCcMEvK\n" + "oN+YHNCNcTt3Qm1vVuiGXmh9QreOV09Gc1SzAltxF2gmI0jzw8r/duz18QXMNsMw\n" + "El/Ah4+02gR70L7qlgttN1NPU3RJpK/L34J7yg649wHTAgMBAAGjbzBtMB0GA1Ud\n" + "DgQWBBROVferD+YYcV1YEnFgC0jYm5X9BjAfBgNVHSMEGDAWgBROVferD+YYcV1Y\n" + "EnFgC0jYm5X9BjAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQTMBGCCWxvY2FsaG9z\n" + "dIcEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAoRbozsm5xXdNX3VO++s2LMzw5KM9\n" + "RpIInHNkMJbnyLJFKJ8DF7nTxSGCA38YMkX3tphPNKZXbg+V64Dqr/XpzOVyiinU\n" + "7hIwyUdSSKKyErZxIWR97lY6Q3SOyPAg8ZElbtvSsSzmd772VE23VTXGDi7AW0PQ\n" + "hag9N2EEnHURMvID15O+UXyFpDdyUyQIbx3HuswsGDH9xBTm4irLyrZwO0KwKg5a\n" + "JBeUiPs0SYRRfn9/MoE6VwAnmOCg3LLR6ZPU3hQtTPLHj2Op1g5fey3X3X6lC+JC\n" + "K6dNZc1zBFPz8KANGUsFYbmoP2bvAAA+6KwCnZZEflUgE7/HFEmQhVOezw==\n" + "-----END CERTIFICATE-----\n"; + +/* test server key */ +static const char srv_self_signed_key_pem[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClivgF8Xq0ekQl\n" + "i++0l7Q5JFwJCuLf04Cb1UKIS80UCfphFd1ILJepNw4bWR3OV1sRI1vFiw6LnCz5\n" + "3vOwVNyiZ+sMGi4bDX4AV9Xd+F83xhG8AjOmKTayW0TxSIvt47Qd5S/4fgraxMtv\n" + "qrRRBen30iKOwX7uNF/4dYb9vdinOldV/e8uzbqSurMGkNDznOeSaNBmdO/7x0VM\n" + "FZM2hwmHyiiw75/j4BhUlLCcMEvKoN+YHNCNcTt3Qm1vVuiGXmh9QreOV09Gc1Sz\n" + "AltxF2gmI0jzw8r/duz18QXMNsMwEl/Ah4+02gR70L7qlgttN1NPU3RJpK/L34J7\n" + "yg649wHTAgMBAAECggEAERbbCtYGakoy7cNX8Ac3Kiz4OVC/4gZWAQBPeX2FwrtS\n" + "9yHIMbK0x1mxIZ6eBpabBpZlW2vDCSOKuxLKiloAWt2qdJnhR5apesSWhe8leT7/\n" + "xq5dgZpAlMH6SIRKObknd2yY+qicW0A0licDrVeUcypkueL8xP9wJtiPInOuQXkI\n" + "QROhB13eStRuRKYwOn5gtwAHJ+J1DFKKiqpBOkrSYf4625StGegJO9+bjK0ei+0W\n" + "tp6unpiwA/lXTgz6Xim1Z3fzWs4XjFgVKzK5s/6yBJjr8spHX6lv7QsahP4w6HZ/\n" + "VcRxP6cJNd/otiTEtJXpbxiiyccwXm/AOcOn22P1cQKBgQDAnY/0G/ap/G98pneE\n" + "suzNXhWOQ8JoL8d66Io8vwTvfiJggfgUcwblI7pPCrSlaZMR7/q6JImE53lZtPk8\n" + "eI3c9lN0ocr8E7+huDpYdk7cMYj9SuxySsXoMLiMqzHFi+NcIhKMF56kk6a5CFCt\n" + "yP1Ofy76LVweGE3XvTwpwE7wUQKBgQDcBLyH1cC71s0I0Gz28AyELV9hPhasjAKO\n" + "12CVbeBVTPd+28uk/3o80wSrTksc6H5ehAA2aTvrb4OhwssWNL+D0fS8YK2cJ3V0\n" + "FJxGAM266+vC4d/8jRTHJnc+6PP3ix5t6vAt+K2Y0fePtefLqf4ebgXx/ODAj3J2\n" + "aZKBldjK4wKBgGIRFpTLk/eR/dUyEBHw4x3gdAsdtqJDCUYrlQ4+ly20Q55tLbiD\n" + "pBQP77CEm9rH+MgeLcKODbIsBB3HRUojet7wTydHpMhY6a1V1ebqPVZgpgWIGwBJ\n" + "z59bBusf0lRo15Y2Bslq0SurvSvh7um8NjO8D1fytj7gUumvgC0lq0sxAoGBAI1+\n" + "kkx9IBTtIDER8XGhkTsT/uoHxwcyh5abVmbjIclZ1TUFX2L+Vft17ePJVy8BKfvY\n" + "wlY7uShBMBNAteDTDXNV/CGFv0DUc4myk4nFjIkwng9XufeuN3WX/Eo+AF/rXSdt\n" + "VwcJjYLhTWdjoe1tppqlQTeN3HCaEA+s92ZVGvXnAoGAMCXGS6WZl1e5wsHRq0Yy\n" + "8Ef2Wrk620bBjKHolkTfvgfhlvxeZM1sv1ioZGsOeQ0z7O7wdJhvL0M/WAG+3yQj\n" + "HSXp81T1vOICPwNYZf8xcvbLKmvj7rHFt6ZAZF2o4EK8ReZTRyA3DUpBCDY+s3FN\n" + "GmBv0D7N3QP0CT3SzfQrPkc=\n" + "-----END PRIVATE KEY-----\n"; + +#endif