libmicrohttpd2

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

commit 7caa8e7968cdef9f80482e16347707de728d9fcb
parent f43f06059b811591c7603892a093f5386e73752c
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Wed,  4 Dec 2024 19:54:12 +0100

Implemented TLS support with selectable TLS backends: GnuTLS and OpenSSL + some fixes and refactoring

Diffstat:
Mconfigure.ac | 881++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mdoc/examples/Makefile.am | 2+-
Mm4/mhd_shutdown_socket_trigger.m4 | 14+++++++-------
Dscripts/d_options.rec | 387-------------------------------------------------------------------------------
Dscripts/d_options.sh | 524-------------------------------------------------------------------------------
Dscripts/options_enum.template | 7-------
Dscripts/options_func.template | 17-----------------
Dscripts/options_macro.template | 14--------------
Dscripts/options_struct.template | 7-------
Dscripts/options_union.template | 4----
Dscripts/r_options.rec | 91-------------------------------------------------------------------------------
Dscripts/r_options.sh | 21---------------------
Msrc/incl_priv/mhd_sys_options.h | 57++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/include/d_options.rec | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/include/microhttpd2.h | 165++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/include/microhttpd2_generated_daemon_options.h | 62+++++++++++++++++++++++++++++++++++---------------------------
Msrc/include/microhttpd2_main.h.in | 42+++++++++++++++++++++++++++++++-----------
Msrc/include/microhttpd2_portability.h | 4++--
Msrc/include/microhttpd2_preamble.h.in | 111++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/mhd2/Makefile.am | 56+++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/mhd2/autoinit_funcs.h | 602+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/mhd2/compat_calloc.c | 1+
Msrc/mhd2/conn_data_process.c | 125++++++++++++++++++++++++-------------------------------------------------------
Msrc/mhd2/conn_data_recv.c | 36++++++++++++++++++------------------
Msrc/mhd2/conn_data_send.c | 118+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/mhd2/conn_mark_ready.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/conn_tls_check.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/conn_tls_check.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/daemon_add_conn.c | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/mhd2/daemon_create.c | 21+++++++++++++++++----
Msrc/mhd2/daemon_logger.h | 18++++++++++++++----
Msrc/mhd2/daemon_options.h | 8+++++---
Msrc/mhd2/daemon_set_options.c | 51+++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/mhd2/daemon_start.c | 239++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/mhd2/events_process.c | 63++++++++++++++++++++++++++++++---------------------------------
Msrc/mhd2/http_status_str.c | 3++-
Asrc/mhd2/lib_get_info.c | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_arr_num_elems.h | 37+++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_assert.h | 7-------
Asrc/mhd2/mhd_conn_socket.h | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_connection.h | 323++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/mhd2/mhd_daemon.h | 38++++++++++++++++++++++++++++++++++++--
Msrc/mhd2/mhd_iovec.h | 2+-
Msrc/mhd2/mhd_itc.c | 8+++++---
Msrc/mhd2/mhd_itc.h | 2++
Msrc/mhd2/mhd_lib_init.c | 434++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/mhd2/mhd_lib_init.h | 38+++++++++++---------------------------
Asrc/mhd2/mhd_lib_init_auto.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_lib_init_impl.h | 41++++++++++++++---------------------------
Msrc/mhd2/mhd_locks.h | 61+++++++++++++++++++++++++++++++++++--------------------------
Msrc/mhd2/mhd_mempool.c | 2++
Msrc/mhd2/mhd_mono_clock.c | 625++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/mhd2/mhd_mono_clock.h | 31++++++++++++++++++++++++-------
Asrc/mhd2/mhd_read_file.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_read_file.h | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_recv.c | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/mhd2/mhd_recv.h | 4++--
Msrc/mhd2/mhd_request.h | 6+++---
Msrc/mhd2/mhd_send.c | 276+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/mhd2/mhd_send.h | 9+++++----
Dsrc/mhd2/mhd_socket_error.c | 86-------------------------------------------------------------------------------
Msrc/mhd2/mhd_socket_error.h | 21+--------------------
Asrc/mhd2/mhd_socket_error_funcs.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_socket_error_funcs.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_socket_type.h | 4++--
Msrc/mhd2/mhd_sockets_funcs.c | 22+++++++++++-----------
Msrc/mhd2/mhd_sockets_macros.h | 82++++++++++++++++++++++++++++++++++++++++----------------------------------------
Asrc/mhd2/mhd_status_code_int.h | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/mhd_str.c | 2++
Msrc/mhd2/mhd_threads.c | 16+++++++++-------
Msrc/mhd2/mhd_threads.h | 52++++++++++++++++++++++++++--------------------------
Asrc/mhd2/mhd_tls_choice.h | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_tls_enums.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_tls_funcs.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_tls_funcs.h | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/mhd_unreachable.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/post_parser_funcs.c | 69++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/mhd2/request_get_value.c | 2+-
Msrc/mhd2/respond_with_error.c | 4++--
Msrc/mhd2/response_from.c | 10+++++-----
Msrc/mhd2/stream_funcs.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/mhd2/stream_funcs.h | 15+++++++++++++++
Msrc/mhd2/stream_process_reply.c | 148++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/mhd2/stream_process_request.c | 208++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/mhd2/stream_process_states.c | 312+++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/mhd2/stream_process_states.h | 16++++++++++++++--
Msrc/mhd2/sys_ip_headers.h | 2+-
Msrc/mhd2/sys_poll.h | 4++--
Msrc/mhd2/sys_select.h | 4++--
Msrc/mhd2/sys_sockets_headers.h | 32++++++++++++++++----------------
Msrc/mhd2/sys_sockets_types.h | 2+-
Msrc/mhd2/sys_thread_entry_type.h | 4++--
Asrc/mhd2/sys_w32_ver.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_dh_params.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_gnu_conn_data.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_gnu_daemon_data.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_gnu_funcs.c | 785+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_gnu_funcs.h | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_gnu_tls_lib.h | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_multi_conn_data.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_multi_daemon_data.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_multi_funcs.c | 617+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_multi_funcs.h | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_multi_tls_lib.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_open_conn_data.h | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_open_daemon_data.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_open_funcs.c | 1188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_open_funcs.h | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/tls_open_tls_lib.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/upgrade_prep.c | 12++++++------
Msrc/mhd2/upgrade_proc.c | 16++++++++--------
Msrc/mhd2/upgraded_net.c | 54+++++++++++++++++++++++++++---------------------------
Msrc/tests/upgrade/test_upgrade.c | 58+++++++++++++++++++++++++++++-----------------------------
113 files changed, 10017 insertions(+), 3274 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -1278,9 +1278,9 @@ int main(void) ) AS_IF([test "x${mhd_cv_cc_kwd_unreachable}" != "xnone"], [ - AC_DEFINE_UNQUOTED([MHD_UNREACHABLE_],[$mhd_cv_cc_kwd_unreachable],[Define to keyword used to indicate unreachable code paths]) - AS_IF([test "x${mhd_cv_cc_kwd_unreachable}" = 'xunreachable()'], - AC_DEFINE([MHD_UNREACHABLE_NEEDS_STDDEF_H],[$mhd_cv_cc_kwd_unreachable],[Define to '1' if MHD_UNREACHABLE_() requires include of <stddef.h> header]) + AC_DEFINE_UNQUOTED([MHD_UNREACHABLE_KEYWORD],[$mhd_cv_cc_kwd_unreachable],[Define to keyword supported to indicate unreachable code paths]) + AS_IF([test "x${mhd_cv_cc_kwd_unreachable}" = "xunreachable()" && test "x$ac_cv_header_stddef_h" = "xyes"], + [AC_DEFINE([MHD_UNREACHABLE_NEEDS_STDDEF_H],[$mhd_cv_cc_kwd_unreachable],[Define to '1' if MHD_UNREACHABLE_KEYWORD requires include of <stddef.h> header])] ) ] ) @@ -1386,6 +1386,7 @@ AS_CASE(["$host_os"], mhd_host_os='Windows/MinGW' AC_MSG_RESULT([[$mhd_host_os]]) AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([[Winsock2 headers are required for W32]])], [AC_INCLUDES_DEFAULT]) + AC_CHECK_HEADERS([sdkddkver.h], [], [], [AC_INCLUDES_DEFAULT]) AC_CACHE_CHECK([for MS lib utility], [ac_cv_use_ms_lib_tool], [mslibcheck=`lib 2>&1` AS_IF([echo "$mslibcheck" | $GREP -e '^Microsoft (R) Library Manager' - >/dev/null], @@ -1721,12 +1722,12 @@ AS_IF([test "x$with_threads" = "xnone"], AS_IF([test "x$USE_THREADS" = "xposix"], [CC="$PTHREAD_CC" - AC_DEFINE([MHD_USE_POSIX_THREADS],[1],[define to use pthreads]) + AC_DEFINE([mhd_THREADS_KIND_POSIX],[1],[define to use pthreads]) MHD_LIB_CFLAGS="$MHD_LIB_CFLAGS $PTHREAD_CFLAGS" MHD_LIBDEPS="$PTHREAD_LIBS $MHD_LIBDEPS" MHD_LIBDEPS_PKGCFG="$PTHREAD_LIBS $MHD_LIBDEPS_PKGCFG"], [AS_IF([test "x$USE_THREADS" = "xw32"], - [AC_DEFINE([MHD_USE_W32_THREADS],[1],[define to use W32 threads])])]) + [AC_DEFINE([mhd_THREADS_KIND_W32],[1],[define to use W32 threads])])]) AM_CONDITIONAL([USE_POSIX_THREADS], [test "x$USE_THREADS" = "xposix"]) AM_CONDITIONAL([USE_W32_THREADS], [test "x$USE_THREADS" = "xw32"]) AM_CONDITIONAL([MHD_USE_THREADS], [test "x$USE_THREADS" != "xnone"]) @@ -1905,11 +1906,11 @@ choke me #endif /* Keep in sync with mhd_threads.h */ -#if defined(MHD_USE_POSIX_THREADS) && (defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) || \ +#if defined(mhd_THREADS_KIND_POSIX) && (defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) || \ defined(HAVE_PTHREAD_SETNAME_NP_GNU) || defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) ) (void) 0; /* no-op */ -#elif defined(MHD_USE_W32_THREADS) && defined(_MSC_FULL_VER) +#elif defined(mhd_THREADS_KIND_W32) && defined(_MSC_FULL_VER) (void) 0; /* no-op */ #else #error No thread name function is available. @@ -3190,7 +3191,7 @@ AS_VAR_IF([[os_is_native_w32]], [["yes"]],[], ) AC_CHECK_DECLS([AF_UNIX,AF_LOCAL,SOMAXCONN,IPV6_V6ONLY,SO_REUSEADDR,SO_REUSEPORT,\ - TCP_NODELAY,TCP_FASTOPEN],[],[], +TCP_NODELAY,TCP_FASTOPEN],[],[], [[ #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> @@ -3720,10 +3721,42 @@ MHD_FIND_LIB([clock_gettime],[[#include <time.h>]], [MHD_LIBDEPS] ) +MHD_CHECK_FUNC([mach_continuous_approximate_time], + [[ +#include <mach/mach.h> +#include <mach/mach_time.h> + ]], + [[ + struct mach_timebase_info mtb_info; + i][f (KERN_SUCCESS != mach_timebase_info(&mtb_info)) + return 2; + i][f (0 == mach_continuous_approximate_time() * mtb_info.numer / mtb_info.denom) + return 3; + ]], [], [ + MHD_CHECK_FUNC([mach_approximate_time], + [[ +#include <mach/mach.h> +#include <mach/mach_time.h> + ]], + [[ + struct mach_timebase_info mtb_info; + i][f (KERN_SUCCESS != mach_timebase_info(&mtb_info)) + return 2; + i][f (0 == mach_approximate_time() * mtb_info.numer / mtb_info.denom) + return 3; + ]] + ) + ] +) + MHD_CHECK_FUNC([clock_get_time], [[ -#include <mach/clock.h> #include <mach/mach.h> +#include <mach/clock.h> + +#if !defined(SYSTEM_CLOCK) && defined(REALTIME_CLOCK) +# define SYSTEM_CLOCK REALTIME_CLOCK +#endif ]], [[ clock_serv_t cs; @@ -4496,10 +4529,16 @@ AM_CONDITIONAL([MHD_HAVE_LIBMAGIC], [[test "x$mhd_cv_have_func_magic_open" = "xy # large file support (> 4 GB) MHD_CHECK_FUNC([lseek64], [[ +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE 1 +#endif #if defined(HAVE_SYS_TYPES_H) # include <sys/types.h> #endif -#include <unistd.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif ]], [[ i][f (((off64_t) -1) == lseek64(0, (off64_t) 0, SEEK_SET)) @@ -4508,10 +4547,16 @@ MHD_CHECK_FUNC([lseek64], ) MHD_CHECK_FUNC([pread64], [[ +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE 1 +#endif #if defined(HAVE_SYS_TYPES_H) # include <sys/types.h> #endif -#include <unistd.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif ]], [[ char buf[5]; @@ -4524,7 +4569,10 @@ MHD_CHECK_FUNC([pread], #if defined(HAVE_SYS_TYPES_H) # include <sys/types.h> #endif -#include <unistd.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif ]], [[ char buf[5]; @@ -4578,6 +4626,9 @@ static void empty_func(void) AC_MSG_RESULT([[yes]]) MHD_CHECK_FUNC([sendfile64], [[ +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE 1 +#endif #include <sys/sendfile.h> ]], [[ @@ -4819,323 +4870,458 @@ AM_CONDITIONAL([HAVE_POST_PARSER], [test "x$enable_postparser" != "xno"]) AC_MSG_RESULT([[$enable_postparser]]) have_gnutls=no -have_gnutls_sni=no -have_gcrypt=no -AS_UNSET([GNUTLS_CPPFLAGS]) -AS_UNSET([GNUTLS_LDFLAGS]) - -# optional: HTTPS support. Enabled by default +have_gnutls_pkgcfg=no +have_openssl=no +have_openssl_pkgcfg=no +multiple_tls="no" +AS_UNSET([MHD_TLS_LIB_CPPFLAGS]) +AS_UNSET([MHD_TLS_LIB_LDFLAGS]) +AS_UNSET([MHD_TLS_LIBDEPS]) +MSG_TLS_BACKENDS="none" + +# optional: HTTPS support. Enabled if GnuTLS is available. AC_ARG_ENABLE([https], [AS_HELP_STRING([--enable-https], [enable HTTPS support (yes, no, auto)[auto]])], [enable_https=${enableval}]) -AS_IF([test "x$enable_https" != "xno"],[ -# -# Next block is large unindented block -# - -# gnutls -have_gnutls_pkgcfg=no -AC_MSG_CHECKING([[how to find GnuTLS library]]) -AC_ARG_WITH([[gnutls]], - [AS_HELP_STRING([[--with-gnutls[=PFX]]],[use GnuTLS for HTTPS support, optional PFX overrides pkg-config data for GnuTLS headers (PFX/include) and libs (PFX/lib)])], - [ - AS_CASE([$with_gnutls], - [no],[ - AC_MSG_RESULT([[GnuTLS disabled]]) - AS_UNSET([GNUTLS_CPPFLAGS]) - AS_UNSET([GNUTLS_CFLAGS]) - AS_UNSET([GNUTLS_LDFLAGS]) - AS_UNSET([GNUTLS_LIBS]) - ], - [yes],[ - AC_MSG_RESULT([[automatically, forced]]) +AS_IF([test "x$enable_https" != "xno"], + [ + have_gnutls_pkgcfg=no + AC_MSG_CHECKING([[how to find GnuTLS library]]) + AC_ARG_WITH([[gnutls]], + [ + AS_HELP_STRING([[--with-gnutls[=PRFX]]], + [use GnuTLS for HTTPS support, optional PRFX overrides pkg-config data for GnuTLS headers (PRFX/include) and libs (PRFX/lib)]) ], [ - AC_MSG_RESULT([[-I$with_gnutls/include -L$with_gnutls/lib -lgnutls]]) - SAVE_LIBS="$LIBS" - LDFLAGS="${LDFLAGS_ac} -L$with_gnutls/lib ${user_LDFLAGS}" - CPPFLAGS="${CPPFLAGS_ac} -I$with_gnutls/include ${user_CPPFLAGS}" - have_gnutls_pkgcfg=no - MHD_CHECK_FUNC([gnutls_check_version],[[#include <gnutls/gnutls.h>]], - [ - if(!gnutls_check_version("2.0.0")) - return 3; + AS_CASE([$with_gnutls], + [no],[ + have_gnutls="no" + AS_UNSET([GNUTLS_CPPFLAGS]) + AS_UNSET([GNUTLS_CFLAGS]) + AS_UNSET([GNUTLS_LDFLAGS]) + AS_UNSET([GNUTLS_LIBS]) + AC_MSG_RESULT([[GnuTLS disabled]]) ], - [ - GNUTLS_CPPFLAGS="-I$with_gnutls/include" - GNUTLS_LDFLAGS="-L$with_gnutls/lib" - GNUTLS_LIBS="-lgnutls" - have_gnutls=yes - ], - [AC_MSG_ERROR([can't find usable libgnutls at specified prefix $with_gnutls])], - [-lgnutls] - ) - CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" - LIBS="$SAVE_LIBS" - ]) - ], - [AC_MSG_RESULT([[automatically]]) - ]) - -AS_IF([test "x$with_gnutls" != "xno" && test "x$have_gnutls" != "xyes"], - [ - PKG_CHECK_MODULES(GNUTLS, [[gnutls]], - [ - have_gnutls_pkgcfg='yes' - SAVE_LIBS="$LIBS" - CFLAGS="${CFLAGS_ac} $GNUTLS_CFLAGS ${user_CFLAGS}" - LIBS="$GNUTLS_LIBS $LIBS" - AC_MSG_CHECKING([[whether GnuTLS is usable]]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ -#include <gnutls/gnutls.h> - ]], [[ - gnutls_session_t session; - gnutls_priority_t priorities; - gnutls_global_init(); - gnutls_priority_init(&priorities, "NORMAL", NULL); - gnutls_init(&session, GNUTLS_SERVER); - gnutls_priority_set(session, priorities); - ]])], - [ - AC_MSG_RESULT([[yes]]) - have_gnutls=yes - # GNUTLS_CFLAGS is actually CPPFLAGS - GNUTLS_CPPFLAGS="$GNUTLS_CFLAGS" - # GNUTLS_LDFLAGS is a combination of LDFLAGS and LIBS - GNUTLS_LDFLAGS="$GNUTLS_LIBS" + [yes],[ + AC_MSG_RESULT([[automatically, forced]]) ], [ - AC_MSG_RESULT([[no]]) - have_gnutls=no + AC_MSG_RESULT([[-I$with_gnutls/include -L$with_gnutls/lib -lgnutls]]) + LDFLAGS="${LDFLAGS_ac} -L$with_gnutls/lib ${user_LDFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} -I$with_gnutls/include ${user_CPPFLAGS}" + have_gnutls_pkgcfg=no + # A simple check for the working header and the library + MHD_CHECK_FUNC([gnutls_check_version], + [[ +]AC_INCLUDES_DEFAULT[ +#include <gnutls/gnutls.h> + ]], + [ + if(!gnutls_check_version("3.0.0")) + return 3; + ], + [ + have_gnutls=yes + GNUTLS_CPPFLAGS="-I$with_gnutls/include" + AS_UNSET([GNUTLS_CFLAGS]) + GNUTLS_LDFLAGS="-L$with_gnutls/lib" + GNUTLS_LIBS="-lgnutls" + ], + [AC_MSG_ERROR([cannot find usable libgnutls at specified prefix $with_gnutls])], + [-lgnutls] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" ]) + ], + [AC_MSG_RESULT([[automatically]])] + ) - AS_IF([test "x$have_gnutls" != "xyes"], - [ - AC_MSG_WARN([pkg-config reports that GnuTLS is present, but GnuTLS can't be used]) - AS_UNSET([GNUTLS_CPPFLAGS]) - AS_UNSET([GNUTLS_CFLAGS]) - AS_UNSET([GNUTLS_LDFLAGS]) - AS_UNSET([GNUTLS_LIBS]) - ] - ) - CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" - LIBS="$SAVE_LIBS" - ], + AS_IF([test "x$with_gnutls" != "xno" && test "x$have_gnutls" != "xyes"], [ - # check for GnuTLS at default paths - have_gnutls_pkgcfg='no' - AC_CHECK_HEADERS([gnutls/gnutls.h], - [AC_CHECK_LIB([gnutls], [gnutls_priority_set], + PKG_CHECK_MODULES([GNUTLS], [[gnutls]], [ - GNUTLS_LIBS="-lgnutls" - have_gnutls=yes - ])], [], [AC_INCLUDES_DEFAULT]) - ]) - ]) - -have_gcrypt='unknown' -AS_IF([test "x$with_gnutls" != "xno" && test "x$have_gnutls" != "xyes"], - [ - AM_PATH_LIBGCRYPT([1.2.2], [have_gcrypt=yes], [have_gcrypt=no]) - AS_IF([[test "x$have_gcrypt" = "xyes"]], - [ - SAVE_LIBS="$LIBS" - CFLAGS="${CFLAGS_ac} $LIBGCRYPT_CFLAGS ${user_CFLAGS}" - # LIBGCRYPT_CFLAGS can be actually a CPPFLAGS, so check them both - CPPFLAGS="${CPPFLAGS_ac} $LIBGCRYPT_CFLAGS ${user_CPPFLAGS}" - AC_CHECK_HEADERS([gcrypt.h], [], [have_gcrypt=no], [AC_INCLUDES_DEFAULT]) - # Check for GnuTLS with gcrypt flags - LDFLAGS="${LDFLAGS_ac} ${LIBGCRYPT_LIBS} ${user_LDFLAGS}" - # A bit of hack: unset cache variable to force recheck - AS_UNSET([ac_cv_header_gnutls_gnutls_h]) - AC_CHECK_HEADERS([gnutls/gnutls.h], - [AS_UNSET([ac_cv_lib_gnutls_gnutls_priority_set]) # A bit of hack: unset cache variable to force recheck - AC_CHECK_LIB([gnutls], [gnutls_priority_set], + CPPFLAGS="${CPPFLAGS_ac} $GNUTLS_CFLAGS ${user_CPPFLAGS}" + # A simple check for the working header and the library + MHD_CHECK_FUNC([gnutls_check_version], + [[ +]AC_INCLUDES_DEFAULT[ +#include <gnutls/gnutls.h> + ]], + [ + if(!gnutls_check_version("3.0.0")) + return 3; + ], + [ + have_gnutls="yes" + have_gnutls_pkgcfg="yes" + # GNUTLS_CFLAGS is actually CPPFLAGS + GNUTLS_CPPFLAGS="$GNUTLS_CFLAGS" + AS_UNSET([GNUTLS_CFLAGS]) + # GNUTLS_LIBS is a combination of LDFLAGS and LIBS + AS_UNSET([GNUTLS_LDFLAGS]) + ], + [ + AS_VAR_IF([with_gnutls],["yes"], + [AC_MSG_ERROR([cannot find usable libgnutls])] + ) + AC_MSG_WARN([pkg-config reports that GnuTLS is present, but GnuTLS cannot be used]) + AS_UNSET([GNUTLS_CPPFLAGS]) + AS_UNSET([GNUTLS_CFLAGS]) + AS_UNSET([GNUTLS_LDFLAGS]) + AS_UNSET([GNUTLS_LIBS]) + ], + [$GNUTLS_LIBS] + ) + ], [ - GNUTLS_CPPFLAGS="$LIBGCRYPT_CFLAGS" - GNUTLS_CFLAGS="$LIBGCRYPT_CFLAGS" - GNUTLS_LDFLAGS="$LIBGCRYPT_LIBS" - GNUTLS_LIBS="-lgnutls" - have_gnutls=yes - ])], [], [AC_INCLUDES_DEFAULT]) - CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" - LIBS="$SAVE_LIBS" - ] - ) - ] -) -AS_IF([test "x$have_gnutls" != "xyes" && test "x$with_gnutls" = "xyes"], - [AC_MSG_ERROR([[can't find usable libgnutls]])]) - - AS_IF([test "x$have_gnutls" = "xyes"], - [ - SAVE_LIBS="$LIBS" - CPPFLAGS="${CPPFLAGS_ac} ${GNUTLS_CPPFLAGS} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${GNUTLS_LDFLAGS} ${user_LDFLAGS}" - LIBS="$GNUTLS_LIBS $LIBS" - AC_MSG_CHECKING([[for gnutls_privkey_import_x509_raw()]]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ + # check for GnuTLS at default paths + have_gnutls_pkgcfg="no" + # A simple check for the working header and the library + MHD_CHECK_FUNC([gnutls_check_version], + [[ +]AC_INCLUDES_DEFAULT[ #include <gnutls/gnutls.h> -#include <gnutls/abstract.h> - ]], [[ - gnutls_datum_t data; - gnutls_privkey_t key = 0; -#ifndef gnutls_load_file - (void)gnutls_load_file; /* Check for declaration. */ -#endif -#ifndef gnutls_privkey_import_x509_raw - (void)gnutls_privkey_import_x509_raw; /* Check for declaration. */ -#endif - gnutls_load_file("key.pem", &data); - gnutls_privkey_import_x509_raw(key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); - gnutls_free(data.data); - ]])], [[have_gnutls_sni=yes]], [[have_gnutls_sni=no]]) - AC_MSG_RESULT([[$have_gnutls_sni]]) - AC_CACHE_CHECK([[whether GnuTLS require libgcrypt initialisation]], [mhd_cv_gcrypt_required], - [ - AC_COMPILE_IFELSE( + ]], + [ + if(!gnutls_check_version("3.0.0")) + return 3; + ], + [ + have_gnutls=yes + AS_UNSET([GNUTLS_CPPFLAGS]) + AS_UNSET([GNUTLS_CFLAGS]) + AS_UNSET([GNUTLS_LDFLAGS]) + GNUTLS_LIBS="-lgnutls" + ], + [ + AS_VAR_IF([with_gnutls],["yes"], + [AC_MSG_ERROR([cannot find usable libgnutls])] + ) + AS_UNSET([GNUTLS_CPPFLAGS]) + AS_UNSET([GNUTLS_CFLAGS]) + AS_UNSET([GNUTLS_LDFLAGS]) + AS_UNSET([GNUTLS_LIBS]) + ], + [-lgnutls] + ) + ] + ) + ] + ) + AS_VAR_IF([have_gnutls],["yes"], + [ + AC_CACHE_CHECK([[whether GnuTLS is modern enough]], [mhd_cv_gnutls_ver_ok], [ - AC_LANG_PROGRAM( - [ + CPPFLAGS="${CPPFLAGS_ac} ${GNUTLS_CPPFLAGS} ${user_CPPFLAGS}" + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +]AC_INCLUDES_DEFAULT[ #include <gnutls/gnutls.h> - ], - [ + ]], + [[ #if !defined(GNUTLS_VERSION_NUMBER) || GNUTLS_VERSION_NUMBER+0 <= 0x020c14 -#error Old versions of GnuTLS require libgcript initialisation +#error Too old version of GnuTLS that requires libgcript initialisation choke me now #endif - ] - ) - ], - [[mhd_cv_gcrypt_required='no']], [[mhd_cv_gcrypt_required='yes']] + ]] + ) + ], + [[mhd_cv_gnutls_ver_ok="yes"]], [[mhd_cv_gnutls_ver_ok="no"]] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + ] ) - ] - ) - CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" - LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" - LIBS="$SAVE_LIBS" - ], - [ - AS_UNSET([GNUTLS_CPPFLAGS]) - AS_UNSET([GNUTLS_LDFLAGS]) - ] - ) - - AS_IF([[test "x$mhd_cv_gcrypt_required" = "xyes" && test "x$have_gcrypt" = "xunknown"]], - [ - AM_PATH_LIBGCRYPT([1.2.2], [have_gcrypt=yes], [have_gcrypt=no]) - AS_IF([[test "x$have_gcrypt" = "xyes"]], - [ - CFLAGS="${CFLAGS_ac} ${LIBGCRYPT_CFLAGS} ${user_CFLAGS}" - # LIBGCRYPT_CFLAGS can be actually a CPPFLAGS, so check them both - CPPFLAGS="${CPPFLAGS_ac} ${LIBGCRYPT_CFLAGS} ${user_CPPFLAGS}" - AC_CHECK_HEADERS([gcrypt.h], [], [have_gcrypt=no], [AC_INCLUDES_DEFAULT]) - CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" - ] - ) - ] - ) + AS_VAR_IF([mhd_cv_gnutls_ver_ok],["yes"],[:], + [ + have_gnutls="no" + AS_VAR_IF([with_gnutls],["yes"], + [AC_MSG_ERROR([cannot find usable libgnutls])] + ) + ] + ) + ] + ) - AS_UNSET([[crypt_missing]]) - AS_IF([[test "x$have_gnutls" = "xyes"]], - [ - AS_IF([[test "x$mhd_cv_gcrypt_required" = "xyes" && test "x$have_gcrypt" != "xyes"]], - [ - crypt_missing="required libgcrypt" - AS_IF([[test "x$enable_https" = "xyes" ]], [AC_MSG_ERROR([[HTTPS support cannot be enabled without $crypt_missing.]])]) - enable_https=no - MSG_HTTPS="no (lacking $crypt_missing)" - AS_UNSET([LIBGCRYPT_CFLAGS]) - AS_UNSET([LIBGCRYPT_LIBS]) + AS_VAR_IF([have_gnutls],["yes"], + [:], + [ + have_gnutls="no" + have_gnutls_pkgcfg="no" AS_UNSET([GNUTLS_CPPFLAGS]) AS_UNSET([GNUTLS_CFLAGS]) AS_UNSET([GNUTLS_LDFLAGS]) AS_UNSET([GNUTLS_LIBS]) - ], - [ - AC_DEFINE([[HTTPS_SUPPORT]],[[1]],[Define to 1 if libmicrohttpd is compiled with HTTPS support.]) - enable_https=yes - AS_IF([[test "x$mhd_cv_gcrypt_required" = "xyes"]], - [ - MSG_HTTPS="yes (using libgnutls and libgcrypt)" - MHD_TLS_LIB_CPPFLAGS="$LIBGCRYPT_CFLAGS $GNUTLS_CPPFLAGS" - MHD_TLS_LIB_CFLAGS="$LIBGCRYPT_CFLAGS $GNUTLS_CFLAGS" - MHD_TLS_LIB_LDFLAGS="$GNUTLS_LDFLAGS" - MHD_TLS_LIBDEPS="$GNUTLS_LIBS $LIBGCRYPT_LIBS" - AC_DEFINE([[MHD_HTTPS_REQUIRE_GCRYPT]], [[1]], [Define to `1' if HTTPS require initialisation of libgcrypt]) - ], - [ - MSG_HTTPS="yes (using libgnutls)" - AS_UNSET([LIBGCRYPT_CFLAGS]) - AS_UNSET([LIBGCRYPT_LIBS]) - MHD_TLS_LIB_CPPFLAGS="$GNUTLS_CPPFLAGS" - MHD_TLS_LIB_CFLAGS="$GNUTLS_CFLAGS" - MHD_TLS_LIB_LDFLAGS="$GNUTLS_LDFLAGS" - MHD_TLS_LIBDEPS="$GNUTLS_LIBS" - ] - ) - AS_IF([[ test "x$have_gnutls_pkgcfg" = "xyes" ]], - [ # remove GnuTLS from private libs in .pc file as it defined in Requires.private - MHD_REQ_PRIVATE='gnutls' - AS_IF([[test "x$mhd_cv_gcrypt_required" = "xyes"]], - [[MHD_LIBDEPS_PKGCFG="$LIBGCRYPT_LIBS $MHD_LIBDEPS_PKGCFG"]] - ) - ], - [ - MHD_REQ_PRIVATE='' - AS_IF([[test "x$mhd_cv_gcrypt_required" = "xyes"]], - [[MHD_LIBDEPS_PKGCFG="$LIBGCRYPT_LIBS $MHD_LIBDEPS_PKGCFG"]] - ) - MHD_LIBDEPS_PKGCFG="$GNUTLS_LIBS $MHD_LIBDEPS_PKGCFG" + ] + ) + + + AC_MSG_CHECKING([[how to find OpenSSL library]]) + AC_ARG_WITH([[openssl]], + [ + AS_HELP_STRING([[--with-openssl[=PRFX]]], + [use OpenSSL for HTTPS support, optional PRFX overrides pkg-config data for OpenSSL headers (PRFX/include) and libs (PRFX/lib)]) + ], + [ + AS_CASE([$with_openssl], + [no],[ + have_openssl="no" + AC_MSG_RESULT([[OpenSSL disabled]]) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [yes],[ + AC_MSG_RESULT([[automatically, forced]]) + ], + [ + AC_MSG_RESULT([[-I$with_openssl/include -L$with_openssl/lib -lssl -lcrypto]]) + LDFLAGS="${LDFLAGS_ac} -L$with_openssl/lib ${user_LDFLAGS}" + CPPFLAGS="${CPPFLAGS_ac} -I$with_openssl/include ${user_CPPFLAGS}" + have_openssl_pkgcfg="no" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include <openssl/ssl.h> + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl="yes" + OPENSSL_CPPFLAGS="-I$with_openssl/include" + AS_UNSET([OPENSSL_CFLAGS]) + OPENSSL_LDFLAGS="-L$with_openssl/lib" + OPENSSL_LIBS="-lssl -lcrypto" + ], + [AC_MSG_ERROR([cannot find usable OpenSSL library at specified prefix $with_openssl])], + [-lssl -lcrypto] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" + LDFLAGS="${LDFLAGS_ac} ${user_LDFLAGS}" ]) - ] - ) - ], - [ - crypt_missing="libgnutls" - AS_IF([[test "x$enable_https" = "xyes" ]], [AC_MSG_ERROR([[HTTPS support cannot be enabled without $crypt_missing.]])]) - enable_https=no - MSG_HTTPS="no (lacking $crypt_missing)" - AS_UNSET([LIBGCRYPT_CFLAGS]) - AS_UNSET([LIBGCRYPT_LIBS]) - AS_UNSET([GNUTLS_CPPFLAGS]) - AS_UNSET([GNUTLS_CFLAGS]) - AS_UNSET([GNUTLS_LDFLAGS]) - AS_UNSET([GNUTLS_LIBS]) - ] - ) -],[ - MSG_HTTPS="no (disabled)" -]) + ], + [AC_MSG_RESULT([[automatically]])] + ) + + AS_IF([test "x$with_openssl" != "xno" && test "x$have_openssl" != "xyes"], + [ + PKG_CHECK_MODULES([OPENSSL], [[openssl >= 3.0]], + [ + CPPFLAGS="${CPPFLAGS_ac} $OPENSSL_CFLAGS ${user_CPPFLAGS}" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include <openssl/ssl.h> + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl=yes + have_openssl_pkgcfg='yes' + # OPENSSL_CFLAGS is actually CPPFLAGS + OPENSSL_CPPFLAGS="$OPENSSL_CFLAGS" + AS_UNSET([OPENSSL_CFLAGS]) + # OPENSSL_LIBS is a combination of LDFLAGS and LIBS + AS_UNSET([OPENSSL_LDFLAGS]) + ], + [ + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + AC_MSG_WARN([pkg-config reports that OpenSSL is present, but OpenSSL cannot be used]) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [$OPENSSL_LIBS] + ) + ], + [ + # check for OpenSSL at default paths + have_openssl_pkgcfg="no" + # A simple check for the working header and the library + MHD_CHECK_FUNC([SSL_CTX_new], + [[ +#include <openssl/ssl.h> + ]], + [[ + SSL_CTX_free(SSL_CTX_new(TLS_server_method())); + ]], + [ + have_openssl="yes" + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + OPENSSL_LIBS="-lssl -lcrypto" + ], + [ + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ], + [-lssl -lcrypto] + ) + ] + ) + ] + ) + AS_VAR_IF([have_openssl],["yes"], + [ + AC_CACHE_CHECK([[whether OpenSSL is modern enough]], [mhd_cv_openssl_ver_ok], + [ + CPPFLAGS="${CPPFLAGS_ac} ${OPENSSL_CPPFLAGS} ${user_CPPFLAGS}" + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include <openssl/opensslv.h> + ]], + [[ +#ifndef OPENSSL_VERSION_PREREQ +#error OPENSSL_VERSION_PREREQ is not defined. OpenSSL is too old. +choke me now +#endif +#if !OPENSSL_VERSION_PREREQ(3,0) +#error OpenSSL version is too old and not suitable. +choke me now +#endif + ]] + ) + ], + [[mhd_cv_openssl_ver_ok="yes"]], [[mhd_cv_openssl_ver_ok="no"]] + ) + CPPFLAGS="${CPPFLAGS_ac} ${user_CPPFLAGS}" + ] + ) + AS_VAR_IF([mhd_cv_openssl_ver_ok],["yes"],[:], + [ + have_openssl="no" + AS_VAR_IF([with_openssl],["yes"], + [AC_MSG_ERROR([cannot find usable OpenSSL library])] + ) + ] + ) + ] + ) + + AS_VAR_IF([have_openssl],["yes"], + [:], + [ + have_openssl="no" + have_openssl_pkgcfg="no" + AS_UNSET([OPENSSL_CPPFLAGS]) + AS_UNSET([OPENSSL_CFLAGS]) + AS_UNSET([OPENSSL_LDFLAGS]) + AS_UNSET([OPENSSL_LIBS]) + ] + ) -# -# End of large unindented block -# + AS_IF([test "x$have_gnutls" = "xyes" || test "x$have_openssl" = "xyes"], + [ + enable_https="yes" + multiple_tls="no" + MSG_TLS_BACKENDS="" + + AS_VAR_IF([have_gnutls],["yes"], + [ + AS_IF([test -n "${MSG_TLS_BACKENDS}"], + [ + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}, " + multiple_tls="yes" + ] + ) + AC_DEFINE([MHD_USE_GNUTLS],[1],[Define to '1' i][f GnuTLS library should be used]) + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}GnuTLS" + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_CPPFLAGS],[$GNUTLS_CPPFLAGS]) + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_LDFLAGS],[$GNUTLS_LDFLAGS]) + MHD_PREPEND_FLAG_TO_VAR([MHD_TLS_LIBDEPS],[$GNUTLS_LIBS]) + ] + ) + AS_VAR_IF([have_openssl],["yes"], + [ + AS_IF([test -n "${MSG_TLS_BACKENDS}"], + [ + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}, " + multiple_tls="yes" + ] + ) + AC_DEFINE([MHD_USE_OPENSSL],[1],[Define to '1' i][f OpenSSL library should be used]) + MSG_TLS_BACKENDS="${MSG_TLS_BACKENDS}OpenSSL" + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_CPPFLAGS],[$OPENSSL_CPPFLAGS]) + MHD_APPEND_FLAG_TO_VAR([MHD_TLS_LIB_LDFLAGS],[$OPENSSL_LDFLAGS]) + MHD_PREPEND_FLAG_TO_VAR([MHD_TLS_LIBDEPS],[$OPENSSL_LIBS]) + ] + ) + AS_VAR_IF([multiple_tls],["yes"], + [ + MSG_HTTPS="yes (multiple backends)" + AC_DEFINE([mhd_HAVE_SEVERAL_TLS_BACKENDS],[1],[Define to '1' if several TLS backend are linked]) + ], + [MSG_HTTPS="yes (single backend)"] + ) + AS_IF([test -z "$MSG_TLS_BACKENDS"],[AC_MSG_FAILURE([configure internal error: no TLS backends])]) + AC_DEFINE([MHD_ENABLE_HTTPS],[1],[Define to '1' i][f HTTPS protocol should be enabled]) + ], + [ + AS_VAR_IF([enable_https],["yes"], + [AC_MSG_ERROR([no suitable TLS lib found, HTTPS cannot be enabled])] + ) + enable_https="no" + MSG_HTTPS="no (no suitable TLS lib found)" + MSG_TLS_BACKENDS="none" + ] + ) + ], + [ + enable_https="no" + MSG_HTTPS="no (disabled)" + ] +) AC_MSG_CHECKING(whether to support HTTPS) +AS_VAR_IF([enable_https],["yes"], + [ + AS_CASE([$MSG_HTTPS],[yes*],[:], + [AC_MSG_FAILURE([configure internal error: wrong MSG_HTTPS])] + ) + ], + [ + enable_https="no" + MSG_TLS_BACKENDS="none" + AS_UNSET([MHD_TLS_LIB_CPPFLAGS]) + AS_UNSET([MHD_TLS_LIB_LDFLAGS]) + AS_UNSET([MHD_TLS_LIBDEPS]) + AS_CASE([$MSG_HTTPS],[no*],[:], + [AC_MSG_FAILURE([configure internal error: wrong MSG_HTTPS])] + ) + ] +) AC_MSG_RESULT([$MSG_HTTPS]) -AM_CONDITIONAL([HAVE_GNUTLS], [[test "x$have_gnutls" = "xyes"]]) -AM_CONDITIONAL([HAVE_GNUTLS_SNI], [[test "x$have_gnutls_sni" = "xyes"]]) -AM_CONDITIONAL([ENABLE_HTTPS], [test "x$enable_https" = "xyes"]) -AM_CONDITIONAL([HTTPS_REQUIRE_GCRYPT], [[test "x$enable_https" = "xyes" && test "x$mhd_cv_gcrypt_required" = "xyes"]]) +AM_CONDITIONAL([MHD_USE_GNUTLS], [[test "x$have_gnutls" = "xyes"]]) AC_SUBST([GNUTLS_CPPFLAGS]) -AC_SUBST([GNUTLS_CFLAGS]) AC_SUBST([GNUTLS_LDFLAGS]) AC_SUBST([GNUTLS_LIBS]) +AM_CONDITIONAL([MHD_USE_OPENSSL], [[test "x$have_openssl" = "xyes"]]) +AC_SUBST([OPENSSL_CPPFLAGS]) +AC_SUBST([OPENSSL_LDFLAGS]) +AC_SUBST([OPENSSL_LIBS]) +AM_CONDITIONAL([MHD_ENABLE_MULTITLS], [test "x$multiple_tls" = "xyes"]) +AM_CONDITIONAL([MHD_ENABLE_HTTPS], [test "x$enable_https" = "xyes"]) AS_VAR_IF([have_gnutls], ["yes"], [ @@ -5350,9 +5536,8 @@ AS_CASE([${enable_md5}],[yes|tlslib], [ AC_CACHE_CHECK([whether GnuTLS supports MD5 hashing],[mhd_cv_gnutls_md5], [ - CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}" CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}" save_LIBS="$LIBS" LIBS="${MHD_TLS_LIBDEPS} ${LIBS}" @@ -5473,9 +5658,8 @@ AS_CASE([${enable_sha256}],[yes|tlslib], [ AC_CACHE_CHECK([whether GnuTLS supports sha256 hashing],[mhd_cv_gnutls_sha256], [ - CFLAGS="${CFLAGS_ac} ${GNUTLS_CFLAGS} ${user_CFLAGS}" CPPFLAGS="${CPPFLAGS_ac} ${MHD_TLS_LIB_CPPFLAGS} ${user_CPPFLAGS}" - CFLAGS="${CFLAGS_ac} ${MHD_TLS_LIB_CFLAGS} ${user_CFLAGS}" + CFLAGS="${CFLAGS_ac} ${user_CFLAGS}" LDFLAGS="${LDFLAGS_ac} ${MHD_TLS_LIB_LDFLAGS} ${user_LDFLAGS}" save_LIBS="$LIBS" LIBS="${MHD_TLS_LIBDEPS} ${LIBS}" @@ -7102,7 +7286,6 @@ AC_SUBST(MHD_LIB_CFLAGS) AC_SUBST(MHD_LIB_LDFLAGS) AC_SUBST(MHD_LIBDEPS) AC_SUBST(MHD_TLS_LIB_CPPFLAGS) -AC_SUBST(MHD_TLS_LIB_CFLAGS) AC_SUBST(MHD_TLS_LIB_LDFLAGS) AC_SUBST(MHD_TLS_LIBDEPS) @@ -7224,53 +7407,73 @@ AS_UNSET([fin_lib_LDFLAGS]) AS_VAR_IF([os_is_windows], ["yes"], [os_ver_msg=" - Target W32 ver: ${mhd_w32_ver_msg}"], [AS_UNSET([[os_ver_msg]])]) - + Target W32 ver : ${mhd_w32_ver_msg}"], [AS_UNSET([[os_ver_msg]])]) + +AC_MSG_NOTICE([[${PACKAGE_NAME} ${PACKAGE_VERSION} Configuration Summary: + [ Target platform ] + Target directory : ${prefix} + Cross-compiling : ${cross_compiling} + Operating System : ${mhd_host_os}${os_ver_msg} + Threading lib : ${USE_THREADS} + Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select} -AC_MSG_NOTICE([GNU libmicrohttpd ${PACKAGE_VERSION} Configuration Summary: - Target directory: ${prefix} - Cross-compiling: ${cross_compiling} - Operating System: ${mhd_host_os}${os_ver_msg} - Threading lib: ${USE_THREADS} + [ Libary options and features ] Inter-thread comm: ${use_itc} - Shutdown of listening socket triggers select: ${mhd_cv_host_shtdwn_trgr_select} - select() support: ${enable_select} - poll() support: ${enable_poll=no} - epoll support: ${enable_epoll=no} - sendfile used: ${found_sendfile} - HTTPS support: ${MSG_HTTPS} - Logging support: ${enable_log_messages} - Verbose auto replies: ${enable_http_messages} - Cookie parser: ${enable_cookie} - POST parser: ${enable_postparser} - Basic auth.: ${enable_bauth} - Digest auth.: ${enable_dauth} + select() support : ${enable_select} + poll() support : ${enable_poll=no} + epoll support : ${enable_epoll=no} + sendfile() : ${found_sendfile} + HTTPS support : ${MSG_HTTPS} + TLS backends : ${MSG_TLS_BACKENDS} + Cookie parser : ${enable_cookie} + POST parser : ${enable_postparser} + Basic auth. : ${enable_bauth} + Digest auth. : ${enable_dauth} Digest auth. defaults: ${dauth_defs_MSG} - MD5: ${enable_md5_MSG} - SHA-256: ${enable_sha256_MSG} - SHA-512/256: ${enable_sha512_256_MSG} - HTTP "Upgrade": ${enable_httpupgrade} - Compact code: ${enable_compact_code} (${compact_code_MSG}) - Use thread names: ${enable_thread_names} + MD5 : ${enable_md5_MSG} + SHA-256 : ${enable_sha256_MSG} + SHA-512/256 : ${enable_sha512_256_MSG} + HTTP "Upgrade" : ${enable_httpupgrade} + Logging support : ${enable_log_messages} + Verbose auto replies: ${enable_http_messages} + + [ Code build options ] + Compact code : ${enable_compact_code} (${compact_code_MSG}) + Use thread names : ${enable_thread_names} Use debug asserts: ${use_asserts_MSG=no} - Use sanitizers: ${enabled_sanitizers:=no} - Build static lib: ${enable_static} - Build shared lib: ${enable_shared} - Build docs: ${enable_doc} - Build examples: ${enable_examples} - Build tools: ${enable_tools} + Use sanitizers : ${enabled_sanitizers:=no} + + [ Build items ] + Build static lib : ${enable_static} + Build shared lib : ${enable_shared} + Build docs : ${enable_doc} + Build examples : ${enable_examples} + Build tools : ${enable_tools} + + [ Test-suite settings ] Test with libcurl: ${MSG_CURL} - Heavy tests: ${use_heavy_tests_MSG} - Fuzzing tests: ${run_zzuf_tests_MSG=no} -]) + Heavy tests : ${use_heavy_tests_MSG} + Fuzzing tests : ${run_zzuf_tests_MSG=no} +]]) -AS_IF([test "x$enable_https" = "xyes"], - [AC_MSG_NOTICE([HTTPS subsystem configuration: - License : LGPL version 2.1 or any later version - ])], - [AC_MSG_NOTICE([ - License : LGPLv2.1+ or eCos - ])]) +licence_num="0" +AS_VAR_IF([have_gnutls],["yes"], + [AS_IF([test "2" -gt "$licence_num"],[licence_num="2"])] +) +AS_VAR_IF([have_openssl],["yes"], + [AS_IF([test "3" -gt "$licence_num"],[licence_num="3"])] +) +AS_CASE([$licence_num], + [0],[licence_descr="LGPLv2.1+ or eCos"], + [2],[licence_descr="LGPL version 2.1 or any later version"], + [3],[licence_descr="LGPL version 3.0 or any later version"], + [AC_MSG_ERROR(internal error: unexpected licence version)] +) + +AC_MSG_NOTICE([[ + [ Licence due to TLS backends used ] + Library licence : ${licence_descr} +]]) AS_IF([test "x$enable_bauth" != "xyes" || \ test "x$enable_dauth" != "xyes" || \ diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am @@ -24,7 +24,7 @@ noinst_PROGRAMS = if ENABLE_BAUTH noinst_PROGRAMS += -if ENABLE_HTTPS +if MHD_ENABLE_HTTPS noinst_PROGRAMS += endif endif diff --git a/m4/mhd_shutdown_socket_trigger.m4 b/m4/mhd_shutdown_socket_trigger.m4 @@ -109,14 +109,14 @@ AC_DEFUN([_MHD_RUN_CHECK_SOCKET_SHUTDOWN_TRIGGER],[dnl # endif typedef int MHD_Socket; # define MHD_INVALID_SOCKET (-1) -# define MHD_POSIX_SOCKETS 1 +# define MHD_SOCKETS_KIND_POSIX 1 #else # include <winsock2.h> # include <ws2tcpip.h> # include <windows.h> typedef SOCKET MHD_Socket; # define MHD_INVALID_SOCKET (INVALID_SOCKET) -# define MHD_WINSOCK_SOCKETS 1 +# define MHD_SOCKETS_KIND_WINSOCK 1 #endif #include <pthread.h> @@ -201,7 +201,7 @@ static MHD_Socket create_socket(void) static void close_socket(MHD_Socket fd) { -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX close(fd); #else closesocket(fd); @@ -333,7 +333,7 @@ static int test_it(void) static int init(void) { -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK WSADATA wsa_data; if (0 != WSAStartup(MAKEWORD(2, 2), &wsa_data) || MAKEWORD(2, 2) != wsa_data.wVersion) @@ -341,15 +341,15 @@ static int init(void) WSACleanup(); return 0; } -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ return 1; } static void cleanup(void) { -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK WSACleanup(); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ } int main(void) diff --git a/scripts/d_options.rec b/scripts/d_options.rec @@ -1,387 +0,0 @@ -# *-* mode: rec -*- -# -# MHD option registry -# -%rec: D_Options -# recutils supports only signed 32 bit values -%typedef: enum_value range 1 0x7FFFFFFF -%key: Name -%singular: Value -%type: Value enum_value -%auto: Value -%mandatory: Value -%mandatory: Comment -%allowed: Type Argument1 Description1 Member1 Argument2 Description2 Member2 Argument3 Description3 Member3 -%type: Name,Type,Argument1,Member1,Argument2,Member2,Argument3,Member3 line -%unique: Type Value Argument1 Description1 Member1 Argument2 Description2 Member2 Argument3 Description3 Member3 - -# MHD behaviour - -Name: WORK_MODE -Value: 40 -Comment: Set MHD work (threading and polling) mode. -+ Consider use of #MHD_DAEMON_OPTION_WM_EXTERNAL_PERIODIC(), #MHD_DAEMON_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL(), #MHD_DAEMON_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_EDGE(), #MHD_DAEMON_OPTION_WM_EXTERNAL_SINGLE_FD_WATCH(), #MHD_DAEMON_OPTION_WM_WORKER_THREADS() or #MHD_DAEMON_OPTION_WM_THREAD_PER_CONNECTION() instead of direct use of this parameter. -Argument1: struct MHD_WorkModeWithParam wmp -Description1: the object created by one of the next functions/macros: #MHD_WM_OPTION_EXTERNAL_PERIODIC(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_LEVEL(), #MHD_WM_OPTION_EXTERNAL_EVENT_LOOP_CB_EDGE(), #MHD_WM_OPTION_EXTERNAL_SINGLE_FD_WATCH(), #MHD_WM_OPTION_WORKER_THREADS(), #MHD_WM_OPTION_THREAD_PER_CONNECTION() - -Name: poll_syscall -Value: 41 -Comment: Select a sockets watch system call used for internal polling. -Argument1: enum MHD_SockPollSyscall els - -Name: log_callback -Value: 60 -Comment: Set a callback to use for logging -Type: struct MHD_DaemonOptionValueLog -Argument1: MHD_LoggingCallback log_cb -Description1: the callback to use for logging, -+ NULL to disable logging -Argument2: void *lob_cb_cls -Description2: the closure for the logging callback - -# Listen socket - -Name: bind_port -Value: 80 -Type: struct MHD_DaemonOptionValueBind -Comment: Bind to the given TCP port and address family. -+ -+ Does not work with #MHD_DAEMON_OPTION_BIND_SA() or #MHD_DAEMON_OPTION_LISTEN_SOCKET(). -+ -+ If no listen socket optins (#MHD_DAEMON_OPTION_BIND_PORT(), #MHD_DAEMON_OPTION_BIND_SA(), #MHD_DAEMON_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection. -Argument1: enum MHD_AddressFamily af -Description1: the address family to use, -+ the #MHD_AF_NONE to disable listen socket (the same effect as if this option is not used) -Argument2: uint_fast16_t port -Description2: port to use, 0 to let system assign any free port, -+ ignored if @a af is #MHD_AF_NONE - -Name: bind_sa -Value: 81 -Type: struct MHD_DaemonOptionValueSA -Comment: Bind to the given socket address. -+ -+ Does not work with #MHD_DAEMON_OPTION_BIND_PORT() or #MHD_DAEMON_OPTION_LISTEN_SOCKET(). -+ -+ If no listen socket optins (#MHD_DAEMON_OPTION_BIND_PORT(), #MHD_DAEMON_OPTION_BIND_SA(), #MHD_DAEMON_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection. -Argument1: size_t sa_len -Description1: the size of the socket address pointed by @a sa. -Argument2: const struct sockaddr *sa -Description2: the address to bind to; can be IPv4 (AF_INET), IPv6 (AF_INET6) or even a UNIX domain socket (AF_UNIX) - -Name: listen_socket -Value: 82 -Comment: Accept connections from the given socket. Socket -+ must be a TCP or UNIX domain (SOCK_STREAM) socket. -+ -+ Does not work with #MHD_DAEMON_OPTION_BIND_PORT() or #MHD_DAEMON_OPTION_BIND_SA(). -+ -+ If no listen socket optins (#MHD_DAEMON_OPTION_BIND_PORT(), #MHD_DAEMON_OPTION_BIND_SA(), #MHD_DAEMON_OPTION_LISTEN_SOCKET()) are used, MHD does not listen for incoming connection. -Argument1: MHD_socket listen_fd -Description1: the listen socket to use, ignored if set to #MHD_INVALID_SOCKET - -Name: listen addr reuse -Value: 100 -Comment: Select mode of reusing address:port listen address. -+ -+ Works only when #MHD_DAEMON_OPTION_BIND_PORT() or #MHD_DAEMON_OPTION_BIND_SA() are used. -Argument1: enum MHD_DaemonOptionBindType reuse_type - -Name: tcp_fastopen -Value: 101 -Type: struct MHD_DaemonOptionValueTFO -Comment: Configure TCP_FASTOPEN option, including setting a -+ custom @a queue_length. -+ -+ Note that having a larger queue size can cause resource exhaustion -+ attack as the TCP stack has to now allocate resources for the SYN -+ packet along with its DATA. -+ -+ Works only when #MHD_DAEMON_OPTION_BIND_PORT() or #MHD_DAEMON_OPTION_BIND_SA() are used. -Argument1: enum MHD_TCPFastOpenType option -Description1: the type use of of TCP FastOpen -Argument2: unsigned int queue_length -Description2: the length of the queue, zero to use system or MHD default, -+ silently ignored on platforms without support for custom queue size - -Name: LISTEN_BACKLOG -Value: 102 -Comment: Use the given backlog for the listen() call. -+ -+ Works only when #MHD_DAEMON_OPTION_BIND_PORT() or #MHD_DAEMON_OPTION_BIND_SA() are used. -Argument1: unsigned int backlog_size - -Name: sigpipe suppressed -Value: 103 -Type: enum MHD_Bool -Comment: Inform that SIGPIPE is suppressed or handled by application. -+ If suppressed/handled, MHD uses network functions that could generate SIGPIPE, like `sendfile()`. -+ Silently ignored when MHD creates internal threads as for them SIGPIPE is suppressed automatically. - -# TLS settings - -Name: TLS -Value: 120 -Comment: Enable TLS (HTTPS) and select TLS backend -Argument1: enum MHD_TlsBackend backend -Description1: the TLS backend to use, -+ #MHD_TLS_BACKEND_NONE for non-TLS (plain TCP) connections - -Name: tls_key_cert -Value: 121 -Comment: Provide TLS key and certificate data in-memory. -+ Works only if TLS mode is enabled. -Type: struct MHD_DaemonOptionValueTlsCert -Argument1: const char *mem_key -Description1: the private key loaded into memory (not a filename) -Argument2: const char *mem_cert -Description2: the certificate loaded into memory (not a filename) -Argument3: const char *mem_cert -Description3: the option passphrase phrase to decrypt the private key, -+ could be NULL is private does not need a password - -Name: tls_client_ca -Value: 122 -Comment: Provide the certificate of the certificate authority (CA) to be used by the MHD daemon for client authentication. -+ Works only if TLS mode is enabled. -Argument1: const char *mem_client_ca -Description1: the CA certificate in memory (not a filename) - -Name: tls_psk_callback -Value: 130 -Type: struct MHD_DaemonOptionValueTlsPskCB -Comment: Configure PSK to use for the TLS key exchange. -Argument1: MHD_PskServerCredentialsCallback psk_cb -Description1: the function to call to obtain pre-shared key -Argument2: void *psk_cb_cls -Description2: the closure for @a psk_cb - -Name: no alpn -Value: 140 -Type: enum MHD_Bool -Comment: Control ALPN for TLS connection. -+ Silently ignored for non-TLS. -+ By default ALPN is automatically used for TLS connections. - -# Connection handling - -Name: DEFAULT_TIMEOUT -Value: 160 -Comment: Specify inactivity timeout for connection. -+ When no activity for specified time on connection, it is closed automatically. -+ Use zero for no timeout, which is also the (unsafe!) default. -Argument1: unsigned int timeout -Description1: the in seconds, zero for no timeout - -Name: GLOBAL_CONNECTION_LIMIT -Value: 161 -Comment: Maximum number of (concurrent) network connections served by daemon -Argument1: unsigned int glob_limit - -Name: PER_IP_LIMIT -Value: 162 -Comment: Limit on the number of (concurrent) network connections made to the server from the same IP address. -+ Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected. -Argument1: unsigned int per_ip_limit - -Name: accept_policy -Value: 163 -Type: struct MHD_DaemonOptionValueAcceptPol -Comment: Set a policy callback that accepts/rejects connections based on the client's IP address. The callbeck function will be called before servicing any new incoming connection. -Argument1: MHD_AcceptPolicyCallback apc -Description1: the accept policy callback -Argument2: void *apc_cls -Description2: the closure for the callback - -# Requests processing - -Name: protocol_strict_level -Value: 200 -Type: struct MHD_DaemonOptionValueStrctLvl -Comment: Set how strictly MHD will enforce the HTTP protocol. -Argument1: enum MHD_ProtocolStrictLevel sl -Description1: the level of strictness -Argument2: enum MHD_UseStictLevel how -Description2: the way how to use the requested level - -Name: early_uri_logger -Value: 201 -Type: struct MHD_DaemonOptionValueUriCB -Comment: Set a callback to be called first for every request when the request line is received (before any parsing of the header). -+ This callback is the only way to get raw (unmodified) request URI as URI is parsed and modified by MHD in-place. -+ Mandatory URI modification may apply before this call, like binary zero replacement, as required by RFCs. -Argument1: MHD_EarlyUriLogCallback cb -Description1: the early URI callback -Argument2: void *cls -Description2: the closure for the callback - -Name: DISABLE_URI_QUERY_PLUS_AS_SPACE -Value: 202 -Type: enum MHD_Bool -Comment: Disable converting plus ('+') character to space in GET parameters (URI part after '?'). -+ Plus conversion is not required by HTTP RFCs, however it required by HTML specifications, see https://url.spec.whatwg.org/#application/x-www-form-urlencoded for details. -+ By default plus is converted to space in the query part of URI. - -# Responses processing - -Name: SUPPRESS_DATE_HEADER -Value: 240 -Type: enum MHD_Bool -Comment: Suppresse use of "Date:" header. -+ According to RFC should be suppressed only if the system has no RTC. -+ The "Date:" is not suppressed (the header is enabled) by default. - -Name: ENABLE_SHOUTCAST -Value: 241 -Type: enum MHD_Bool -Comment: Use SHOUTcast for responses. -+ This will cause *all* responses to begin with the SHOUTcast "ICY" line instead of "HTTP". - -# MHD limits - -Name: conn memory limit -Value: 280 -Type: size_t -Comment: Maximum memory size per connection. -+ Default is 32kb. -+ Values above 128kb are unlikely to result in much performance benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems. -+ The size should be large enough to fit all request headers (together with internal parsing information). - -Name: large pool size -Value: 281 -Type: size_t -Comment: The size of the shared memory pool for accamulated upload processing. -+ The same "large" pool is shared for all connections server by MHD and used when application requests avoiding of incremental upload processing to accamulate complete content upload before giving it to the application. -+ Default is 8Mb. -+ Can be set to zero to disable share pool. - -Name: stack size -Value: 282 -Type: size_t -Comment: Desired size of the stack for the threads started by MHD. -+ Use 0 for system default, which is also MHD default. -+ Works only with ##MHD_DAEMON_OPTION_WORKER_THREADS() or #MHD_DAEMON_OPTION_THREAD_PER_CONNECTION(). - -Name: fd_number_limit -Value: 283 -Comment: The the maximum FD value. -+ The limit is applied to all sockets used by MHD. -+ If listen socket FD is equal or higher that specified value, the daemon fail to start. -+ If new connection FD is equal or higher that specified value, the connection is rejected. -+ Useful if application uses select() for polling the sockets, system FD_SETSIZE is good value for this option in such case. -+ Does not work with ##MHD_DAEMON_OPTION_WORKER_THREADS() or #MHD_DAEMON_OPTION_THREAD_PER_CONNECTION(). -+ Does not work on W32 (WinSock sockets). -Argument1: MHD_socket max_fd - -# MHD optimisations - -Name: TURBO -Value: 320 -Type: enum MHD_Bool -Comment: Enable `turbo`. -+ Disables certain calls to `shutdown()`, enables aggressive non-blocking optimistic reads and other potentially unsafe optimisations. -+ Most effects only happen with internal threads with epoll. -+ The 'turbo' mode is not enabled (mode is disabled) by default. - -Name: DISABLE_THREAD_SAFETY -Value: 321 -Type: enum MHD_Bool -Comment: Disable some internal thread safety. -+ Indicates that MHD daemon will be used by application in single-threaded mode only. When this flag is set then application must call any MHD function only within a single thread. -+ This flag turns off some internal thread-safety and allows MHD making some of the internal optimisations suitable only for single-threaded environment. -+ Not compatible with any internal threads modes. -+ If MHD is compiled with custom configuration for embedded projects without threads support, this option is mandatory. -+ Thread safety is not disabled (safety is enabled) by default. - -Name: DISALLOW_UPGRADE -Value: 322 -Type: enum MHD_Bool -Comment: You need to set this option if you want to disable use of HTTP "Upgrade". -+ "Upgrade" may require usage of additional internal resources, which we can avoid providing if they will not be used. -+ You should only use this option if you do not use "Upgrade" functionality and need a generally minor boost in performance and resources saving. -+ The "Upgrade" is not disallowed ("upgrade" is allowed) by default. - -Name: DISALLOW_SUSPEND_RESUME -Value: 323 -Type: enum MHD_Bool -Comment: Disable #MHD_action_suspend() functionality. -+ -+ You should only use this function if you do not use suspend functionality and need a generally minor boost in performance. -+ The suspend is not disallowed (suspend is allowed) by default. - -# Notification callbacks - -Name: daemon_ready_callback -Value: 360 -Type: struct MHD_DaemonOptionValueReadyCB -Comment: Set a callback to be called for pre-start finalisation. -+ -+ The specified callback will be called one time, after network initialisation, TLS pre-initialisation, but before the start of the internal threads (if allowed) -Argument1: MHD_DaemonReadyCallback cb -Description1: the pre-start callback -Argument2: void *cb_cls -Description2: the closure for the callback - -Name: notify_connection -Value: 361 -Type: struct MHD_DaemonOptionValueNotifConnCB -Comment: Set a function that should be called whenever a connection is started or closed. -Argument1: MHD_NotifyConnectionCallback ncc -Description1: the callback for notifications -Argument2: void *cls -Description2: the closure for the callback - -Name: notify_stream -Value: 362 -Type: struct MHD_DaemonOptionValueNotifStreamCB -Comment: Register a function that should be called whenever a stream is started or closed. -+ For HTTP/1.1 this callback is called one time for every connection. -Argument1: MHD_NotifyStreamCallback nsc -Description1: the callback for notifications -Argument2: void *cls -Description2: the closure for the callback - -# Digest Auth settings - -Name: random entropy -Value: 400 -Type: struct MHD_DaemonOptionValueRand -Comment: Set strong random data to be used by MHD. -+ Currently the data is only needed for Digest Auth module. -+ The recommended size is between 8 and 32 bytes. Security can be lower for sizes less or equal four. -+ Sizes larger then 32 (or, probably, larger than 16 - debatable) will not increase the security. -Argument1: size_t buf_size -Description1: the size of the buffer -Argument2: const void *buf -Description2: the buffer with strong random data, the content will be copied by MHD - -Name: dauth_map_size -Value: 401 -Comment: Specify the size of the internal hash map array that tracks generated digest nonces usage. -+ When the size of the map is too small then need to handle concurrent DAuth requests, a lot of "stale nonce" results will be produced. -+ By default the size is 8 bytes (very small). -Argument1: size_t size -Description1: the size of the map array - -Name: dauth_nonce_bind_type -Value: 402 -Type: enum MHD_DaemonOptionValueDAuthBindNonce -Comment: Control the scope of validity of MHD-generated nonces. -+ This regulates how "nonces" are generated and how "nonces" are checked by #MHD_digest_auth_check() and similar functions. -+ This option allows bitwise OR combination of #MHD_DaemonOptionValueDAuthBindNonce values. -+ When this option is not used then default value is #MHD_DAEMON_OPTION_VALUE_DAUTH_BIND_NONCE_NONE. -Argument1: bind_type - -Name: dauth_def_nonce_timeout -Value: 403 -Comment: Default nonce timeout value (in seconds) used for Digest Auth. -+ Silently ignored if followed by zero value. -+ @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest() -Argument1: unsigned int timeout - -Name: dauth_def_max_nc -Value: 404 -Comment: Default maximum nc (nonce count) value used for Digest Auth. -+ Silently ignored if followed by zero value. -+ @see #MHD_digest_auth_check(), MHD_digest_auth_check_digest() -Argument1: uint_fast32_t max_nc diff --git a/scripts/d_options.sh b/scripts/d_options.sh @@ -1,524 +0,0 @@ -#!/bin/bash - -# This file is part of GNU libmicrohttpd -# Copyright (C) 2024 Karlson2k (Evgeny Grin), Christian Grothoff - -# 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 - - -export LC_ALL=C -export LANG=C - -if command -v recsel >/dev/null 2>&1 ; then : ; else - echo "Error: The command 'recsel' is missing. Please install recutils." >&2 - exit 1 -fi - -if command -v recset >/dev/null 2>&1 ; then : ; else - echo "Error: The command 'recset' is missing. Please install recutils." >&2 - exit 1 -fi - -if command -v recfmt >/dev/null 2>&1 ; then : ; else - echo "Error: The command 'recfmt' is missing. Please install recutils." >&2 - exit 1 -fi - -if (( 0 + 1 )) 2>/dev/null && test "$(( 2 + 2 ))" = "4" 2>/dev/null ; then : ; else - echo "Error: Built-in shell math is required" >&2 - exit 1 -fi - -if declare -a ARGS 2>/dev/null ; then : ; else - echo "Error: Indexed arrays support is required" >&2 - exit 1 -fi - -if [[ "false" ]] ; then : ; else - echo "Error: Compound command support is required" >&2 - exit 1 -fi - -if [[ $'\n' = ' -' ]] ; then : ; else - echo "Error: ANSI-C quoting support is required" >&2 - exit 1 -fi - -if [[ "abc" =~ 'b' ]] && [[ "xyz" =~ [x-z]{3} ]] ; then : ; else - echo "Error: Regular expression match support is required" >&2 - exit 1 -fi - -test_var="abc ABC Abc" -if test "${test_var^}" = "Abc ABC Abc" && test "${test_var^^}" = "ABC ABC ABC" && test "${test_var,,}" = "abc abc abc"; then : ; else - echo "Error: Shell upper- and lowercase variable conversion support required" >&2 - exit 1 -fi - -if test "${test_var// /_}" = "abc_ABC_Abc" ; then : ; else - echo "Error: Shell variable replacement conversion support required" >&2 - exit 1 -fi - -unset test_var - -if [[ "${0}" =~ (^|/|\\)'d_options.sh'$ ]]; then - options_type='daemon' -elif [[ "${0}" =~ (^|/|\\)'r_options.sh'$ ]]; then - options_type='response' -else - echo "Wrong name ('$0') of the script file" >&2 - exit 1 -fi - -# parameters -max_width=79 -if [[ "$options_type" = 'daemon' ]]; then - input_rec='d_options.rec' - rec_name='D_Options' - hdr_marker="Daemon" - one_char_opt_name='D' - short_opt_name="$options_type" -else - input_rec="r_options.rec" - rec_name='R_Options' - hdr_marker="Response" - one_char_opt_name='R' - short_opt_name="resp" -fi -tmp_rec_name="${rec_name}_preproc" -tmp_rec_file="${input_rec%.rec}_preproc.rec" - -# fixed strings -flat_arg_descr='the value of the parameter' - -err_exit() { - local msg="$1" - local err=$2 - - [[ -z $msg ]] && msg="Error!" - ( [[ -z $err ]] || (( err < 1 )) ) && err=2 - echo "$msg" >&2 - exit $err -} - -# cut string an newline character -cut_str_nl() { - local str="$1" - declare -g cut_str_nl_res='' - if [[ "$str" =~ $'\n' ]]; then - cut_str_nl_res="${str%%$'\n'*}" - return 0 - fi - return 1 -} - -# cut string to given length at word boundary if possible -# process embedded new line characters -cut_str_word () { - local str="$1" - local len=$2 - declare -g cut_str_word_res='' - [[ $len -le 0 ]] && return 1 - if cut_str_nl "${str:0:$(( len + 1 ))}"; then - cut_str_word_res="${cut_str_nl_res}" - cut_str_word_res="${cut_str_word_res% }" - return 0 - fi - if [[ ${#str} -le $len ]]; then - cut_str_word_res="${str}" - return 0 - fi - if [[ "${str:${len}:1}" = " " ]]; then - cut_str_word_res="${str:0:${len}}" - cut_str_word_res="${cut_str_word_res% }" - return 0 - fi - cut_str_word_res="${str:0:${len}}" - cut_str_word_res="${cut_str_word_res% *}" - cut_str_word_res="${cut_str_word_res% }" - return 0 -} - -format_doxy() { - local prefix1="$1" # first line prefix - local desc="$2" - local prefix2="$3" # prefix on all other lines - local width="$4" - local tmp_str - declare -g format_doxy_res='' - [[ -z $width ]] && width=$max_width - prefix1="${prefix1%"${prefix1##*[! ]}"} " # force single trailing space - [[ -z $prefix2 ]] && prefix2="$prefix1" - [[ ${#prefix1} -ge $width ]] && err_exit "Too long prefix ('${prefix1}') for width $width." - desc="${desc#"${desc%%[! ]*}"}" - desc="${desc%"${desc##*[! ]}"}" # trim desc - local width_r=$(( width - ${#prefix1} )) - local tmp_str="${prefix1//?/ }" # Space-only string with the same length - prefix2=" -${prefix2}${tmp_str:${#prefix2}}" - cut_str_word "$desc" $width_r || return 1 - format_doxy_res="${prefix1}${cut_str_word_res}" - format_doxy_res="${format_doxy_res% }" - desc="${desc:${#cut_str_word_res}}" - desc="${desc#"${desc%%[! ]*}"}" # trim leading spaces - desc="${desc#$'\n'}" # remove leading newline character - while [[ -n "$desc" ]]; do - cut_str_word "$desc" $width_r || return 1 - tmp_str="${prefix2}${cut_str_word_res}" - format_doxy_res+="${tmp_str% }" - desc="${desc:${#cut_str_word_res}}" - desc="${desc#"${desc%%[! ]*}"}" # trim leading spaces - desc="${desc#$'\n'}" # remove leading newline character - done - return 0 -} - -def_name_for_type() { - case $1 in - 'enum MHD_Bool') echo -n "bool_val";; - 'unsigned int') echo -n "uint_val";; - 'uint_fast64_t') echo -n "uint64_val";; - 'uint_fast32_t') echo -n "uint32_val";; - 'uint_fast16_t') echo -n "uint16_val";; - 'size_t') echo -n "sizet_val";; - *) local tp="${1,,}" && printf '%s' "${tp// /_}_val";; - esac -} - -capitalise_first() { - local first_char="${1:0:1}" - printf '%s' "${first_char^^}${1:1}" -} - -echo "Trimming lines in '$input_rec'..." -${SED-sed} -E -e 's/ +$//' -i "$input_rec" || err_exit -echo "OK" - -echo "Checking '$input_rec'..." -recfix --check "$input_rec" || exit 3 -echo "OK" - -cat << _EOF_ > "$tmp_rec_file" -%rec: ${tmp_rec_name} -%key: EName -%mandatory: Value -%mandatory: Name -%type: Value int -%sort: Value -%singular: EName UName SName Value - -_EOF_ - -echo "Processing input file..." -for N in $(recsel -t "${rec_name}" -R Value "$input_rec") -do - NAME=$(recsel -t "${rec_name}" -P Name -e "Value = $N" "$input_rec") - if [[ -z $NAME ]]; then - echo "The 'Name' field is empty for 'Value=$N'" >&2 - exit 2 - fi - echo -n '.' - COMMENT=$(recsel -t "${rec_name}" -P Comment -e "Value = $N" "$input_rec") - if [[ -z $COMMENT ]]; then - echo "The 'Comment' field is empty for '$NAME' ('Value=$N')" >&2 - exit 2 - fi - TYPE=$(recsel -t "${rec_name}" -P Type -e "Value = $N" "$input_rec") - EComment="" # The initial part of doxy comment for the enum value - EName="" # The name of the enum value - UName="" # The name of the union member - UType="" # The type of the union member - UTypeSp='' # The type of the union member with space at the end for non-pointers - SComment="" # The doxy comment for the set macro/function - SName="" # The name of the set macro/function - MArguments="" # The arguments for the macro - CLBody="" # The Compound Literal body (for the set macro) - SFArguments="" # The arguments for the static function - SFBody="" # The static function body - StBody='' # The data struct body (if any) - - nested='maybe' # The option has nested struct parameters ('yes'/'no'/'maybe') - - clean_name="${NAME//_/ }" - clean_name="${clean_name,,}" # Lowercase space-delimited - - echo -n "$N: ${clean_name// /_}" - - EName="${clean_name^^}" - EName="MHD_${one_char_opt_name^^}_O_${EName// /_}" # Uppercase '_'-joined - - UName="v_${clean_name// /_}" # lowercase '_'-joined - - SName="${clean_name^^}" - SName="MHD_${one_char_opt_name^^}_OPTION_${SName// /_}" # Uppercase '_'-joined - - format_doxy ' * ' "$COMMENT" || err_exit - EComment="$format_doxy_res" - - format_doxy ' * ' "$COMMENT" || err_exit - SComment="$format_doxy_res" - - # Read option parameters - ARGS=( ) - DESCRS=( ) - MEMBERS=( ) - M=1 - while - ARGM=$(recsel -t "${rec_name}" -P Argument${M} -e "Value = $N" "$input_rec") - [[ -n $ARGM ]] - do - ARGS[$M]="$ARGM" - DESCRS[$M]=$(recsel -t "${rec_name}" -P Description${M} -e "Value = $N" "$input_rec") - MEMBERS[$M]=$(recsel -t "${rec_name}" -P Member${M} -e "Value = $N" "$input_rec") - (( M++ )) - echo -n '.' - done - - # Basic data checks - (( M - 1 == ${#ARGS[@]} )) || err_exit - - if [[ ${#ARGS[@]} -eq 0 ]]; then - [[ -z $TYPE ]] && err_exit "No 'Argument1' is specified for '$NAME' ('Value=$N') without 'Type'" >&2 - nested='no' - ARGS[1]='' - DESCRS[1]="$flat_arg_descr" - MEMBERS[1]='' - elif [[ ${#ARGS[@]} -eq 1 ]]; then - nested='no' - else - nested='yes' - [[ -z $TYPE ]] && err_exit "No 'Type' is specified for non-flat (nested, with multiple parameters) '$NAME' ('Value=$N')" >&2 - fi - - # Process option parameters - for (( M=1 ; M <= ${#ARGS[@]} ; M++ )) ; do - - arg_name='' # The name of the current argument - arg_type='' # The type of the data of the current argument - arg_descr='' # The description of the current argument - nest_member='' # The name of the member of the nested structure - [[ "${ARGS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Argument${M}' value '${ARGS[$M]}' for '$NAME' ('Value=$N') is not trimmed" - [[ "${DESCRS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Description${M}' value '${DESCRS[$M]}' for '$NAME' ('Value=$N') is not trimmed" - [[ "${MEMBERS[$M]}" =~ (^' '|' '$) ]] && err_exit "'Member${M}' value '${MEMBERS[$M]}' for '$NAME' ('Value=$N') is not trimmed" - # Pre-process parameters data - if [[ -n ${ARGS[$M]} ]]; then - arg_name="${ARGS[$M]##* }" - arg_name="${arg_name#\*}" - arg_type="${ARGS[$M]%${arg_name}}" - arg_type="${arg_type% }" - else - if [[ $nested = 'yes' ]]; then - err_exit "Empty or no 'Argument${M}' ('$arg_type') for '$NAME' ('Value=$N')" - else - [[ -z $TYPE ]] && err_exit "No 'Argument1' is specified for '$NAME' ('Value=$N') without 'Type'" >&2 - arg_name="$(def_name_for_type "$TYPE")" - arg_type="$TYPE" - fi - fi - arg_descr="${DESCRS[$M]}" - nest_membr="${MEMBERS[$M]}" - - [[ -z $arg_name ]] && err_exit # Should not happen - if [[ $nested = 'yes' ]]; then - # non-flat, nested - [[ -z $arg_type ]] && err_exit "No argument type in 'Argument${M}' ('${ARGS[$M]}') for $NAME ('Value=$N')" - [[ $TYPE = $arg_type ]] && \ - err_exit "The same 'Type' and type for in 'Argument${M}' ('$arg_type') used for non-flat (nested) '$NAME' ('Value=$N')" - [[ -z $arg_descr ]] && \ - err_exit "Empty or no 'Description${M}' for argument '${ARGS[$M]}' for non-flat (nested) '$NAME' ('Value=$N')" - if [[ "$arg_name" = "$nest_membr" ]]; then - echo "The name for 'Argument${M}' ('${ARGS[$M]}') is the same as the 'Member${M}' ('$nest_membr') for non-flat (nested) '$NAME' ('Value=$N')" >&2 - nest_membr="v_${nest_membr}" - echo "Auto-correcting the struct member name to '$nest_membr' to avoid wrong macro expansion" >&2 - fi - else - # flat, non-nested - if [[ -z $arg_type ]]; then - if [[ -z $TYPE ]]; then - err_exit "Both 'Type' and type for in 'Argument${M}' ('${ARGS[$M]}') are empty for '$NAME' ('Value=$N')" - else - arg_type="$TYPE" - fi - else - if [[ -z $TYPE ]]; then - TYPE="$arg_type" - elif [[ $TYPE != $arg_type ]]; then - err_exit "Different 'Type' ('$TYPE') and type for in 'Argument${M}' ('$arg_type') used for '$NAME' ('Value=$N')" - fi - fi - [[ -z $arg_descr ]] && arg_descr="$flat_arg_descr" - [[ -n $nest_membr ]] && \ - err_exit "'Member${M}' is provided for non-nested (flat) '$NAME' ('Value=$N')" - fi - - [[ "$arg_type" =~ \*$ ]] || arg_type+=' ' # Position '*' correctly - [[ "$arg_name" = "${UName}" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the union member name ('${UName}'). Macro would not work." - [[ "$arg_name" = "opt" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the option struct member name ('opt'). Macro would not work." - [[ "$arg_name" = "val" ]] && err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') conflicts with the option struct member name ('val'). Macro would not work." - [[ "${arg_name,,}" = "${arg_name}" ]] || err_exit "The name ('$arg_name') of the argument 'Argument${M}' ('${ARGS[$M]}') for '$NAME' ('Value=$N') has capital letter(s)" - [[ $nested = 'yes' ]] && [[ -z $nest_membr ]] && nest_membr="v_${arg_name}" - [[ "${#arg_name}" -ge 15 ]] && echo "Warning: too long (${#arg_name} chars) parameter name '${arg_name}'." >&2 - - [[ $M -gt 1 ]] && [[ $nested = 'no' ]] && err_exit - - # Use parameters data - - format_doxy ' * @param '"$arg_name " "$arg_descr" ' * '|| err_exit - SComment+=$'\n'"$format_doxy_res" - - [[ $M -gt 1 ]] && MArguments+=',' - MArguments+="$arg_name" - - if [[ $nested = 'yes' ]]; then - [[ $M -gt 1 ]] && SFArguments+=',' - SFArguments+=$'\n'" ${arg_type}$arg_name" - else - if (( ${#SName} + ${#arg_type} + ${#arg_name} <= $max_width )); then - SFArguments+="${arg_type}$arg_name" - else - SFArguments+=$'\n'" ${arg_type}$arg_name" - fi - fi - - #[[ $M -gt 1 ]] && CLBody+=', \'$'\n'" " - [[ $M -gt 1 ]] && CLBody+=', \##removeme##'$'\n'" " # '##removeme##' is a workaround for requtils bug - CLBody+=".val.${UName}" - [[ $nested = 'yes' ]] && CLBody+=".${nest_membr}" - CLBody+=" = ($arg_name)" - - [[ $M -gt 1 ]] && SFBody+=$'\n'" " - SFBody+="opt_val.val.${UName}" - [[ $nested = 'yes' ]] && SFBody+=".${nest_membr}" - SFBody+=" = ${arg_name};" - - if [[ $nested = 'yes' ]] && [[ "$TYPE" =~ ^'struct ' ]]; then - StBody+=$'\n' - StBody+=" /**"$'\n' - format_doxy ' * ' "$(capitalise_first "$arg_descr")" || err_exit - StBody+="$format_doxy_res"$'\n'" */"$'\n' - StBody+=" ${arg_type}$nest_membr;" - fi - echo -n '.' - done - - UType="$TYPE" - if [[ $nested = 'yes' ]] && [[ "$TYPE" =~ ^'struct ' ]]; then - need_struct_decl='yes' - else - need_struct_decl='no' - fi - [[ "$UType" =~ \*$ ]] && UTypeSp="$UType" || UTypeSp="$UType " # Position '*' correctly - - recins -t "${tmp_rec_name}" \ - -f Name -v "$NAME" \ - -f Value -v "$N" \ - -f hdr_marker -v "$hdr_marker" \ - -f EComment -v "$EComment" \ - -f EName -v "$EName" \ - -f UName -v "$UName" \ - -f UType -v "$UType" \ - -f UTypeSp -v "$UTypeSp" \ - -f SComment -v "$SComment" \ - -f SName -v "$SName" \ - -f MArguments -v "$MArguments" \ - -f CLBody -v "$CLBody" \ - -f SFArguments -v "$SFArguments" \ - -f SFBody -v "$SFBody" \ - -f StBody -v "$StBody" \ - --verbose "$tmp_rec_file" || err_exit - echo '.' -done -echo "finished." - -echo "Updating header file..." -header_name='microhttpd2.h' -start_of_marker=" = MHD ${hdr_marker} Option " -end_of_start_marker=' below are generated automatically = ' -end_of_end_marker=' above are generated automatically = ' -I=0 - -cp "../src/include/${header_name}" "./${header_name}" || err_exit - -middle_of_marker='enum values' -echo "${middle_of_marker}..." -in_file="${header_name}" -out_file="${header_name%.h}_tmp$((++I)).h" -recfmt -f options_enum.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit -middle_of_marker='enum values' -start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit -${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h -} -/'"$end_marker"'/p -/'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit - -middle_of_marker='structures' -echo "${middle_of_marker}..." -in_file="${out_file}" -out_file="${header_name%.h}_tmp$((++I)).h" -recsel -e "StBody != ''" "$tmp_rec_file" | recfmt -f options_struct.template > "header_insert${I}.h" || err_exit -start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit -${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h -} -/'"$end_marker"'/p -/'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit - -middle_of_marker='union members' -echo "${middle_of_marker}..." -in_file="${out_file}" -out_file="${header_name%.h}_tmp$((++I)).h" -recfmt -f options_union.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit -start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit -${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h -} -/'"$end_marker"'/p -/'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit - -middle_of_marker='macros' -echo "${middle_of_marker}..." -in_file="${out_file}" -out_file="${header_name%.h}_tmp$((++I)).h" -recfmt -f options_macro.template < "$tmp_rec_file" | ${SED-sed} -e 's/##removeme##//g' - > "header_insert${I}.h" || err_exit -start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit -${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h -} -/'"$end_marker"'/p -/'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit - -middle_of_marker='static functions' -echo "${middle_of_marker}..." -in_file="${out_file}" -out_file="${header_name%.h}_tmp$((++I)).h" -recfmt -f options_func.template < "$tmp_rec_file" > "header_insert${I}.h" || err_exit -start_marker="${start_of_marker}${middle_of_marker}${end_of_start_marker}" && end_marker="${start_of_marker}${middle_of_marker}${end_of_end_marker}" || err_exit -${SED-sed} -e '/'"$start_marker"'/{p; r header_insert'"$I"'.h -} -/'"$end_marker"'/p -/'"$start_marker"'/,/'"$end_marker"'/d' "${in_file}" > "${out_file}" || err_exit - -echo "finished." - -if diff "../src/include/${header_name}" "${out_file}" > /dev/null; then - echo "The updated header '${header_name}' content is equal to the previous content, the file remains the same." -else - mv "${out_file}" "../src/include/${header_name}" || err_exit - echo "The header '${header_name}' has been updated with the new content." -fi - -echo "Cleanup..." -rm -f "$tmp_rec_file" ${header_name%.h}_tmp?.h header_insert?.h -echo "completed." diff --git a/scripts/options_enum.template b/scripts/options_enum.template @@ -1,7 +0,0 @@ - /** -{{EComment}} - * The parameter value must be placed to the - * @a {{UName}} member. - */ - {{EName}} = {{Value}} - , diff --git a/scripts/options_func.template b/scripts/options_func.template @@ -1,17 +0,0 @@ -/** -{{SComment}} - * @return the object of struct MHD_{{hdr_marker}}OptionAndValue with the requested - * values - */ -static MHD_INLINE struct MHD_{{hdr_marker}}OptionAndValue -{{SName}} ({{SFArguments}}) -{ - struct MHD_{{hdr_marker}}OptionAndValue opt_val; - - opt_val.opt = {{EName}}; - {{SFBody}} - - return opt_val; -} - - diff --git a/scripts/options_macro.template b/scripts/options_macro.template @@ -1,14 +0,0 @@ -/** -{{SComment}} - * @return the object of struct MHD_{{hdr_marker}}OptionAndValue with the requested - * values - */ -# define {{SName}}({{MArguments}}) \ - MHD_NOWARN_COMPOUND_LITERALS_ \ - (const struct MHD_{{hdr_marker}}OptionAndValue) \ - { \ - .opt = ({{EName}}), \ - {{CLBody}} \ - } \ - MHD_RESTORE_WARN_COMPOUND_LITERALS_ - diff --git a/scripts/options_struct.template b/scripts/options_struct.template @@ -1,7 +0,0 @@ -/** - * Data for #MHD_D_O_{{EName}} - */ -{{UType}} v_{{UName}} -{{{StBody}} -}; - diff --git a/scripts/options_union.template b/scripts/options_union.template @@ -1,4 +0,0 @@ - /** - * Value for #{{EName}} - */ - {{UTypeSp}}{{UName}}; diff --git a/scripts/r_options.rec b/scripts/r_options.rec @@ -1,91 +0,0 @@ -# *-* mode: rec -*- -# -# response option registry -# -%rec: R_Options -# recutils supports only signed 32 bit values -%typedef: enum_value range 1 0x7FFFFFFF -%key: Name -%singular: Value -%type: Value enum_value -%auto: Value -%mandatory: Value -%mandatory: Comment -%allowed: Type Argument1 Description1 Member1 Argument2 Description2 Member2 Argument3 Description3 Member3 -%type: Name,Type,Argument1,Member1,Argument2,Member2,Argument3,Member3 line -%unique: Type Value Argument1 Description1 Member1 Argument2 Description2 Member2 Argument3 Description3 Member3 - -# General properties - -Name: REUSABLE -Value: 20 -Type: enum MHD_Bool -Comment: Make the response object re-usable. -+ The response will not be consumed by MHD_action_from_response() and must be destroyed by MHD_response_destroy(). -+ Useful if the same response is often used to reply. - -# Content control - -Name: HEAD_ONLY_RESPONSE -Value: 40 -Type: enum MHD_Bool -Comment: Enable special processing of the response as body-less (with undefined body size). No automatic "Content-Length" or "Transfer-Encoding: chunked" headers are added when the response is used with #MHD_HTTP_NOT_MODIFIED code or to respond to HEAD request. -+ The flag also allow to set arbitrary "Content-Length" by #MHD_response_add_header() function. -+ This flag value can be used only with responses created without body (zero-size body). -+ Responses with this flag enabled cannot be used in situations where reply body must be sent to the client. -+ This flag is primarily intended to be used when automatic "Content-Length" header is undesirable in response to HEAD requests. - -Name: CHUNKED_ENC -Value: 41 -Type: enum MHD_Bool -Comment: Force use of chunked encoding even if the response content size is known. -+ Ignored when the reply cannot have body/content. - -# Connection control - -Name: CONN_CLOSE -Value: 60 -Type: enum MHD_Bool -Comment: Force close connection after sending the response, prevents keep-alive connections and adds "Connection: close" header. - -# Compatibility settings - -Name: HTTP_1_0_COMPATIBLE_STRIC -Value: 80 -Type: enum MHD_Bool -Comment: Only respond in conservative (dumb) HTTP/1.0-compatible mode. -+ Response still use HTTP/1.1 version in header, but always close the connection after sending the response and do not use chunked encoding for the response. -+ You can also set the #MHD_R_O_HTTP_1_0_SERVER flag to force HTTP/1.0 version in the response. -+ Responses are still compatible with HTTP/1.1. -+ This option can be used to communicate with some broken client, which does not implement HTTP/1.1 features, but advertises HTTP/1.1 support. - -Name: HTTP_1_0_SERVER -Value: 81 -Type: enum MHD_Bool -Comment: Only respond in HTTP/1.0-mode. -+ Contrary to the #MHD_R_O_HTTP_1_0_COMPATIBLE_STRICT flag, the response's HTTP version will always be set to 1.0 and keep-alive connections will be used if explicitly requested by the client. -+ The "Connection:" header will be added for both "close" and "keep-alive" connections. -+ Chunked encoding will not be used for the response. -+ Due to backward compatibility, responses still can be used with HTTP/1.1 clients. -+ This option can be used to emulate HTTP/1.0 server (for response part only as chunked encoding in requests (if any) is processed by MHD). -+ With this option HTTP/1.0 server is emulated (with support for "keep-alive" connections). - -# Violate HTTP and/or RFCs - -Name: INSANITY_HEADER_CONTENT_LENGTH -Value: 100 -Type: enum MHD_Bool -Comment: Disable sanity check preventing clients from manually setting the HTTP content length option. -+ Allow to set several "Content-Length" headers. These headers will be used even with replies without body. - -# Callbacks - -Name: termination_callback -Value: 121 -Type: struct MHD_ResponeOptionValueTermCB -Comment: Set a function to be called once MHD is finished with the request. -Argument1: MHD_RequestTerminationCallback term_cb -Description1: the function to call, -+ NULL to not use the callback -Argument2: void *term_cb_cls -Description2: the closure for the callback diff --git a/scripts/r_options.sh b/scripts/r_options.sh @@ -1,21 +0,0 @@ -#!/bin/bash - -# This file is part of GNU libmicrohttpd -# Copyright (C) 2024 Karlson2k (Evgeny Grin) - -# 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 - -source ./d_options.sh diff --git a/src/incl_priv/mhd_sys_options.h b/src/incl_priv/mhd_sys_options.h @@ -326,12 +326,19 @@ #endif /* HAVE_LINUX_SENDFILE || HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */ -#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) || defined(mhd_THREADS_KIND_W32) # ifndef MHD_USE_THREADS # define MHD_USE_THREADS 1 # endif -#endif /* MHD_USE_POSIX_THREADS || MHD_USE_W32_THREADS */ +#endif /* mhd_THREADS_KIND_POSIX || mhd_THREADS_KIND_W32 */ +#if defined(_WIN32) && ! defined(__CYGWIN__) +/** + * Defined if the platform is native W32. + * Not define on Cygwin. + */ +# define mhd_W32_NATIVE 1 +#endif /** * Macro to drop 'const' qualifier from pointer. * Try to avoid compiler warning. @@ -341,10 +348,30 @@ */ #ifdef HAVE_UINTPTR_T # define mhd_DROP_CONST(ptr) ((void *) ((uintptr_t) ((const void *) (ptr)))) +#elif defined(HAVE_INTPTR_T) +# define mhd_DROP_CONST(ptr) ((void *) ((intptr_t) ((const void *) (ptr)))) #else # define mhd_DROP_CONST(ptr) ((void *) ((const void *) (ptr))) #endif +/** + * Cast signed integer to pointer + */ +#if defined(HAVE_INTPTR_T) +# define mhd_INT_TO_PTR(i) ((void *) ((intptr_t) (i))) +#else +# define mhd_INT_TO_PTR(i) ((void *) (i)) +#endif + +/** + * Cast unsigned integer to pointer + */ +#if defined(HAVE_UINTPTR_T) +# define mhd_UINT_TO_PTR(i) ((void *) ((uintptr_t) (i))) +#else +# define mhd_UINT_TO_PTR(i) ((void *) (i)) +#endif + #if defined(OS390) #define _OPEN_THREADS @@ -381,10 +408,12 @@ #define RESTRICT __restrict__ #endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */ -#if defined(LINUX) && (defined(HAVE_SENDFILE64) || defined(HAVE_LSEEK64)) && \ - ! defined(_LARGEFILE64_SOURCE) +#if defined(__linux__) && \ + (defined(HAVE_SENDFILE64) || defined(HAVE_LSEEK64) || defined(HAVE_PREAD64)) +# if ! defined(_LARGEFILE64_SOURCE) /* On Linux, special macro is required to enable definitions of some xxx64 functions */ -#define _LARGEFILE64_SOURCE 1 +# define _LARGEFILE64_SOURCE 1 +# endif #endif #ifdef HAVE_C11_GMTIME_S @@ -392,11 +421,14 @@ #define __STDC_WANT_LIB_EXT1__ 1 #endif /* HAVE_C11_GMTIME_S */ -#if ! defined(_DEBUG) && ! defined(NDEBUG) +/* Ensure that exactly one of the two macros is always defined */ +#if defined(NDEBUG) && defined(_DEBUG) +#error Both _DEBUG and NDEBUG are defined +#elif ! defined(_DEBUG) && ! defined(NDEBUG) # ifndef DEBUG /* Used by some toolchains */ # define NDEBUG 1 /* Use NDEBUG by default */ # else /* DEBUG */ -# define _DEBUG 1 /* Non-standart macro */ +# define _DEBUG 1 /* Non-standard macro */ # endif /* DEBUG */ #endif /* !_DEBUG && !NDEBUG */ @@ -464,6 +496,17 @@ __pragma(runtime_checks("c", restore)) #endif /* _MSC_FULL_VER */ +#ifdef _DEBUG +# ifndef MHD_NO_TLS_DEBUG_MESSAGES +# ifndef mhd_USE_TLS_DEBUG_MESSAGES +/** + * Enable debugging output on TLS library (if possible) + */ +# define mhd_USE_TLS_DEBUG_MESSAGES 1 +# endif +# endif +#endif + /* Un-define some HAVE_DECL_* macro if they equal zero. This should allow safely use #ifdef in the code. Define HAS_DECL_* macros only if matching HAVE_DECL_* macro diff --git a/src/include/d_options.rec b/src/include/d_options.rec @@ -149,18 +149,67 @@ Argument1: enum MHD_TlsBackend backend Description1: the TLS backend to use, + #MHD_TLS_BACKEND_NONE for non-TLS (plain TCP) connections -Name: tls_key_cert +Name: tls_cert_key Value: 121 Comment: Provide TLS key and certificate data in-memory. + Works only if TLS mode is enabled. Type: struct MHD_DaemonOptionValueTlsCert -Argument1: const char *mem_key -Description1: the private key loaded into memory (not a filename) -Argument2: const char *mem_cert -Description2: the certificate loaded into memory (not a filename) +Argument1: /* const */ char *mem_cert +Description1: The X.509 certificates chain in PEM format loaded into memory (not a filename). ++ The first certificate must be the server certificate, following by the chain of signing ++ certificates up to (but not including) CA root certificate. +Argument2: const char *mem_key +Description2: the private key in PEM format loaded into memory (not a filename) Argument3: const char *mem_pass Description3: the option passphrase phrase to decrypt the private key, -+ could be NULL is private does not need a password ++ could be NULL if private key does not need a password +CustomSetter: /* custom setter */ ++ if ((NULL == option->val.tls_cert_key.v_mem_cert) ++ || (NULL == option->val.tls_cert_key.v_mem_key)) ++ return MHD_SC_TLS_CONF_BAD_CERT; ++ else ++ { ++ size_t cert_size; ++ size_t key_size; ++ size_t pass_size; ++ cert_size = strlen (option->val.tls_cert_key.v_mem_cert); ++ key_size = strlen (option->val.tls_cert_key.v_mem_key); ++ if ((0 == cert_size) ++ || (0 == key_size)) ++ return MHD_SC_TLS_CONF_BAD_CERT; ++ ++cert_size; /* Space for zero-termination */ ++ ++key_size; /* Space for zero-termination */ ++ if (NULL != option->val.tls_cert_key.v_mem_pass) ++ pass_size = strlen (option->val.tls_cert_key.v_mem_pass); ++ else ++ pass_size = 0; ++ if (0 != pass_size) ++ ++pass_size; /* Space for zero-termination */ ++ if (NULL != settings->tls_cert_key.v_mem_cert) ++ free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!! ++ settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size); ++ if (NULL == settings->tls_cert_key.v_mem_cert) ++ return MHD_SC_DAEMON_MALLOC_FAILURE; ++ memcpy (settings->tls_cert_key.v_mem_cert, ++ option->val.tls_cert_key.v_mem_cert, ++ cert_size); ++ memcpy (settings->tls_cert_key.v_mem_cert + cert_size, ++ option->val.tls_cert_key.v_mem_key, ++ key_size); ++ settings->tls_cert_key.v_mem_key = ++ settings->tls_cert_key.v_mem_cert + cert_size; ++ if (0 != pass_size) ++ { ++ memcpy (settings->tls_cert_key.v_mem_cert + cert_size + key_size, ++ option->val.tls_cert_key.v_mem_pass, ++ pass_size); ++ settings->tls_cert_key.v_mem_pass = ++ settings->tls_cert_key.v_mem_cert + cert_size + key_size; ++ } ++ else ++ settings->tls_cert_key.v_mem_pass = NULL; ++ } + Name: tls_client_ca Value: 122 diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -459,6 +459,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode , /** * Failed to allocate memory for the daemon resources. + * TODO: combine similar error codes for daemon */ MHD_SC_DAEMON_MALLOC_FAILURE = 30081 , @@ -714,12 +715,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_TLS_DISABLED = 50000 , /** - * The application attempted to setup TLS parameters before - * enabling TLS. - */ - MHD_SC_TLS_BACKEND_UNINITIALIZED = 50003 - , - /** * The selected TLS backend does not yet support this operation. */ MHD_SC_TLS_BACKEND_OPERATION_UNSUPPORTED = 50004 @@ -1158,6 +1153,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_REPLY_POOL_ALLOCATION_FAILURE = 50231 , /** + * Failed to read the file for file-backed response. + */ + MHD_SC_REPLY_FILE_READ_ERROR = 50232 + , + /** * Failed to allocate memory in connection's pool for the reply. */ MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250 @@ -1282,11 +1282,36 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED = 50840 , /** + * Global initialisation of MHD library failed + */ + MHD_SC_LIB_INIT_GLOBAL_FAILED = 51000 + , + /** + * Failed to initialise TLS context for the daemon + */ + MHD_SC_TLS_DAEMON_INIT_FAILED = 51200 + , + /** + * Failed to initialise TLS context for the new connection + */ + MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201 + , + /** + * Warning about TLS backend configuration + */ + MHD_SC_TLS_LIB_CONF_WARNING = 51202 + , + /** + * Failed to perform TLS handshake + */ + MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220 + , + /** * Something wrong in the internal MHD logic. * This error should be never returned if MHD works as expected. * If this code is ever returned, please report to MHD maintainers. */ - MHD_SC_INTERNAL_ERROR = 51000 + MHD_SC_INTERNAL_ERROR = 59900 , /* 60000-level errors are because the application @@ -1305,17 +1330,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001 , /** - * The application requested an unsupported TLS backend to be used. - */ - MHD_SC_TLS_BACKEND_UNSUPPORTED = 60003 - , - /** - * The application requested a TLS cipher suite which is not - * supported by the selected backend. - */ - MHD_SC_TLS_CIPHERS_INVALID = 60004 - , - /** * MHD is closing a connection because the application * logic to generate the response data failed. */ @@ -1386,6 +1400,34 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016 , /** + * The application requested an unsupported TLS backend to be used. + */ + MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020 + , + /** + * The application attempted to setup TLS parameters before + * enabling TLS. + */ + MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021 + , + /** + * The application requested a TLS backend which cannot be used due + * to missing TLS dynamic library or backend initialisation problem. + */ + MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022 + , + /** + * Provided TLS certificate and/or private key are incorrect + */ + MHD_SC_TLS_CONF_BAD_CERT = 60023 + , + /** + * The application requested a daemon setting that cannot be used with + * selected TLS backend + */ + MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60024 + , + /** * The response header name has forbidden characters or token */ MHD_SC_RESP_HEADER_NAME_INVALID = 60050 @@ -1426,6 +1468,18 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_REPLY_HEADERS_TOO_LARGE = 60103 , /** + * Specified offset in file-backed response is too large and not supported + * by the platform + */ + MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE = 60104 + , + /** + * File-backed response has file smaller than specified combination of + * the file offset and the response size. + */ + MHD_SC_REPLY_FILE_TOO_SHORT = 60105 + , + /** * The new connection cannot be used because the FD number is higher than * the limit set by FD_SETSIZE (if internal polling with select is used) or * by application. @@ -3296,14 +3350,12 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_UseStictLevel enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend { /** - * Disable TLS, use plain TCP connections + * Disable TLS, use plain TCP connections (default) */ MHD_TLS_BACKEND_NONE = 0 , /** * Use best available TLS backend. - * Currently this is equivalent to GnuTLS (if TLS is enabled - * for MHD build). */ MHD_TLS_BACKEND_ANY = 1 , @@ -3311,6 +3363,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use GnuTLS as TLS backend. */ MHD_TLS_BACKEND_GNUTLS = 2 + , + /** + * Use OpenSSL as TLS backend. + */ + MHD_TLS_BACKEND_OPENSSL = 3 }; /** @@ -3702,8 +3759,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_CLIENT_ABORT = 30 , /** - * The request is not valid according to - * HTTP specifications. + * The request is not valid according to HTTP specifications. * @ingroup request */ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31 @@ -3715,7 +3771,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_BY_APP_ABORT = 40 , /** - * The application aborted request without response. + * The request was aborted due to the application failed to provide a valid + * resonse. * @ingroup request */ MHD_REQUEST_ENDED_BY_APP_ERROR = 41 @@ -3727,6 +3784,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_NO_RESOURCES = 50 , /** + * The request was aborted due to error reading file for file-backed response + * @ingroup request + */ + MHD_REQUEST_ENDED_FILE_ERROR = 51 + , + /** * Closing the session since MHD is being shut down. * @ingroup request */ @@ -3949,16 +4012,18 @@ MHD_D_OPTION_TLS ( /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, - * could be NULL is private does not need a password + * could be NULL if private key does not need a password * @return structure with the requested setting */ struct MHD_DaemonOptionAndValue -MHD_D_OPTION_TLS_KEY_CERT ( +MHD_D_OPTION_TLS_CERT_KEY ( + /* const */ char *mem_cert, const char *mem_key, - const char *mem_cert, const char *mem_pass ); @@ -4683,8 +4748,8 @@ MHD_FN_PAR_NONNULL_ (1); * @param addrlen number of bytes in @a addr * @param connection_cntx meta data the application wants to * associate with the new connection object - * @return #MHD_SC_OK on success - * error on failure + * @return #MHD_SC_OK on success, + * error on failure (the @a client_socket is closed) * @ingroup specialized */ MHD_EXTERN_ enum MHD_StatusCode @@ -8119,8 +8184,8 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed * of the library depending on conditions. * Some of the features can be disabled or selected at build-time. */ /** - * Get whether HTTPS is supported and type of TLS backend(s) available if - * HTTPS is supported. + * Get whether HTTPS and which types of TLS backend(s) supported by + * this build. * The result is placed in @a v_tls member. */ MHD_LIB_INFO_FIXED_TYPE_TLS = 100 @@ -8236,20 +8301,24 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_LibInfoFixedITCType /** - * The types of the TLS backend supported + * The types of the TLS backend supported/available/enabled * @note the enum can be extended in future versions with new members */ -struct MHD_LibInfoFixedTLSType +struct MHD_LibInfoTLSType { /** - * The TLS is supported. + * The TLS is supported/enabled. * Set to #MHD_YES if any other member is #MHD_YES. */ enum MHD_Bool tls_supported; /** - * The TLS is supported by GnuTLS backend. + * The GnuTLS backend is supported/available/enabled. + */ + enum MHD_Bool backend_gnutls; + /** + * The OpenSSL backend is supported/available/enabled. */ - enum MHD_Bool tls_gnutls; + enum MHD_Bool backend_openssl; }; /** @@ -8286,9 +8355,9 @@ union MHD_LibInfoFixedData */ enum MHD_LibInfoFixedITCType v_itc; /** - * The types of the TLS backend supported + * The types of the TLS backend supported by the build */ - struct MHD_LibInfoFixedTLSType v_tls; + struct MHD_LibInfoTLSType v_tls; }; /** @@ -8347,6 +8416,17 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoDynamic MHD_LIB_INFO_DYNAMIC_INITED = 0 , + /** + * Get whether HTTPS and which types of TLS backend(s) currently available. + * If any MHD daemonis active (created and not destroyed, not nessesary + * running) the result reflects the current backends availability. + * If no MHD daemon is active, then this function would try to temporarly + * enable backends to check for their availability. + * The result is placed in @a v_tls member. + */ + MHD_LIB_INFO_DYNAMIC_TYPE_TLS = 100 + , + /* * Sentinel * */ /** * The sentinel value. @@ -8369,6 +8449,11 @@ union MHD_LibInfoDynamicData enum MHD_Bool v_bool; /** + * The types of the TLS backends available + */ + struct MHD_LibInfoTLSType v_tls; + + /** * Unused member. * Help enforcing future-proof alignment of the union. * Do not use. diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h @@ -108,7 +108,7 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. */ - MHD_D_O_TLS_KEY_CERT = 121 + MHD_D_O_TLS_CERT_KEY = 121 , /** @@ -424,23 +424,25 @@ struct MHD_DaemonOptionValueTFO }; /** - * Data for #MHD_D_O_TLS_KEY_CERT + * Data for #MHD_D_O_TLS_CERT_KEY */ struct MHD_DaemonOptionValueTlsCert { /** - * the private key loaded into memory (not a filename) + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - const char *v_mem_key; + /* const */ char *v_mem_cert; /** - * the certificate loaded into memory (not a filename) + * the private key in PEM format loaded into memory (not a filename) */ - const char *v_mem_cert; + const char *v_mem_key; /** * the option passphrase phrase to decrypt the private key, - * could be NULL is private does not need a password + * could be NULL if private key does not need a password */ const char *v_mem_pass; @@ -656,10 +658,12 @@ union MHD_DaemonOptionValue enum MHD_TlsBackend tls; /** - * Value for #MHD_D_O_TLS_KEY_CERT. - * the private key loaded into memory (not a filename) + * Value for #MHD_D_O_TLS_CERT_KEY. + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - struct MHD_DaemonOptionValueTlsCert tls_key_cert; + struct MHD_DaemonOptionValueTlsCert tls_cert_key; /** * Value for #MHD_D_O_TLS_CLIENT_CA. @@ -1023,20 +1027,22 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, - * could be NULL is private does not need a password + * could be NULL if private key does not need a password * @return structure with the requested setting */ -# define MHD_D_OPTION_TLS_KEY_CERT(mem_key,mem_cert,mem_pass) \ +# define MHD_D_OPTION_TLS_CERT_KEY(mem_cert,mem_key,mem_pass) \ MHD_NOWARN_COMPOUND_LITERALS_ \ (const struct MHD_DaemonOptionAndValue) \ { \ - .opt = MHD_D_O_TLS_KEY_CERT, \ - .val.tls_key_cert.v_mem_key = (mem_key), \ - .val.tls_key_cert.v_mem_cert = (mem_cert), \ - .val.tls_key_cert.v_mem_pass = (mem_pass) \ + .opt = MHD_D_O_TLS_CERT_KEY, \ + .val.tls_cert_key.v_mem_cert = (mem_cert), \ + .val.tls_cert_key.v_mem_key = (mem_key), \ + .val.tls_cert_key.v_mem_pass = (mem_pass) \ } \ MHD_RESTORE_WARN_COMPOUND_LITERALS_ /** @@ -1740,25 +1746,27 @@ MHD_D_OPTION_TLS ( /** * Provide TLS key and certificate data in-memory. * Works only if TLS mode is enabled. - * @param mem_key the private key loaded into memory (not a filename) - * @param mem_cert the certificate loaded into memory (not a filename) + * @param mem_cert The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. + * @param mem_key the private key in PEM format loaded into memory (not a filename) * @param mem_pass the option passphrase phrase to decrypt the private key, - * could be NULL is private does not need a password + * could be NULL if private key does not need a password * @return structure with the requested setting */ static MHD_INLINE struct MHD_DaemonOptionAndValue -MHD_D_OPTION_TLS_KEY_CERT ( +MHD_D_OPTION_TLS_CERT_KEY ( + /* const */ char *mem_cert, const char *mem_key, - const char *mem_cert, const char *mem_pass ) { struct MHD_DaemonOptionAndValue opt_val; - opt_val.opt = MHD_D_O_TLS_KEY_CERT; - opt_val.val.tls_key_cert.v_mem_key = mem_key; - opt_val.val.tls_key_cert.v_mem_cert = mem_cert; - opt_val.val.tls_key_cert.v_mem_pass = mem_pass; + opt_val.opt = MHD_D_O_TLS_CERT_KEY; + opt_val.val.tls_cert_key.v_mem_cert = mem_cert; + opt_val.val.tls_cert_key.v_mem_key = mem_key; + opt_val.val.tls_cert_key.v_mem_pass = mem_pass; return opt_val; } diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in @@ -259,8 +259,8 @@ MHD_FN_PAR_NONNULL_ (1); * @param addrlen number of bytes in @a addr * @param connection_cntx meta data the application wants to * associate with the new connection object - * @return #MHD_SC_OK on success - * error on failure + * @return #MHD_SC_OK on success, + * error on failure (the @a client_socket is closed) * @ingroup specialized */ MHD_EXTERN_ enum MHD_StatusCode @@ -3695,8 +3695,8 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoFixed * of the library depending on conditions. * Some of the features can be disabled or selected at build-time. */ /** - * Get whether HTTPS is supported and type of TLS backend(s) available if - * HTTPS is supported. + * Get whether HTTPS and which types of TLS backend(s) supported by + * this build. * The result is placed in @a v_tls member. */ MHD_LIB_INFO_FIXED_TYPE_TLS = 100 @@ -3812,20 +3812,24 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_LibInfoFixedITCType /** - * The types of the TLS backend supported + * The types of the TLS backend supported/available/enabled * @note the enum can be extended in future versions with new members */ -struct MHD_LibInfoFixedTLSType +struct MHD_LibInfoTLSType { /** - * The TLS is supported. + * The TLS is supported/enabled. * Set to #MHD_YES if any other member is #MHD_YES. */ enum MHD_Bool tls_supported; /** - * The TLS is supported by GnuTLS backend. + * The GnuTLS backend is supported/available/enabled. + */ + enum MHD_Bool backend_gnutls; + /** + * The OpenSSL backend is supported/available/enabled. */ - enum MHD_Bool tls_gnutls; + enum MHD_Bool backend_openssl; }; /** @@ -3862,9 +3866,9 @@ union MHD_LibInfoFixedData */ enum MHD_LibInfoFixedITCType v_itc; /** - * The types of the TLS backend supported + * The types of the TLS backend supported by the build */ - struct MHD_LibInfoFixedTLSType v_tls; + struct MHD_LibInfoTLSType v_tls; }; /** @@ -3923,6 +3927,17 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_LibInfoDynamic MHD_LIB_INFO_DYNAMIC_INITED = 0 , + /** + * Get whether HTTPS and which types of TLS backend(s) currently available. + * If any MHD daemonis active (created and not destroyed, not nessesary + * running) the result reflects the current backends availability. + * If no MHD daemon is active, then this function would try to temporarly + * enable backends to check for their availability. + * The result is placed in @a v_tls member. + */ + MHD_LIB_INFO_DYNAMIC_TYPE_TLS = 100 + , + /* * Sentinel * */ /** * The sentinel value. @@ -3945,6 +3960,11 @@ union MHD_LibInfoDynamicData enum MHD_Bool v_bool; /** + * The types of the TLS backends available + */ + struct MHD_LibInfoTLSType v_tls; + + /** * Unused member. * Help enforcing future-proof alignment of the union. * Do not use. diff --git a/src/include/microhttpd2_portability.h b/src/include/microhttpd2_portability.h @@ -68,11 +68,11 @@ MHD_C_DECLRATIONS_START_HERE_ * MHD_Socket is type for socket FDs */ # if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET) -# define MHD_POSIX_SOCKETS 1 +# define MHD_SOCKETS_KIND_POSIX 1 typedef int MHD_Socket; # define MHD_INVALID_SOCKET (-1) # else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */ -# define MHD_WINSOCK_SOCKETS 1 +# define MHD_SOCKETS_KIND_WINSOCK 1 typedef SOCKET MHD_Socket; # define MHD_INVALID_SOCKET (INVALID_SOCKET) # endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */ diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in @@ -459,6 +459,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode , /** * Failed to allocate memory for the daemon resources. + * TODO: combine similar error codes for daemon */ MHD_SC_DAEMON_MALLOC_FAILURE = 30081 , @@ -714,12 +715,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_TLS_DISABLED = 50000 , /** - * The application attempted to setup TLS parameters before - * enabling TLS. - */ - MHD_SC_TLS_BACKEND_UNINITIALIZED = 50003 - , - /** * The selected TLS backend does not yet support this operation. */ MHD_SC_TLS_BACKEND_OPERATION_UNSUPPORTED = 50004 @@ -1158,6 +1153,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_REPLY_POOL_ALLOCATION_FAILURE = 50231 , /** + * Failed to read the file for file-backed response. + */ + MHD_SC_REPLY_FILE_READ_ERROR = 50232 + , + /** * Failed to allocate memory in connection's pool for the reply. */ MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250 @@ -1282,11 +1282,36 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED = 50840 , /** + * Global initialisation of MHD library failed + */ + MHD_SC_LIB_INIT_GLOBAL_FAILED = 51000 + , + /** + * Failed to initialise TLS context for the daemon + */ + MHD_SC_TLS_DAEMON_INIT_FAILED = 51200 + , + /** + * Failed to initialise TLS context for the new connection + */ + MHD_SC_TLS_CONNECTION_INIT_FAILED = 51201 + , + /** + * Warning about TLS backend configuration + */ + MHD_SC_TLS_LIB_CONF_WARNING = 51202 + , + /** + * Failed to perform TLS handshake + */ + MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED = 51220 + , + /** * Something wrong in the internal MHD logic. * This error should be never returned if MHD works as expected. * If this code is ever returned, please report to MHD maintainers. */ - MHD_SC_INTERNAL_ERROR = 51000 + MHD_SC_INTERNAL_ERROR = 59900 , /* 60000-level errors are because the application @@ -1305,17 +1330,6 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC = 60001 , /** - * The application requested an unsupported TLS backend to be used. - */ - MHD_SC_TLS_BACKEND_UNSUPPORTED = 60003 - , - /** - * The application requested a TLS cipher suite which is not - * supported by the selected backend. - */ - MHD_SC_TLS_CIPHERS_INVALID = 60004 - , - /** * MHD is closing a connection because the application * logic to generate the response data failed. */ @@ -1386,6 +1400,34 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_CONFIGURATION_CONN_LIMIT_TOO_SMALL = 60016 , /** + * The application requested an unsupported TLS backend to be used. + */ + MHD_SC_TLS_BACKEND_UNSUPPORTED = 60020 + , + /** + * The application attempted to setup TLS parameters before + * enabling TLS. + */ + MHD_SC_TLS_BACKEND_UNINITIALIZED = 60021 + , + /** + * The application requested a TLS backend which cannot be used due + * to missing TLS dynamic library or backend initialisation problem. + */ + MHD_SC_TLS_BACKEND_UNAVAILABLE = 60022 + , + /** + * Provided TLS certificate and/or private key are incorrect + */ + MHD_SC_TLS_CONF_BAD_CERT = 60023 + , + /** + * The application requested a daemon setting that cannot be used with + * selected TLS backend + */ + MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS = 60024 + , + /** * The response header name has forbidden characters or token */ MHD_SC_RESP_HEADER_NAME_INVALID = 60050 @@ -1426,6 +1468,18 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_REPLY_HEADERS_TOO_LARGE = 60103 , /** + * Specified offset in file-backed response is too large and not supported + * by the platform + */ + MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE = 60104 + , + /** + * File-backed response has file smaller than specified combination of + * the file offset and the response size. + */ + MHD_SC_REPLY_FILE_TOO_SHORT = 60105 + , + /** * The new connection cannot be used because the FD number is higher than * the limit set by FD_SETSIZE (if internal polling with select is used) or * by application. @@ -3296,14 +3350,12 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_UseStictLevel enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend { /** - * Disable TLS, use plain TCP connections + * Disable TLS, use plain TCP connections (default) */ MHD_TLS_BACKEND_NONE = 0 , /** * Use best available TLS backend. - * Currently this is equivalent to GnuTLS (if TLS is enabled - * for MHD build). */ MHD_TLS_BACKEND_ANY = 1 , @@ -3311,6 +3363,11 @@ enum MHD_FIXED_ENUM_APP_SET_ MHD_TlsBackend * Use GnuTLS as TLS backend. */ MHD_TLS_BACKEND_GNUTLS = 2 + , + /** + * Use OpenSSL as TLS backend. + */ + MHD_TLS_BACKEND_OPENSSL = 3 }; /** @@ -3702,8 +3759,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_CLIENT_ABORT = 30 , /** - * The request is not valid according to - * HTTP specifications. + * The request is not valid according to HTTP specifications. * @ingroup request */ MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR = 31 @@ -3715,7 +3771,8 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_BY_APP_ABORT = 40 , /** - * The application aborted request without response. + * The request was aborted due to the application failed to provide a valid + * resonse. * @ingroup request */ MHD_REQUEST_ENDED_BY_APP_ERROR = 41 @@ -3727,6 +3784,12 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_RequestEndedCode MHD_REQUEST_ENDED_NO_RESOURCES = 50 , /** + * The request was aborted due to error reading file for file-backed response + * @ingroup request + */ + MHD_REQUEST_ENDED_FILE_ERROR = 51 + , + /** * Closing the session since MHD is being shut down. * @ingroup request */ diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -21,8 +21,8 @@ lib_LTLIBRARIES = \ noinst_DATA = MOSTLYCLEANFILES = +nodist_libmicrohttpd2_la_SOURCES = $(CONFIG_HEADER) libmicrohttpd2_la_SOURCES = \ - $(CONFIG_HEADER) \ autoinit_funcs.h \ sys_offsetof.h \ sys_null_macro.h sys_base_types.h sys_bool_type.h \ @@ -31,12 +31,13 @@ libmicrohttpd2_la_SOURCES = \ sys_select.h sys_poll.h \ sys_sendfile.h \ compat_calloc.h \ - mhd_assert.h \ - mhd_cntnr_ptr.h \ - mhd_tristate.h \ + sys_w32_ver.h \ + mhd_assert.h mhd_unreachable.h \ + mhd_cntnr_ptr.h mhd_arr_num_elems.h \ + mhd_tristate.h mhd_status_code_int.h \ mhd_socket_type.h mhd_sockets_macros.h \ mhd_sockets_funcs.c mhd_sockets_funcs.h \ - mhd_socket_error.c mhd_socket_error.h \ + mhd_socket_error_funcs.c mhd_socket_error_funcs.h mhd_socket_error.h \ mhd_atomic_counter.c mhd_atomic_counter.h \ mhd_bool.h \ mhd_str.c mhd_str.h \ @@ -46,13 +47,16 @@ libmicrohttpd2_la_SOURCES = \ mhd_iovec.h \ mhd_panic.c mhd_panic.h \ mhd_lib_init.c mhd_lib_init_impl.h mhd_lib_init.h \ + mhd_lib_init_auto.h \ + lib_get_info.c \ mhd_dlinked_list.h \ - mhd_connection.h \ + mhd_conn_socket.h mhd_connection.h \ mhd_locks.h \ mhd_itc.c mhd_itc.h mhd_itc_types.h \ mhd_threads.c mhd_threads.h sys_thread_entry_type.h \ mhd_mono_clock.c mhd_mono_clock.h \ mhd_mempool.c mhd_mempool.h \ + mhd_read_file.c mhd_read_file.h \ mhd_recv.c mhd_recv.h \ mhd_send.c mhd_send.h \ mhd_daemon.h \ @@ -100,10 +104,28 @@ post_parser_files = \ upgrade_files = \ mhd_upgrade.h \ - upgrade_prep.c upgrade_prep.h \ - upgrade_proc.c upgrade_proc.h \ + upgrade_prep.c upgrade_prep.h \ + upgrade_proc.c upgrade_proc.h \ upgraded_net.c +tls_common_files = \ + mhd_tls_choice.h \ + mhd_tls_funcs.c mhd_tls_funcs.h mhd_tls_enums.h \ + tls_dh_params.h \ + conn_tls_check.c conn_tls_check.h + +tls_multi_files = \ + tls_multi_tls_lib.h tls_multi_daemon_data.h tls_multi_conn_data.h \ + tls_multi_funcs.c tls_multi_funcs.h + +tls_gnu_files = \ + tls_gnu_tls_lib.h tls_gnu_daemon_data.h tls_gnu_conn_data.h \ + tls_gnu_funcs.c tls_gnu_funcs.h + +tls_open_files = \ + tls_open_tls_lib.h tls_open_daemon_data.h tls_open_conn_data.h \ + tls_open_funcs.c tls_open_funcs.h + if HAVE_POST_PARSER libmicrohttpd2_la_SOURCES += $(post_parser_files) endif @@ -112,11 +134,27 @@ if MHD_UPGRADE_SUPPORT libmicrohttpd2_la_SOURCES += $(upgrade_files) endif +if MHD_ENABLE_HTTPS + libmicrohttpd2_la_SOURCES += $(tls_common_files) + +if MHD_ENABLE_MULTITLS + libmicrohttpd2_la_SOURCES += $(tls_multi_files) +endif + +if MHD_USE_GNUTLS + libmicrohttpd2_la_SOURCES += $(tls_gnu_files) +endif + +if MHD_USE_OPENSSL + libmicrohttpd2_la_SOURCES += $(tls_open_files) +endif +endif + libmicrohttpd2_la_CPPFLAGS = \ $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \ -DBUILDING_MHD_LIB=1 libmicrohttpd2_la_CFLAGS = \ - $(AM_CFLAGS) $(MHD_LIB_CFLAGS) $(MHD_TLS_LIB_CFLAGS) + $(AM_CFLAGS) $(MHD_LIB_CFLAGS) libmicrohttpd2_la_LDFLAGS = \ $(AM_LDFLAGS) $(MHD_LIB_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) \ $(W32_MHD_LIB_LDFLAGS) \ diff --git a/src/mhd2/autoinit_funcs.h b/src/mhd2/autoinit_funcs.h @@ -1,6 +1,6 @@ /* - * AutoinitFuncs: Automatic Initialization and Deinitialization Functions - * Copyright(C) 2014-2023 Karlson2k (Evgeny Grin) + * AutoinitFuncs: Automatic Initialisation and Deinitialisation Functions + * Copyright(C) 2014-2024 Karlson2k (Evgeny Grin) * * This header is free software; you can redistribute it and / or * modify it under the terms of the GNU Lesser General Public @@ -18,14 +18,15 @@ */ /* - General usage is simple: include this header, declare or define two - functions with zero parameters (void) and any return type: one for - initialisation and one for deinitialisation, add - _SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code + General usage is simple: include this header, define two functions + with zero parameters (void) and any return type: one for initialisation and + one for deinitialisation, add + AIF_SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code, and functions will be automatically called during application startup and shutdown. This is useful for libraries as libraries don't have direct access - to main() functions. + to the main() function. + Example: ------------------------------------------------- #include <stdlib.h> @@ -37,181 +38,275 @@ void libInit(void) { someVar = 3; - somePtr = malloc(100); + // Calling other library functions could be unsafe the as the order + // of initialisation of libraries is not strictly defined + //somePtr = malloc(100); } void libDeinit(void) { - free(somePtr); + // Calling other library functions could be unsafe + //free(somePtr); } - _SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit); + AIF_SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit); ------------------------------------------------- - If initialiser or deinitialiser function is not needed, just define - it as empty function. + If initialiser or deinitialiser function is not needed, just use + an empty function as a placeholder. This header should work with GCC, clang, MSVC (2010 or later) and SunPro / Sun Studio / Oracle Solaris Studio / Oracle Developer Studio - compiler. + compiler and other compatible compilers. Supported C and C++ languages; application, static and dynamic (DLL) libraries; non-optimized (Debug) and optimised (Release) compilation and linking. - For more information see header code and comments in code. + Besides the main macro mentioned above, the header defines other helper + macros prefixed with AIF_. These macros can be used directly, but they + are not designed to be defined externally. + + The header behaviour could be adjusted by defining various AUTOINIT_FUNCS_* + macros before including the header. + + For more information see the header code and comments in the code. */ -#ifndef AUTOINIT_FUNCS_INCLUDED -#define AUTOINIT_FUNCS_INCLUDED 1 +#ifndef AIF_HEADER_INCLUDED +#define AIF_HEADER_INCLUDED 1 /** -* Current version of the header in packed BCD form. -* 0x01093001 = 1.9.30-1. +* The header version number in packed BCD form. +* (For example, version 1.9.30-1 would be 0x01093001) */ -#define AUTOINIT_FUNCS_VERSION 0x01001000 +#define AIF_VERSION 0x02000100 + +/* Define AUTOINIT_FUNCS_NO_WARNINGS to disable all custom warnings + in this header */ +#ifdef AUTOINIT_FUNCS_NO_WARNINGS +# ifndef AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH +# define AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH 1 +# endif +# ifndef AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C +# define AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C 1 +# endif +#endif -#if defined(__GNUC__) || defined(__clang__) -/* if possible - check for supported attribute */ +/* If possible - check for supported attributes */ #ifdef __has_attribute -#if ! __has_attribute (constructor) || ! __has_attribute (destructor) -#define _GNUC_ATTR_CONSTR_NOT_SUPPORTED 1 -#endif /* !__has_attribute(constructor) || !__has_attribute(destructor) */ +# if __has_attribute (constructor) && __has_attribute (destructor) +# define AIF_GNUC_ATTR_CONSTR_SUPPORTED 1 +# else /* ! __has_attribute (constructor) || ! __has_attribute (destructor) */ +# define AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED 1 +# endif /* ! __has_attribute (constructor) || ! __has_attribute (destructor) */ #endif /* __has_attribute */ -#endif /* __GNUC__ */ + +#if defined(__GNUC__) && __GNUC__ < 2 \ + && ! defined(AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED) \ + && ! defined(AIF_GNUC_ATTR_CONSTR_SUPPORTED) +# define AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED 1 +#endif /* "__has_attribute__ ((constructor))" is supported by GCC, clang and Sun/Oracle compiler starting from version 12.1. */ -#if ((defined(__GNUC__) || defined(__clang__)) && \ - ! defined(_GNUC_ATTR_CONSTR_NOT_SUPPORTED)) || \ - (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100) - -#define GNUC_SET_INIT_AND_DEINIT(FI,FD) \ - void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void); \ - void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \ - void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void) \ +#if (defined(AIF_GNUC_ATTR_CONSTR_SUPPORTED) \ + || defined(__GNUC__) || defined(__clang__) \ + || (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100)) && \ + ! defined(AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED) + +# define AIF_GNUC_SET_INIT_AND_DEINIT(FI,FD) \ + void __attribute__ ((constructor)) AIF_GNUC_init_helper_ ## FI (void); \ + void __attribute__ ((destructor)) AIF_GNUC_deinit_helper_ ## FD (void); \ + void __attribute__ ((constructor)) AIF_GNUC_init_helper_ ## FI (void) \ { (void) (FI) (); } \ - void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void) \ + void __attribute__ ((destructor)) AIF_GNUC_deinit_helper_ ## FD (void) \ { (void) (FD) (); } \ - struct _GNUC_dummy_str_ ## FI {int i;} + struct AIF_GNUC_dummy_str_ ## FI {int i;} -#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD) -#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1 - -#elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600 +#elif defined(_WIN32) && defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600 && \ + ! defined(__CYGWIN__) /* Make sure that your project/sources define: _LIB if building a static library (_LIB is ignored if _CONSOLE is defined); _USRDLL if building DLL-library; not defined both _LIB and _USRDLL if building an application */ -/* Define AUTOINIT_FUNCS_DECLARE_STATIC_REG if you need macro declaration - for registering static initialisation functions even if you building DLL */ -/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro - _SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL */ - /* Stringify macros */ -#define _INSTRMACRO(a) #a -#define _STRMACRO(a) _INSTRMACRO (a) - -#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_DECLARE_STATIC_REG) \ - || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG) - -/* Use "C" linkage for variable to simplify variable decoration */ -#ifdef __cplusplus -#define W32_INITVARDECL extern "C" -#else -#define W32_INITVARDECL extern -#endif - -/* How variable is decorated by compiler */ -#if (defined(_WIN32) || defined(_WIN64)) \ +# define AIF_INSTRMACRO(a) #a /* Strigify helper */ +# define AIF_STRMACRO(a) AIF_INSTRMACRO (a) /* Expand and strigify */ + +/* Concatenate macros */ +# define AIF_INCONCAT(a,b) a ## b /* Concatenate helper */ +# define AIF_CONCAT(a,b) AIF_INCONCAT (a,b) /* Expand and concatenate */ + +/* Use "C" linkage for variables to simplify symbols decoration */ +# ifdef __cplusplus +# define AIF_W32_INITVARDECL extern "C" +# define AIF_W32_INITHELPERFUNCDECL static +# else +# define AIF_W32_INITVARDECL extern +# define AIF_W32_INITHELPERFUNCDECL static +# endif + +/* How variables are decorated by compiler */ +# if (defined(_WIN32) || defined(_WIN64)) \ && ! defined(_M_IX86) && ! defined(_X86_) -#if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \ +# if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \ && ! defined(_M_ARM) && ! defined(_M_ARM64) -#pragma message(__FILE__ "(" _STRMACRO(__LINE__) ") : warning AIFW001 : " \ - "Untested architecture, linker may fail with unresolved symbol") -#endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */ -#define W32_VARDECORPREFIX -#define W32_DECORVARNAME(v) v -#define W32_VARDECORPREFIXSTR "" -#elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_)) -#define W32_VARDECORPREFIX _ -#define W32_DECORVARNAME(v) _ ## v -#define W32_VARDECORPREFIXSTR "_" -#else +# ifndef AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH +#pragma message(__FILE__ "(" AIF_STRMACRO(__LINE__) ") : warning AIFW001 : " \ + "Untested architecture, linker may fail with unresolved symbols") +# endif /* ! AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH */ +# endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */ +# define AIF_W32_VARDECORPREFIX +# define AIF_W32_DECORVARNAME(v) v +# define AIF_W32_VARDECORPREFIXSTR "" +# elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_)) +# define AIF_W32_VARDECORPREFIX _ +# define AIF_W32_DECORVARNAME(v) _ ## v +# define AIF_W32_VARDECORPREFIXSTR "_" +# else #error Do not know how to decorate symbols for this architecture -#endif +# endif + /* Internal variable prefix (can be any) */ -#define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f -#define W32_INITHELPERVARNAMEDECORSTR(f) \ - W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f)) +# define AIF_W32_INITHELPERVARNAME(f) _aif_init_ptr_ ## f +# define AIF_W32_INITHELPERVARNAMEDECORSTR(f) \ + AIF_W32_VARDECORPREFIXSTR AIF_STRMACRO (AIF_W32_INITHELPERVARNAME (f)) -/* Declare section (segment), put variable pointing to init function to chosen segment, - force linker to always include variable to avoid omitting by optimiser */ -/* Initialisation function must be declared as - void __cdecl FuncName(void) */ -/* "extern" with initialisation value means that variable is declared AND defined. */ -#define W32_VFPTR_IN_SEG(S,F) \ - __pragma(section (S,long,read)) \ - __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ - W32_INITVARDECL __declspec(allocate (S))void \ - (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F -/* Sections (segments) for pointers to initialisers/deinitialisers */ +/* Sections (segments) for pointers to initialisers */ /* Semi-officially suggested section for early initialisers (called before C++ objects initialisers), "void" return type */ -#define W32_SEG_INIT_EARLY ".CRT$XCT" +# define AIF_W32_SEG_STAT_INIT_EARLY1 ".CRT$XCT" +/* Guessed section name for early initialisers (called before + C++ objects initialisers, after first initialisers), "void" return type */ +# define AIF_W32_SEG_STAT_INIT_EARLY2 ".CRT$XCTa" /* Semi-officially suggested section for late initialisers (called after C++ objects initialisers), "void" return type */ -#define W32_SEG_INIT_LATE ".CRT$XCV" +# define AIF_W32_SEG_STAT_INIT_LATE ".CRT$XCV" -/* Unsafe sections (segments) for pointers to initialisers/deinitialisers */ +/* Unsafe sections (segments) for pointers to initialisers */ /* C++ lib initialisers, "void" return type (reserved by the system!) */ -#define W32_SEG_INIT_CXX_LIB ".CRT$XCL" +# define AIF_W32_SEG_STAT_INIT_CXX_LIB ".CRT$XCL" /* C++ user initialisers, "void" return type (reserved by the system!) */ -#define W32_SEG_INIT_CXX_USER ".CRT$XCU" +# define AIF_W32_SEG_STAT_INIT_CXX_USER ".CRT$XCU" - -/* Declare section (segment), put variable pointing to init function to chosen segment, - force linker to always include variable to avoid omitting by optimiser */ -/* Initialisation function must be declared as - int __cdecl FuncName(void) */ -/* Startup process is aborted if initialiser returns non-zero */ -/* "extern" with initialisation value means that variable is declared AND defined. */ -#define W32_IFPTR_IN_SEG(S,F) \ - __pragma(section (S,long,read)) \ - __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \ - W32_INITVARDECL __declspec(allocate (S))int \ - (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F +/* Declare section (segment), put variable pointing to init function to + chosen segment, force linker to always include variable to avoid omitting + by optimiser */ +/* These initialisation function must be declared as + void __cdecl FuncName(void) */ +/* Note: "extern" with initialisation value means that variable is declared AND + defined. */ +# define AIF_W32_INIT_VFPTR_IN_SEG(S,F) \ + __pragma(section(S, long, read)) \ + __pragma(comment(linker, "/INCLUDE:" \ + AIF_W32_INITHELPERVARNAMEDECORSTR (F))) \ + AIF_W32_INITVARDECL __declspec(allocate (S)) void \ + (__cdecl * AIF_W32_INITHELPERVARNAME (F))(void) = &F /* Unsafe sections (segments) for pointers to initialisers with "int" return type */ /* C lib initialisers, "int" return type (reserved by the system!). These initialisers are called before others. */ -#define W32_SEG_INIT_C_LIB ".CRT$XIL" +# define AIF_W32_SEG_STAT_INIT_C_LIB ".CRT$XIL" /* C user initialisers, "int" return type (reserved by the system!). These initialisers are called before others. */ -#define W32_SEG_INIT_C_USER ".CRT$XIU" +# define AIF_W32_SEG_STAT_INIT_C_USER ".CRT$XIU" + +/* Declare section (segment), put variable pointing to init function to + chosen segment, force linker to always include variable to avoid omitting + by optimiser */ +/* These initialisation function must be declared as + int __cdecl FuncName(void) */ +/* Startup process is aborted if initialiser returns non-zero */ +/* Note: "extern" with initialisation value means that variable is declared AND + defined. */ +# define AIF_W32_INIT_IFPTR_IN_SEG(S,F) \ + __pragma(section(S, long, read)) \ + __pragma(comment(linker, \ + "/INCLUDE:" AIF_W32_INITHELPERVARNAMEDECORSTR (F))) \ + AIF_W32_INITVARDECL __declspec(allocate (S)) int \ + (__cdecl * AIF_W32_INITHELPERVARNAME (F))(void) = &F + +/* Not recommended / unsafe */ +/* "lib" initialisers are called before "user" initialisers */ +/* "C" initialisers are called before "C++" initialisers */ +# define AIF_W32_REG_STAT_INIT_C_USER(F) \ + AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_C_USER,F) +# define AIF_W32_REG_STAT_INIT_C_LIB(F) \ + AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_C_LIB,F) +# define AIF_W32_REG_STAT_INIT_CXX_USER(F) \ + AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_CXX_USER,F) +# define AIF_W32_REG_STAT_INIT_CXX_LIB(F) \ + AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_CXX_LIB,F) +/* Declare macros for different initialisers sections */ -/* Declare macro for different initialisers sections */ /* Macro can be used several times to register several initialisers */ /* Once function is registered as initialiser, it will be called automatically - during application startup */ -#define W32_REG_INIT_EARLY(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_EARLY,F) -#define W32_REG_INIT_LATE(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_LATE,F) + during application startup or library loading */ +# define AIF_W32_REG_STAT_INIT_EARLY1(F) \ + AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_EARLY1,F) +# define AIF_W32_REG_STAT_INIT_EARLY2(F) \ + AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_EARLY2,F) +# define AIF_W32_REG_STAT_INIT_LATE(F) \ + AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_LATE,F) + +# ifndef _DLL +/* Sections (segments) for pointers to deinitialisers */ + +/* These section are not used when common rutime (CRT, the C library) is used + as DLL. In such case only sections in CRT DLL are used for deinitialisation + pointers. */ + +/* The section name based on semi-documented deinitialisation procedure, + functions called before first C deinitialisers, "void" return type */ +# define AIF_W32_SEG_STAT_DEINIT_FIRST ".CRT$XPAa" +/* The section name based on semi-documented deinitialisation procedure, + functions called after AIF_W32_SEG_STAT_DEINIT_FIRST deinitialisers and + before first C deinitialisers, "void" return type */ +# define AIF_W32_SEG_STAT_DEINIT_SECOND ".CRT$XPAb" +/* The section name based on semi-documented deinitialisation procedure, + functions called after AIF_W32_SEG_STAT_DEINIT_SECOND deinitialisers and + before first C deinitialisers, "void" return type */ +# define AIF_W32_SEG_STAT_DEINIT_THIRD ".CRT$XPAc" +/* Internal variable prefix (can be any) */ +# define AIF_W32_DEINITHELPERVARNAME(f) _aif_deinit_ptr_ ## f +# define AIF_W32_DEINITHELPERVARNAMEDECORSTR(f) \ + AIF_W32_VARDECORPREFIXSTR AIF_STRMACRO (AIF_W32_DEINITHELPERVARNAME (f)) + +/* The macro to declare section (segment), put variable pointing to deinit + function to chosen segment, force linker to always include variable to + avoid omitting by optimiser */ +/* These deinitialisation function must be declared as + void __cdecl FuncName(void) */ +/* Note: "extern" with initialisation value means that variable is declared AND + defined. */ +# define AIF_W32_DEINIT_VFPTR_IN_SEG(S,F) \ + __pragma(section(S, long, read)) \ + __pragma(comment(linker, "/INCLUDE:" \ + AIF_W32_DEINITHELPERVARNAMEDECORSTR (F))) \ + AIF_W32_INITVARDECL __declspec(allocate (S)) void \ + (__cdecl * AIF_W32_DEINITHELPERVARNAME (F))(void) = &F + +/* Declare macros for different deinitialisers sections */ +/* Macro can be used several times to register several deinitialisers */ +/* Once function is registered as initialiser, it will be called automatically + during application shutdown or library unloading */ +# define AIF_W32_REG_STAT_DEINIT_EARLY(F) \ + AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_FIRST,F) +# define AIF_W32_REG_STAT_DEINIT_LATE1(F) \ + AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_SECOND,F) +# define AIF_W32_REG_STAT_DEINIT_LATE2(F) \ + AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_THIRD,F) -/* Not recommended / unsafe */ -/* "lib" initialisers are called before "user" initialisers */ -/* "C" initialisers are called before "C++" initialisers */ -#define W32_REG_INIT_C_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_USER,F) -#define W32_REG_INIT_C_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_C_LIB,F) -#define W32_REG_INIT_CXX_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_USER,F) -#define W32_REG_INIT_CXX_LIB(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_LIB,F) +# endif /* ! _DLL */ /* Choose main register macro based on language and program type */ /* Assuming that _LIB or _USRDLL is defined for static or DLL-library */ @@ -225,79 +320,228 @@ /* By default C++ static or DLL-library code and any C code and will be registered as early initialiser, while C++ non-library code will be registered as late initialiser */ -#if (! defined(__cplusplus) || \ - ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) || \ +# if (! defined(__cplusplus) || \ + defined(_LIB) || defined(_USRDLL) || \ defined(AUTOINIT_FUNCS_FORCE_EARLY_INIT)) && \ ! defined(AUTOINIT_FUNCS_FORCE_LATE_INIT) -#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY (F) -#else -#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE (F) -#endif +/* Use early initialiser and late deinitialiser */ +# if defined(_LIB) || defined(_USRDLL) +/* Static or DLL library */ +# define AIF_W32_REGISTER_STAT_INIT(F) AIF_W32_REG_STAT_INIT_EARLY1 (F) +# ifdef AIF_W32_REG_STAT_DEINIT_LATE2 +# define AIF_W32_REGISTER_STAT_DEINIT(F) \ + AIF_W32_REG_STAT_DEINIT_LATE2 (F) +# endif +# else +/* Application code */ +# define AIF_W32_REGISTER_STAT_INIT(F) AIF_W32_REG_STAT_INIT_EARLY2 (F) +# ifdef AIF_W32_REG_STAT_DEINIT_LATE1 +# define AIF_W32_REGISTER_STAT_DEINIT(F) \ + AIF_W32_REG_STAT_DEINIT_LATE1 (F) +# endif +# endif +# else +/* Use late initialiser and early deinitialiser */ +# define AIF_W32_REGISTER_STAT_INIT(F) AIF_W32_REG_STAT_INIT_LATE (F) +# ifdef AIF_W32_REG_STAT_DEINIT_EARLY +# define AIF_W32_REGISTER_STAT_DEINIT(F) \ + AIF_W32_REG_STAT_DEINIT_EARLY (F) +# endif +# endif + + +/* Static deinit registration on W32 could be risky as it works only + if CRT is used as static lib (not as DLL) and relies on correct + definition of "_DLL" macro by build system. */ +/* If "_DLL" macro is correctly defined, static deinitialiser registration + can be enabled by defining AUTOINIT_FUNCS_ALLOW_W32_STAT_DEINIT macro. + If it is used, it can save from including an extra header. */ +# if defined(AIF_W32_REGISTER_STAT_DEINIT) \ + && defined(AUTOINIT_FUNCS_ALLOW_W32_STAT_DEINIT) +# define AIF_W32_SET_STAT_INIT_AND_DEINIT(FI,FD) \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_init_helper_ ## FI (void); \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_deinit_helper_ ## FD (void); \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_init_helper_ ## FI (void) \ + { (void) (FI) (); } \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_deinit_helper_ ## FD (void) \ + { (void) (FD) (); } \ + AIF_W32_REGISTER_STAT_INIT (AIF_W32_stat_init_helper_ ## FI); \ + AIF_W32_REGISTER_STAT_DEINIT (AIF_W32_stat_deinit_helper_ ## FD) +# else + +/* Note: 'atexit()' is just a wrapper for '_onexit()' on W32 */ +# include <stdlib.h> /* required for _onexit() */ + +# define AIF_W32_SET_STAT_INIT_AND_DEINIT(FI,FD) \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_init_helper_ ## FI (void); \ + AIF_W32_INITHELPERFUNCDECL int \ + __cdecl AIF_W32_stat_deinit_helper_ ## FD (void); \ + AIF_W32_INITHELPERFUNCDECL void \ + __cdecl AIF_W32_stat_init_helper_ ## FI (void) \ + { (void) (FI) (); \ + _onexit (&AIF_W32_stat_deinit_helper_ ## FD); } \ + AIF_W32_INITHELPERFUNCDECL int \ + __cdecl AIF_W32_stat_deinit_helper_ ## FD (void) \ + { (void) (FD) (); return ! 0; } \ + AIF_W32_REGISTER_STAT_INIT (AIF_W32_stat_init_helper_ ## FI) +# endif + +#endif /* _WIN32 && _MSC_VER + 0 >= 1600 && ! _GNUC_ATTR_CONSTR_SUPPORTED */ + + +#if defined(_WIN32) && ! defined(__CYGWIN__) && \ + (defined(_USRDLL) || defined(DLL_EXPORT)) + +# if defined(__MINGW32__) || defined(__MINGW64__) +/* The minimal portable set of the headers to pull in the definitions of "BOOL", + "WINAPI", "HINSTANCE", "DWORD" and "LPVOID" */ +/* Thi minimal set does not work with MS headers as they depend on some vital + macros defined only in the "windows.h" header only */ +# include <windef.h> +# include <winnt.h> +# else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# include <windows.h> +# endif + +# ifdef DLL_PROCESS_ATTACH +# define AIF_W32_DLL_PROCESS_ATTACH DLL_PROCESS_ATTACH +# else +# define AIF_W32_DLL_PROCESS_ATTACH 1 +# endif +# ifdef DLL_PROCESS_DETACH +# define AIF_W32_DLL_PROCESS_DETACH DLL_PROCESS_DETACH +# else +# define AIF_W32_DLL_PROCESS_DETACH 0 +# endif + +/* When process is terminating, DLL should not perform a cleanup, leaving + allocated resources to be cleaned by the system. This is because some + threads may be explicitly terminated without proper cleanup, and some + system resources, including process heap, could be in inconsistent state. + Define AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT to enable calling of deinit + function such situations. + Note: if AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT is defined and the DLL is + delay-loaded, then both the initialiser and the deinitialiser could be + called late (or last), breaking requirement for calling deinitialisers in + reverse order of initialisers. */ +# ifndef AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT +# define AIF_W32_IS_DLL_DEINIT_SAFE(pReserved) (NULL == (pReserved)) +# else +# define AIF_W32_IS_DLL_DEINIT_SAFE(pReserved) TRUE +# endif + +/* If DllMain is already present in user's code, + define AUTOINIT_FUNCS_CALL_USR_DLLMAIN and + rename user's DllMain to usr_DllMain. + The usr_DllMain() must be declared (or full defined) before using the macro + for setting initialiser and deinitialiser. Alternatively, if usr_DllMain() + is another file (or after the macro), define macro + AUTOINIT_FUNCS_DECLARE_USR_DLLMAIN to enable automatic declaration of this + function. + Define AUTOINIT_FUNCS_USR_DLLMAIN_NAME to user function name if usr_DllMain + is not suitable. */ +# ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN +# define AIF_W32_CALL_USER_DLLMAIN(h,r,p) TRUE +# else /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ +# ifndef AUTOINIT_FUNCS_USR_DLLMAIN_NAME +# define AIF_W32_USR_DLLMAIN_NAME usr_DllMain +# else +# define AIF_W32_USR_DLLMAIN_NAME AUTOINIT_FUNCS_USR_DLLMAIN_NAME +# endif +# define AIF_W32_CALL_USER_DLLMAIN(h,r,p) \ + AIF_W32_USR_DLLMAIN_NAME ((h),(r),(p)) +# ifdef AUTOINIT_FUNCS_DECLARE_USR_DLLMAIN +# define AIF_DECL_USR_DLLMAIN \ + BOOL WINAPI AIF_W32_USR_DLLMAIN_NAME (HINSTANCE hinst, DWORD reason, \ + LPVOID pReserved); +# endif +# endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ + +# ifndef AIF_DECL_USR_DLLMAIN +# define AIF_DECL_USR_DLLMAIN /* empty */ +# endif + +# define AIF_W32_SET_DLL_INIT_AND_DEINIT(FI,FD) \ + BOOL WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID pReserved); \ + AIF_DECL_USR_DLLMAIN \ + BOOL WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID pReserved) \ + { BOOL aif_ret; (void) hinst; \ + if (AIF_W32_DLL_PROCESS_ATTACH == reason) { \ + (void) (FI) (); \ + aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved); \ + if (! aif_ret && NULL != pReserved) { (void) (FD) (); } } \ + else if (AIF_W32_DLL_PROCESS_DETACH == reason) { \ + aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved); \ + if (AIF_W32_IS_DLL_DEINIT_SAFE (pReserved)) { (void) (FD) (); } } \ + else aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved); \ + return aif_ret; \ + } struct AIF_W32_dummy_strc_ ## FI {int i;} +#endif /* _WIN32 && ! __CYGWIN__ && (_USRDLL || DLL_EXPORT) */ -#endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG - || AUTOINIT_FUNCS_FORCE_STATIC_REG */ +/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro + AIF_SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL. + Static registration works for DLL too, but less precise and flexible. */ -#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG) +#if defined(AIF_W32_SET_DLL_INIT_AND_DEINIT) \ + && ! (defined(AUTOINIT_FUNCS_PREFER_STATIC_REG) \ + && (defined(AIF_GNUC_SET_INIT_AND_DEINIT) \ + || defined(AIF_W32_SET_STAT_INIT_AND_DEINIT))) -#include <stdlib.h> /* required for atexit() */ +# define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \ + AIF_W32_SET_DLL_INIT_AND_DEINIT (FI,FD) +/* Indicate that automatic initialisers/deinitialisers are supported */ +# define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1 -#define W32_SET_INIT_AND_DEINIT(FI,FD) \ - void __cdecl _W32_init_helper_ ## FI (void); \ - void __cdecl _W32_deinit_helper_ ## FD (void); \ - void __cdecl _W32_init_helper_ ## FI (void) \ - { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \ - void __cdecl _W32_deinit_helper_ ## FD (void) \ - { (void) (FD) (); } \ - W32_REGISTER_INIT (_W32_init_helper_ ## FI) -#else /* _USRDLL */ - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif /* WIN32_LEAN_AND_MEAN */ - -#include <Windows.h> /* Required for DllMain */ - -/* If DllMain is already present in code, define AUTOINIT_FUNCS_CALL_USR_DLLMAIN - and rename DllMain to usr_DllMain */ -#ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN -#define W32_SET_INIT_AND_DEINIT(FI,FD) \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ - { (void) hinst; (void) unused; \ - if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ - else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ - return TRUE; \ - } struct _W32_dummy_strc_ ## FI {int i;} -#else /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ -#define W32_SET_INIT_AND_DEINIT(FI,FD) \ - BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \ - BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused) \ - { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \ - else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \ - return usr_DllMain (hinst,reason,unused); \ - } struct _W32_dummy_strc_ ## FI {int i;} -#endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */ -#endif /* _USRDLL */ - -#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) W32_SET_INIT_AND_DEINIT (FI,FD) +#elif defined(AIF_GNUC_SET_INIT_AND_DEINIT) + +# define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \ + AIF_GNUC_SET_INIT_AND_DEINIT (FI,FD) /* Indicate that automatic initialisers/deinitialisers are supported */ -#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1 +# define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1 -#else /* !__GNUC__ && !_MSC_FULL_VER */ +#elif defined(AIF_W32_SET_STAT_INIT_AND_DEINIT) -/* Define EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED before inclusion of header to - abort compilation if automatic initialisers/deinitialisers are not supported */ -#ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED -#error \ - Compiler/platform does not support automatic calls of user-defined initializer and deinitializer -#endif /* EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED */ +# define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \ + AIF_W32_SET_STAT_INIT_AND_DEINIT (FI,FD) +/* Indicate that automatic initialisers/deinitialisers are supported */ +# define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1 -/* Do nothing */ -#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) +#else + +/* Define AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED before inclusion of + this header to abort compilation if automatic initialisers/deinitialisers + are not supported */ +# ifdef AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED +#error User-defined initialiser and deinitialiser functions are not supported +# endif /* AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED */ + +# if defined(__SUNPRO_C) && (defined(sun) || defined(__sun)) \ + && (defined(__SVR4) || defined(__svr4__)) +/* "#parama init(func_name)" can be used. "func_name" must be declared. + The form is "void func_name(void)". */ +# define AIF_PRAGMA_INIT_SUPPORTED 1 +/* "#parama fini(func_name)" can be used. "func_name" must be declared. + The form is "void func_name(void)". */ +# define AIF_PRAGMA_FINI_SUPPORTED 1 +# if ! defined(AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C) +#warning The compiler supports "#pragma init(func1)" and "#pragma fini(func2)" +#warning Use "pragma" to set initialiser and deinitialiser functions +# endif +# endif + +/* "Not supported" implementation */ +# define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) /* No-op */ /* Indicate that automatic initialisers/deinitialisers are not supported */ -#define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1 +# define AIF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1 -#endif /* !__GNUC__ && !_MSC_FULL_VER */ -#endif /* !AUTOINIT_FUNCS_INCLUDED */ +#endif +#endif /* !AIF_HEADER_INCLUDED */ diff --git a/src/mhd2/compat_calloc.c b/src/mhd2/compat_calloc.c @@ -23,6 +23,7 @@ * @brief The implementation of the calloc() replacement * @author Karlson2k (Evgeny Grin) */ +#include "mhd_sys_options.h" #include "compat_calloc.h" #ifndef HAVE_CALLOC diff --git a/src/mhd2/conn_data_process.c b/src/mhd2/conn_data_process.c @@ -36,6 +36,7 @@ #include "sys_base_types.h" #include "mhd_assert.h" +#include "mhd_unreachable.h" #include "mhd_daemon.h" #include "mhd_connection.h" @@ -46,28 +47,53 @@ #include "conn_data_send.h" #include "stream_process_states.h" +#ifdef MHD_ENABLE_HTTPS +# include "conn_tls_check.h" +#endif /* MHD_ENABLE_HTTPS */ + MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c) { + bool send_ready_state_known; + bool has_sock_err; + bool data_processed; + +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (c)) + { + switch (mhd_conn_tls_check (c)) + { + case mhd_CONN_TLS_CHECK_OK: + break; /* Process HTTP data */ + case mhd_CONN_TLS_CHECK_HANDSHAKING: + return true; /* TLS is not yet ready */ + case mhd_CONN_TLS_CHECK_BROKEN: + return false; /* Connection is broken */ + default: + mhd_assert (0 && "Impossible value"); + mhd_UNREACHABLE (); + } + } +#endif /* MHD_ENABLE_HTTPS */ + /* The "send-ready" state is known if system polling call is edge-triggered (it always checks for both send- and recv-ready) or if connection needs sending (therefore "send-ready" was explicitly checked by sockets polling call). */ - const bool send_ready_state_known = + send_ready_state_known = ((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) || (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info))); - const bool has_sock_err = - (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready)); - bool data_processed; - + has_sock_err = + (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready)); data_processed = false; if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)) { bool use_recv; - use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready)); + use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY + & (c->sk.ready | mhd_C_HAS_TLS_DATA_IN (c)))); use_recv = use_recv || - (has_sock_err && c->sk_nonblck); + (has_sock_err && c->sk.props.is_nonblck); if (use_recv) { @@ -85,15 +111,16 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c) * + connection is ready for sending or * + just formed send data, connection send ready status is not known and * connection socket is non-blocking - * + detected network error on the connection, to check to the error */ + * + detected network error on the connection, to check for the error */ /* Assuming that after finishing receiving phase, connection send system buffers should have some space as sending was performed before receiving or has not been performed yet. */ - use_send = (0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)); + use_send = (0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk.ready)); use_send = use_send || - (data_processed && (! send_ready_state_known) && c->sk_nonblck); + (data_processed && (! send_ready_state_known) + && c->sk.props.is_nonblck); use_send = use_send || - (has_sock_err && c->sk_nonblck); + (has_sock_err && c->sk.props.is_nonblck); if (use_send) { @@ -106,80 +133,4 @@ mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c) if (! data_processed) return mhd_conn_process_data (c); return true; - -#if 0 // TODO: re-implement fasttrack as a single unified buffer sending - if (! force_close) - { - /* No need to check value of 'ret' here as closed connection - * cannot be in MHD_EVENT_LOOP_INFO_SEND state. */ - if ( (MHD_EVENT_LOOP_INFO_SEND == c->event_loop_info) && - write_ready) - { - MHD_connection_handle_write (c); - ret = MHD_connection_handle_idle (c); - states_info_processed = true; - } - } - else - { - MHD_connection_close_ (c, - MHD_REQUEST_TERMINATED_WITH_ERROR); - return MHD_connection_handle_idle (c); - } - - if (! states_info_processed) - { /* Connection is not read or write ready, but external conditions - * may be changed and need to be processed. */ - ret = MHD_connection_handle_idle (c); - } - /* Fast track for fast connections. */ - /* If full request was read by single read_handler() invocation - and headers were completely prepared by single MHD_connection_handle_idle() - then try not to wait for next sockets polling and send response - immediately. - As writeability of socket was not checked and it may have - some data pending in system buffers, use this optimization - only for non-blocking sockets. */ - /* No need to check 'ret' as connection is always in - * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */ - else if (on_fasttrack && c->sk_nonblck) - { - if (MHD_CONNECTION_HEADERS_SENDING == c->state) - { - MHD_connection_handle_write (c); - /* Always call 'MHD_connection_handle_idle()' after each read/write. */ - ret = MHD_connection_handle_idle (c); - } - /* If all headers were sent by single write_handler() and - * response body is prepared by single MHD_connection_handle_idle() - * call - continue. */ - if ((MHD_CONNECTION_UNCHUNKED_BODY_READY == c->state) || - (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)) - { - MHD_connection_handle_write (c); - ret = MHD_connection_handle_idle (c); - } - } - - /* All connection's data and states are processed for this turn. - * If connection already has more data to be processed - use - * zero timeout for next select()/poll(). */ - /* Thread-per-connection do not need global zero timeout as - * connections are processed individually. */ - /* Note: no need to check for read buffer availability for - * TLS read-ready connection in 'read info' state as connection - * without space in read buffer will be marked as 'info block'. */ - if ( (! c->daemon->data_already_pending) && - (! mhd_D_HAS_THR_PER_CONN (c->daemon)) ) - { - if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info)) - c->daemon->data_already_pending = true; -#ifdef HTTPS_SUPPORT - else if ( (c->tls_read_ready) && - (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)) ) - c->daemon->data_already_pending = true; -#endif /* HTTPS_SUPPORT */ - } - return ret; -#endif } diff --git a/src/mhd2/conn_data_recv.c b/src/mhd2/conn_data_recv.c @@ -24,6 +24,8 @@ * @author Karlson2k (Evgeny Grin) */ +#include "mhd_sys_options.h" + #include "conn_data_recv.h" #include "mhd_sys_options.h" @@ -47,16 +49,14 @@ mhd_conn_data_recv (struct MHD_Connection *restrict c, size_t received; enum mhd_SocketError res; - mhd_assert (MHD_CONNECTION_CLOSED != c->state); + mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage); mhd_assert (NULL != c->read_buffer); mhd_assert (c->read_buffer_size > c->read_buffer_offset); mhd_assert (! has_err || \ - (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY))); - mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \ + (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))); + mhd_assert ((0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \ has_err); - // TODO: TLS support: handshake/transport layer - buf = c->read_buffer + c->read_buffer_offset; buf_size = c->read_buffer_size - c->read_buffer_offset; @@ -67,10 +67,10 @@ mhd_conn_data_recv (struct MHD_Connection *restrict c, /* Handle errors */ if ((mhd_SOCKET_ERR_NO_ERROR == res) && (0 == received)) { - c->sk_rmt_shut_wr = true; + c->sk.state.rmt_shut_wr = true; res = mhd_SOCKET_ERR_REMT_DISCONN; } - if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck) + if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk.props.is_nonblck) { /* Re-try last time to detect the error */ uint_fast64_t dummy_buf; @@ -78,16 +78,16 @@ mhd_conn_data_recv (struct MHD_Connection *restrict c, } if (mhd_SOCKET_ERR_IS_HARD (res)) { - c->sk_discnt_err = res; - c->sk_ready = - (enum mhd_SocketNetState) (((unsigned int) c->sk_ready) + c->sk.state.discnt_err = res; + c->sk.ready = + (enum mhd_SocketNetState) (((unsigned int) c->sk.ready) | mhd_SOCKET_NET_STATE_ERROR_READY); } return; } if (0 == received) - c->sk_rmt_shut_wr = true; + c->sk.state.rmt_shut_wr = true; c->read_buffer_offset += received; mhd_stream_update_activity_mark (c); // TODO: centralise activity update @@ -100,8 +100,8 @@ if ((bytes_read < 0) || socket_error) { if (MHD_ERR_CONNRESET_ == bytes_read) { - if ( (MHD_CONNECTION_INIT < c->state) && - (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) ) + if ( (mhd_HTTP_STAGE_INIT < c->stage) && + (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage) ) { #ifdef HAVE_MESSAGES MHD_DLOG (c->daemon, @@ -115,7 +115,7 @@ if ((bytes_read < 0) || socket_error) } #ifdef HAVE_MESSAGES - if (MHD_CONNECTION_INIT != c->state) + if (mhd_HTTP_STAGE_INIT != c->stage) MHD_DLOG (c->daemon, _ ("Connection socket is closed when reading " \ "request due to the error: %s\n"), @@ -130,9 +130,9 @@ if ((bytes_read < 0) || socket_error) #if 0 // TODO: handle remote shut WR if (0 == bytes_read) { /* Remote side closed c. */ // FIXME: Actually NOT! - c->sk_rmt_shut_wr = true; - if ( (MHD_CONNECTION_INIT < c->state) && - (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) ) + c->sk.state.rmt_shut_wr = true; + if ( (mhd_HTTP_STAGE_INIT < c->stage) && + (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage) ) { #ifdef HAVE_MESSAGES MHD_DLOG (c->daemon, @@ -143,7 +143,7 @@ if (0 == bytes_read) MHD_connection_close_ (c, MHD_REQUEST_TERMINATED_CLIENT_ABORT); } - else if (MHD_CONNECTION_INIT == c->state) + else if (mhd_HTTP_STAGE_INIT == c->stage) /* This termination code cannot be reported to the application * because application has not been informed yet about this request */ MHD_connection_close_ (c, diff --git a/src/mhd2/conn_data_send.c b/src/mhd2/conn_data_send.c @@ -36,6 +36,7 @@ #include "mhd_str_macros.h" #include "mhd_assert.h" +#include "mhd_unreachable.h" #include "mhd_connection.h" #include "mhd_response.h" @@ -51,12 +52,12 @@ * If so, transition into "next_state". * * @param connection connection to check write status for - * @param next_state the next state to transition to + * @param next_stage the next state to transition to * @return #MHD_NO if we are not done, #MHD_YES if we are */ static MHD_FN_PAR_NONNULL_ALL_ bool check_write_done (struct MHD_Connection *restrict connection, - enum MHD_CONNECTION_STATE next_state) + enum mhd_HttpStage next_stage) { // TODO: integrate into states processing if ( (connection->write_buffer_append_offset != @@ -66,7 +67,7 @@ check_write_done (struct MHD_Connection *restrict connection, return false; connection->write_buffer_append_offset = 0; connection->write_buffer_send_offset = 0; - connection->state = next_state; + connection->stage = next_stage; return true; } @@ -83,17 +84,13 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) // TODO: assert check suspended -#ifdef HTTPS_SUPPORT - // TODO: TLS support, handshake -#endif /* HTTPS_SUPPORT */ - // TODO: MOVE out STATES PROCESSING res = mhd_SOCKET_ERR_INTERNAL; - switch (c->state) + switch (c->stage) { - case MHD_CONNECTION_CONTINUE_SENDING: + case mhd_HTTP_STAGE_CONTINUE_SENDING: res = mhd_send_data (c, http_100_continue_msg_len - c->continue_message_write_offset, @@ -104,7 +101,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) if (mhd_SOCKET_ERR_NO_ERROR == res) c->continue_message_write_offset += sent; break; - case MHD_CONNECTION_HEADERS_SENDING: + case mhd_HTTP_STAGE_HEADERS_SENDING: if (1) { struct MHD_Response *const restrict resp = c->rp.response; @@ -154,7 +151,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) } if (mhd_SOCKET_ERR_NO_ERROR == res) { - mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state); + mhd_assert (mhd_HTTP_STAGE_HEADERS_SENDING == c->stage); if (sent > wb_ready) { @@ -163,34 +160,35 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) mhd_assert (0 == c->rp.rsp_cntn_read_pos); mhd_assert (! c->rp.props.chunked); mhd_assert (c->rp.props.send_reply_body); - c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY; + c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY; c->write_buffer_send_offset += wb_ready; c->rp.rsp_cntn_read_pos = sent - wb_ready; if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size) - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; } else { c->write_buffer_send_offset += sent; // TODO: move it to data processing check_write_done (c, - MHD_CONNECTION_HEADERS_SENT); + mhd_HTTP_STAGE_HEADERS_SENT); } } } break; - case MHD_CONNECTION_UNCHUNKED_BODY_READY: - case MHD_CONNECTION_CHUNKED_BODY_READY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: + case mhd_HTTP_STAGE_CHUNKED_BODY_READY: if (1) { struct MHD_Response *const restrict resp = c->rp.response; mhd_assert (c->rp.props.send_reply_body); - mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size); - mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \ + mhd_assert (c->rp.rsp_cntn_read_pos <= resp->cntn_size); + mhd_assert ((mhd_HTTP_STAGE_CHUNKED_BODY_READY != c->stage) || \ (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)); if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) { mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype); + mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size); res = mhd_send_data (c, c->rp.rsp_cntn_read_pos - resp->cntn_size, @@ -232,7 +230,7 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) { /* Switch to filereader */ mhd_assert (! c->rp.props.chunked); c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; - c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY; } } } @@ -247,32 +245,32 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) { if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) { - enum MHD_CONNECTION_STATE next_state; + enum mhd_HttpStage next_stage; c->write_buffer_send_offset += sent; // TODO: move it to data processing - if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state) - next_state = + if (mhd_HTTP_STAGE_CHUNKED_BODY_READY == c->stage) + next_stage = (c->rp.response->cntn_size == c->rp.rsp_cntn_read_pos) ? - MHD_CONNECTION_CHUNKED_BODY_SENT : - MHD_CONNECTION_CHUNKED_BODY_UNREADY; + mhd_HTTP_STAGE_CHUNKED_BODY_SENT : + mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY; else - next_state = + next_stage = (c->rp.rsp_cntn_read_pos == resp->cntn_size) ? - MHD_CONNECTION_FULL_REPLY_SENT : - MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + mhd_HTTP_STAGE_FULL_REPLY_SENT : + mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY; check_write_done (c, - next_state); + next_stage); } else { c->rp.rsp_cntn_read_pos += sent; if (c->rp.rsp_cntn_read_pos == resp->cntn_size) - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; } } } break; - case MHD_CONNECTION_FOOTERS_SENDING: + case mhd_HTTP_STAGE_FOOTERS_SENDING: res = mhd_send_data (c, c->write_buffer_append_offset - c->write_buffer_send_offset, @@ -285,11 +283,11 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) c->write_buffer_send_offset += sent; // TODO: move it to data processing check_write_done (c, - MHD_CONNECTION_FULL_REPLY_SENT); + mhd_HTTP_STAGE_FULL_REPLY_SENT); } break; #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADE_HEADERS_SENDING: + case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: res = mhd_send_data (c, c->write_buffer_append_offset - c->write_buffer_send_offset, @@ -301,38 +299,38 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) c->write_buffer_send_offset += sent; break; #endif /* MHD_UPGRADE_SUPPORT */ - case MHD_CONNECTION_INIT: - case MHD_CONNECTION_REQ_LINE_RECEIVING: - case MHD_CONNECTION_REQ_LINE_RECEIVED: - case MHD_CONNECTION_REQ_HEADERS_RECEIVING: - case MHD_CONNECTION_HEADERS_RECEIVED: - case MHD_CONNECTION_HEADERS_PROCESSED: - case MHD_CONNECTION_BODY_RECEIVING: - case MHD_CONNECTION_BODY_RECEIVED: - case MHD_CONNECTION_FOOTERS_RECEIVING: - case MHD_CONNECTION_FOOTERS_RECEIVED: - case MHD_CONNECTION_FULL_REQ_RECEIVED: - case MHD_CONNECTION_REQ_RECV_FINISHED: - case MHD_CONNECTION_START_REPLY: - case MHD_CONNECTION_HEADERS_SENT: - case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY: - case MHD_CONNECTION_CHUNKED_BODY_UNREADY: - case MHD_CONNECTION_CHUNKED_BODY_SENT: - case MHD_CONNECTION_FULL_REPLY_SENT: - case MHD_CONNECTION_PRE_CLOSING: - case MHD_CONNECTION_CLOSED: + case mhd_HTTP_STAGE_INIT: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: + case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: + case mhd_HTTP_STAGE_HEADERS_RECEIVED: + case mhd_HTTP_STAGE_HEADERS_PROCESSED: + case mhd_HTTP_STAGE_BODY_RECEIVING: + case mhd_HTTP_STAGE_BODY_RECEIVED: + case mhd_HTTP_STAGE_FOOTERS_RECEIVING: + case mhd_HTTP_STAGE_FOOTERS_RECEIVED: + case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: + case mhd_HTTP_STAGE_REQ_RECV_FINISHED: + case mhd_HTTP_STAGE_START_REPLY: + case mhd_HTTP_STAGE_HEADERS_SENT: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: + case mhd_HTTP_STAGE_FULL_REPLY_SENT: + case mhd_HTTP_STAGE_PRE_CLOSING: + case mhd_HTTP_STAGE_CLOSED: #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADING: - case MHD_CONNECTION_UPGRADED: - case MHD_CONNECTION_UPGRADED_CLEANING: + case mhd_HTTP_STAGE_UPGRADING: + case mhd_HTTP_STAGE_UPGRADED: + case mhd_HTTP_STAGE_UPGRADED_CLEANING: #endif /* MHD_UPGRADE_SUPPORT */ mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); res = mhd_SOCKET_ERR_INTERNAL; break; default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); res = mhd_SOCKET_ERR_INTERNAL; break; } @@ -343,9 +341,9 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) } else if (mhd_SOCKET_ERR_IS_HARD (res)) { - c->sk_discnt_err = res; - c->sk_ready = - (enum mhd_SocketNetState) (((unsigned int) c->sk_ready) + c->sk.state.discnt_err = res; + c->sk.ready = + (enum mhd_SocketNetState) (((unsigned int) c->sk.ready) | mhd_SOCKET_NET_STATE_ERROR_READY); } } diff --git a/src/mhd2/conn_mark_ready.h b/src/mhd2/conn_mark_ready.h @@ -94,4 +94,59 @@ mhd_conn_mark_unready (struct MHD_Connection *restrict c, } +/** + * Update "ready" mark on the connection, remove or add connection to + * the "process ready" list if necessary. + * @param c the connection to update + * @param force_ready ignore network states and mark the connection as "ready" + * @param d the daemon for the connection + */ +MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void +mhd_conn_mark_ready_update3 (struct MHD_Connection *restrict c, + unsigned int force_ready, + struct MHD_Daemon *restrict d) +{ + if (force_ready || + (0 != + ((((unsigned int) c->sk.ready) | mhd_C_HAS_TLS_DATA_IN (c)) + & ((unsigned int) c->event_loop_info) + & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND)))) + mhd_conn_mark_ready (c, d); + else + mhd_conn_mark_unready (c, d); +} + + +/** + * Update "ready" mark on the connection, remove or add connection to + * the "process ready" list if necessary. + * This function could be used if the "daemon" handle is already extracted + * from the connection. + * @param c the connection to update + * @param d the daemon for the connection + */ +MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void +mhd_conn_mark_ready_update2 (struct MHD_Connection *restrict c, + struct MHD_Daemon *restrict d) +{ + mhd_conn_mark_ready_update3 (c, 0, d); +} + + +/** + * Update "ready" mark on the connection, remove or add connection to + * the "process ready" list if necessary. + * This function could be used if the "daemon" handle has not been extracted + * from the connection. + * @param c the connection to update + * @param d the daemon for the connection + */ +MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ void +mhd_conn_mark_ready_update (struct MHD_Connection *restrict c) +{ + mhd_conn_mark_ready_update2 (c, + c->daemon); +} + + #endif /* ! MHD_CONN_MARK_READY_H */ diff --git a/src/mhd2/conn_tls_check.c b/src/mhd2/conn_tls_check.c @@ -0,0 +1,131 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/conn_tls_handshake.c + * @brief The implementation of connection TLS handling functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "conn_tls_check.h" + +#include "mhd_assert.h" +#include "mhd_unreachable.h" + +#include "mhd_daemon.h" +#include "mhd_connection.h" + +#include "mhd_socket_error_funcs.h" +#include "daemon_logger.h" + +#include "mhd_tls_funcs.h" + +#include "conn_mark_ready.h" +#include "stream_funcs.h" +#include "stream_process_states.h" + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnTlsCheckResult +mhd_conn_tls_check (struct MHD_Connection *restrict c) +{ + mhd_assert (mhd_C_HAS_TLS (c)); + mhd_assert (mhd_D_HAS_TLS (c->daemon)); + mhd_assert ((mhd_CONN_STATE_TLS_HANDSHAKE_RECV == c->conn_state) || \ + (mhd_CONN_STATE_TLS_HANDSHAKE_SEND == c->conn_state) || \ + (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state)); + + if (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state) + return mhd_CONN_TLS_CHECK_OK; /* TLS is already connected */ + + if (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready)) + { + /* Some socket error has been detected. Do not try to handshake. */ + if (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err) + c->sk.state.discnt_err = mhd_socket_error_get_from_socket (c->sk.fd); + mhd_conn_start_closing_skt_err (c); + return mhd_CONN_TLS_CHECK_BROKEN; + } + /* Check whether the socket is ready for the required send/recv operation */ + if (0 == (((mhd_CONN_FLAG_RECV | mhd_CONN_FLAG_SEND) + & ((unsigned int) c->conn_state) + & ((unsigned int) c->sk.ready)))) + return mhd_CONN_TLS_CHECK_HANDSHAKING; + + switch (mhd_tls_conn_handshake (c->tls)) + { + case mhd_TLS_PROCED_SUCCESS: + c->conn_state = mhd_CONN_STATE_TLS_CONNECTED; + if (! c->sk.props.is_nonblck) + { + /* The socket is blocking, + probably all available data has been processed already. */ + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' and 'send-ready' */ + (((unsigned int) c->sk.ready) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_SEND_READY) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_RECV_READY)); + } + /* TLS is connected now, set event loop state based on HTTP protocol. + Some early application-level data could be processing in this round. */ + mhd_conn_event_loop_state_update (c); + + return mhd_CONN_TLS_CHECK_OK; + break; + case mhd_TLS_PROCED_RECV_MORE_NEEDED: + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ + (((unsigned int) c->sk.ready) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_RECV_READY)); + /* Intentional fall-through */ + case mhd_TLS_PROCED_RECV_INTERRUPTED: + c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_RECV; + c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; + break; + case mhd_TLS_PROCED_SEND_MORE_NEEDED: + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) c->sk.ready) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_SEND_READY)); + /* Intentional fall-through */ + case mhd_TLS_PROCED_SEND_INTERRUPTED: + c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_SEND; + c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; + break; + case mhd_TLS_PROCED_FAILED: + c->conn_state = mhd_CONN_STATE_TLS_FAILED; + mhd_LOG_MSG (c->daemon, \ + MHD_SC_TLS_CONNECTION_HANDSHAKED_FAILED, \ + "Failed to perform TLS handshake on the new connection"); + c->sk.state.discnt_err = mhd_SOCKET_ERR_TLS; + mhd_conn_start_closing_skt_err (c); + return mhd_CONN_TLS_CHECK_BROKEN; + break; + default: + mhd_assert (0 && "Should be unreachable"); + mhd_UNREACHABLE (); + return mhd_CONN_TLS_CHECK_BROKEN; + } + + mhd_conn_mark_ready_update (c); + return mhd_CONN_TLS_CHECK_HANDSHAKING; +} diff --git a/src/mhd2/conn_tls_check.h b/src/mhd2/conn_tls_check.h @@ -0,0 +1,71 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/conn_tls_check.h + * @brief The declarations of connection TLS handling functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_CONN_TLS_CHECK_H +#define MHD_CONN_TLS_CHECK_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +struct MHD_Connection; /* forward declaration */ + +/** + * The results of connection TLS checking + */ +enum mhd_ConnTlsCheckResult +{ + /** + * The TLS layer is connected, the communication over TLS can be performed + */ + mhd_CONN_TLS_CHECK_OK = 0 + , + /** + * The TLS layer connection is in progress. + * Communication over TLS is not possible yet. + */ + mhd_CONN_TLS_CHECK_HANDSHAKING + , + /** + * The connection is broken and must be closed + */ + mhd_CONN_TLS_CHECK_BROKEN +}; + + +/** + * Check connection TLS status, perform TLS (re-)handshake if necessary, + * update connection's recv()/send() event loop state and connection active + * state if network operation has been performed. + * @param c the connection to process + * @return #mhd_CONN_TLS_CHECK_OK if the connection can be used, + * other enum mhd_ConnTlsCheckResult values otherwise + */ +MHD_INTERNAL enum mhd_ConnTlsCheckResult +mhd_conn_tls_check (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + +#endif /* ! MHD_CONN_TLS_CHECK_H */ diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c @@ -67,6 +67,10 @@ #include "response_destroy.h" #include "conn_mark_ready.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif + #include "mhd_public_api.h" @@ -80,7 +84,7 @@ connection_set_initial_state (struct MHD_Connection *restrict c) { size_t read_buf_size; - mhd_assert (MHD_CONNECTION_INIT == c->state); + mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE; c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; @@ -139,12 +143,14 @@ notify_app_conn (struct MHD_Daemon *restrict daemon, * @param sk_spipe_supprs indicate that the @a client_socket has * set SIGPIPE suppression * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket - * @return pointer to the connection on success, NULL if this daemon could - * not handle the connection (i.e. malloc failed, etc). - * The socket will be closed in case of error; 'errno' is - * set to indicate further details about the error. + * @param[out] conn_out the pointer to variable to be set to the address + * of newly allocated connection structure + * @return #MHD_SC_OK on success, + * error on failure (the @a client_socket is closed) */ -static enum MHD_StatusCode +static MHD_FN_MUST_CHECK_RESULT_ +MHD_FN_PAR_NONNULL_ (1) +MHD_FN_PAR_NONNULL_ (9) MHD_FN_PAR_OUT_ (9) enum MHD_StatusCode new_connection_prepare_ (struct MHD_Daemon *restrict daemon, MHD_Socket client_socket, const struct sockaddr_storage *restrict addr, @@ -155,62 +161,121 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon, enum mhd_Tristate sk_is_nonip, struct MHD_Connection **restrict conn_out) { - struct MHD_Connection *connection; + struct MHD_Connection *c; + enum MHD_StatusCode ret; + size_t tls_data_size; + + ret = MHD_SC_OK; *conn_out = NULL; - if (NULL == (connection = mhd_calloc (1, sizeof (struct MHD_Connection)))) - { - mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE, - "Failed to allocate memory for the new connection"); - mhd_socket_close (client_socket); - return MHD_SC_CONNECTION_MALLOC_FAILURE; - } + tls_data_size = 0; +#ifdef MHD_ENABLE_HTTPS + if (mhd_D_HAS_TLS (daemon)) + tls_data_size = mhd_tls_conn_get_tls_size (daemon->tls); +#endif - if (! external_add) + c = mhd_calloc (1, sizeof (struct MHD_Connection) + tls_data_size); + if (NULL == c) { - connection->sk_corked = mhd_T_NO; - connection->sk_nodelay = mhd_T_NO; + mhd_LOG_MSG (daemon, \ + MHD_SC_CONNECTION_MALLOC_FAILURE, \ + "Failed to allocate memory for the new connection"); + ret = MHD_SC_CONNECTION_MALLOC_FAILURE; } else { - connection->sk_corked = mhd_T_MAYBE; - connection->sk_nodelay = mhd_T_MAYBE; - } +#ifdef MHD_ENABLE_HTTPS + if (0 != tls_data_size) + c->tls = (struct mhd_TlsConnData *) (c + 1); +# ifndef HAVE_NULL_PTR_ALL_ZEROS + else + c->tls = NULL; +# endif +#endif - if (0 < addrlen) - { - if (NULL == (connection->addr = malloc (addrlen))) + if (! external_add) { - mhd_LOG_MSG (daemon, MHD_SC_CONNECTION_MALLOC_FAILURE, - "Failed to allocate memory for the new connection"); - mhd_socket_close (client_socket); - free (connection); - return MHD_SC_CONNECTION_MALLOC_FAILURE; + c->sk.state.corked = mhd_T_NO; + c->sk.state.nodelay = mhd_T_NO; } - memcpy (connection->addr, - addr, - addrlen); - } - else - connection->addr = NULL; - connection->addr_len = addrlen; - connection->socket_fd = client_socket; - connection->sk_nonblck = non_blck; - connection->is_nonip = sk_is_nonip; - connection->sk_spipe_suppress = sk_spipe_supprs; + else + { + c->sk.state.corked = mhd_T_MAYBE; + c->sk.state.nodelay = mhd_T_MAYBE; + } + + if (0 < addrlen) + { + // TODO: combine into single allocation. Alignment should be taken into account + c->sk.addr.data = (struct sockaddr_storage *) malloc (addrlen); + if (NULL == c->sk.addr.data) + { + mhd_LOG_MSG (daemon, \ + MHD_SC_CONNECTION_MALLOC_FAILURE, \ + "Failed to allocate memory for the new connection"); + ret = MHD_SC_CONNECTION_MALLOC_FAILURE; + } + else + memcpy (c->sk.addr.data, + addr, + addrlen); + } + else + c->sk.addr.data = NULL; + + if (MHD_SC_OK == ret) + { + c->sk.addr.size = addrlen; + c->sk.fd = client_socket; + c->sk.props.is_nonblck = non_blck; + c->sk.props.is_nonip = sk_is_nonip; + c->sk.props.has_spipe_supp = sk_spipe_supprs; #ifdef MHD_USE_THREADS - mhd_thread_handle_ID_set_invalid (&connection->tid); + mhd_thread_handle_ID_set_invalid (&c->tid); #endif /* MHD_USE_THREADS */ - connection->daemon = daemon; - connection->connection_timeout_ms = daemon->conns.cfg.timeout; - connection->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; - if (0 != connection->connection_timeout_ms) - connection->last_activity = MHD_monotonic_msec_counter (); + c->daemon = daemon; + c->connection_timeout_ms = daemon->conns.cfg.timeout; + c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; - // TODO: init TLS - *conn_out = connection; + if (0 != tls_data_size) + { + if (! mhd_tls_conn_init (daemon->tls, + &(c->sk), + c->tls)) + { + mhd_LOG_MSG (daemon, \ + MHD_SC_TLS_CONNECTION_INIT_FAILED, \ + "Failed to initialise TLS context for " \ + "the new connection"); + ret = MHD_SC_TLS_CONNECTION_INIT_FAILED; + } + else + { + c->conn_state = mhd_CONN_STATE_TLS_HANDSHAKE_RECV; +#ifndef NDEBUG + c->dbg.tls_inited = true; +#endif + } + } + + if (MHD_SC_OK == ret) + { + if (0 != c->connection_timeout_ms) + c->last_activity = mhd_monotonic_msec_counter (); + *conn_out = c; - return MHD_SC_OK; + return MHD_SC_OK; /* Success exit point */ + } + + /* Below is a cleanup path */ + if (NULL != c->sk.addr.data) + free (c->sk.addr.data); + } + free (c); + } + mhd_socket_close (client_socket); + mhd_assert (MHD_SC_OK != ret); + return ret; /* Failure exit point */ } @@ -317,7 +382,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon, event.data.ptr = connection; if (0 != epoll_ctl (daemon->events.data.epoll.e_fd, EPOLL_CTL_ADD, - connection->socket_fd, + connection->sk.fd, &event)) { mhd_LOG_MSG (daemon, MHD_SC_EPOLL_CTL_ADD_FAILED, @@ -328,7 +393,7 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon, { if (0) // TODO: implement turbo { - connection->sk_ready = mhd_SOCKET_NET_STATE_RECV_READY + connection->sk.ready = mhd_SOCKET_NET_STATE_RECV_READY | mhd_SOCKET_NET_STATE_SEND_READY; mhd_conn_mark_ready (connection, daemon); } @@ -356,13 +421,16 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon, } /* Free resources allocated before the call of this functions */ - // TODO: TLS support +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (connection)) + mhd_tls_conn_deinit (connection->tls); +#endif // TODO: per IP limit - if (NULL != connection->addr) - free (connection->addr); - (void) mhd_socket_close (connection->socket_fd); + if (NULL != connection->sk.addr.data) + free (connection->sk.addr.data); + (void) mhd_socket_close (connection->sk.fd); free (connection); mhd_assert (MHD_SC_OK != res); return res; /* *** Function failure exit point *** */ @@ -385,10 +453,8 @@ new_connection_process_ (struct MHD_Daemon *restrict daemon, * @param sk_spipe_supprs indicate that the @a client_socket has * set SIGPIPE suppression * @param sk_is_nonip _MHD_YES if this is not a TCP/IP socket - * @return #MHD_YES on success, #MHD_NO if this daemon could - * not handle the connection (i.e. malloc failed, etc). - * The socket will be closed in any case; 'errno' is - * set to indicate further details about the error. + * @return #MHD_SC_OK on success, + * error on failure (the @a client_socket is closed) */ static enum MHD_StatusCode internal_add_connection (struct MHD_Daemon *daemon, @@ -543,8 +609,8 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon, return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; } #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - if ((0 != addr->sa_len) && - (sizeof(struct sockaddr_in) > (size_t) addr->sa_len) ) + if ((0 != sk.addr.data->sa_len) && + (sizeof(struct sockaddr_in) > (size_t) sk.addr.data->sa_len) ) { mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ "MHD_add_connection() has been called with " \ @@ -567,8 +633,8 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon, return MHD_SC_CONFIGURATION_WRONG_SA_SIZE; } #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - if ((0 != addr->sa_len) && - (sizeof(struct sockaddr_in6) > (size_t) addr->sa_len) ) + if ((0 != sk.addr.data->sa_len) && + (sizeof(struct sockaddr_in6) > (size_t) sk.addr.data->sa_len) ) { mhd_LOG_MSG (daemon, MHD_SC_CONFIGURATION_WRONG_SA_SIZE, \ "MHD_add_connection() has been called with " \ @@ -580,9 +646,9 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ } #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - if ((0 != addr->sa_len) && - (addrlen > (size_t) addr->sa_len)) - addrlen = (size_t) addr->sa_len; /* Use safest value */ + if ((0 != sk.addr.data->sa_len) && + (addrlen > (size_t) sk.addr.data->sa_len)) + addrlen = (size_t) sk.addr.data->sa_len; /* Use safest value */ #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ #endif /* HAVE_INET6 */ } @@ -604,11 +670,11 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon, else sk_nonbl = true; -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK sk_spipe_supprs = false; -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ sk_spipe_supprs = true; /* Nothing to suppress on W32 */ -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ #if defined(MHD_socket_nosignal_) if (! sk_spipe_supprs) sk_spipe_supprs = MHD_socket_nosignal_ (client_socket); @@ -652,7 +718,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon, socket as the initial offset into the pool for load balancing */ unsigned int offset; -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK uint_fast64_t osb = (uint_fast64_t) client_socket; osb ^= (((uint_fast64_t) client_socket) >> 9); osb ^= (((uint_fast64_t) client_socket) >> 18); @@ -752,11 +818,11 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon) | SOCK_NOSIGPIPE_OR_ZERO)))) { sk_nonbl = (SOCK_NONBLOCK_OR_ZERO != 0); -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK sk_spipe_supprs = (SOCK_NOSIGPIPE_OR_ZERO != 0); -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ sk_spipe_supprs = true; /* Nothing to suppress on W32 */ -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0); } #endif /* USE_ACCEPT4 */ @@ -772,11 +838,11 @@ mhd_daemon_accept_connection (struct MHD_Daemon *restrict daemon) #else /* ! MHD_ACCEPT_INHERIT_NONBLOCK */ sk_nonbl = false; #endif /* ! MHD_ACCEPT_INHERIT_NONBLOCK */ -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK sk_spipe_supprs = false; -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ sk_spipe_supprs = true; /* Nothing to suppress on W32 */ -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ sk_cloexec = false; } #endif /* _DEBUG || !USE_ACCEPT4 */ @@ -975,9 +1041,25 @@ mhd_conn_close_final (struct MHD_Connection *restrict c) mhd_assert (c != mhd_DLINKEDL_GET_FIRST (&(c->daemon->conns), all_conn)); mhd_assert (c != mhd_DLINKEDL_GET_LAST (&(c->daemon->conns), all_conn)); - if (NULL != c->addr) - free (c->addr); - mhd_socket_close (c->socket_fd); +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (c)) + { + mhd_assert (mhd_D_HAS_TLS (c->daemon)); + mhd_assert (c->dbg.tls_inited); + mhd_tls_conn_deinit (c->tls); + } +# ifndef NDEBUG + else + { + mhd_assert (! mhd_D_HAS_TLS (c->daemon)); + mhd_assert (! c->dbg.tls_inited); + } +# endif +#endif + + if (NULL != c->sk.addr.data) + free (c->sk.addr.data); + mhd_socket_close (c->sk.fd); free (c); } diff --git a/src/mhd2/daemon_create.c b/src/mhd2/daemon_create.c @@ -28,6 +28,9 @@ #include "sys_base_types.h" #include "sys_malloc.h" +#ifndef NDEBUG +# include <stdio.h> /* For debug print */ +#endif /* ! NDEBUG */ #include "mhd_public_api.h" @@ -51,7 +54,14 @@ MHD_daemon_create (MHD_RequestCallback req_cb, struct MHD_Daemon *d; struct DaemonOptions *s; - MHD_GLOBAL_INIT_CHECK (); + if (! mhd_lib_init_global_if_needed ()) + { +#ifndef NDEBUG + fprintf (stderr, "Failed to initialise MHD library.\n"); + fflush (stderr); +#endif /* ! NDEBUG */ + return NULL; + } if (NULL == req_cb) return NULL; @@ -71,9 +81,9 @@ MHD_daemon_create (MHD_RequestCallback req_cb, /* Any floating point and pointer members must be initialised manually here */ #ifndef HAVE_NULL_PTR_ALL_ZEROS s->bind_sa.v_sa = NULL; - s->tls_key_cert.v_mem_key = NULL; - s->tls_key_cert.v_mem_cert = NULL; - s->tls_key_cert.v_mem_pass = NULL; + s->tls_cert_key.v_mem_key = NULL; + s->tls_cert_key.v_mem_cert = NULL; + s->tls_cert_key.v_mem_pass = NULL; s->tls_client_ca = NULL; s->tls_psk_callback.v_psk_cb = NULL; s->tls_psk_callback.v_psk_cb_cls = NULL; @@ -88,6 +98,9 @@ MHD_daemon_create (MHD_RequestCallback req_cb, s->notify_stream.v_nsc = NULL; s->notify_stream.v_cls = NULL; s->random_entropy.v_buf = NULL; + s->tls_cert_key.v_mem_cert = NULL; + s->tls_cert_key.v_mem_key = NULL; + s->tls_cert_key.v_mem_pass = NULL; d->log_params.v_log_cb = NULL; /* optional */ #endif /* !HAVE_NULL_PTR_ALL_ZEROS */ diff --git a/src/mhd2/daemon_logger.h b/src/mhd2/daemon_logger.h @@ -50,18 +50,23 @@ mhd_logger (struct MHD_Daemon *daemon, ...); /** - * Log a single message. + * Log a single fixed message. * * The @a msg is a 'printf()' string, treated as format specifiers string. * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format * specifier symbol. + * + * Note: no printf() parameters allowed (except the format string). To log + * message with variable parameters, use #mhd_LOG_PRINT with #mhd_LOG_FMT. */ #define mhd_LOG_MSG(daemon,sc,msg) mhd_logger (daemon,sc,msg) /** - * Format message and log it + * Format message and log it. * * Always use with #mhd_LOG_FMT() for the format string. + * + * Example: mhd_LOG_PRINT(daemon, MHD_SC_VALUE, mhd_LOG_FMT("Number: %d"), i); */ #define mhd_LOG_PRINT mhd_logger @@ -77,18 +82,23 @@ mhd_logger (struct MHD_Daemon *daemon, #ifdef HAVE_MACRO_VARIADIC /** - * Log a single message. + * Log a single fixed message. * * The @a msg is a 'printf()' string, treated as format specifiers string. * Any '%' symbols should be doubled ('%%') to avoid interpretation as a format * specifier symbol. + * + * Note: no printf() parameters allowed (except the format string). To log + * message with variable parameters, use #mhd_LOG_PRINT with #mhd_LOG_FMT. */ #define mhd_LOG_MSG(daemon,sc,msg) do { (void) daemon; } while (0) /** - * Format message and log it + * Format message and log it. * * Always use with #mhd_LOG_FMT() for the format string. + * + * Example: mhd_LOG_PRINT(daemon, MHD_SC_VALUE, mhd_LOG_FMT("Number: %d"), i); */ #define mhd_LOG_PRINT(daemon,sc,fm,...) do { (void) daemon; } while (0) diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h @@ -94,10 +94,12 @@ struct DaemonOptions /** - * Value for #MHD_D_O_TLS_KEY_CERT. - * the private key loaded into memory (not a filename) + * Value for #MHD_D_O_TLS_CERT_KEY. + * The X.509 certificates chain in PEM format loaded into memory (not a filename). + * The first certificate must be the server certificate, following by the chain of signing + * certificates up to (but not including) CA root certificate. */ - struct MHD_DaemonOptionValueTlsCert tls_key_cert; + struct MHD_DaemonOptionValueTlsCert tls_cert_key; /** diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c @@ -86,10 +86,53 @@ MHD_daemon_set_options ( case MHD_D_O_TLS: settings->tls = option->val.tls; continue; - case MHD_D_O_TLS_KEY_CERT: - settings->tls_key_cert.v_mem_key = option->val.tls_key_cert.v_mem_key; - settings->tls_key_cert.v_mem_cert = option->val.tls_key_cert.v_mem_cert; - settings->tls_key_cert.v_mem_pass = option->val.tls_key_cert.v_mem_pass; + case MHD_D_O_TLS_CERT_KEY: + /* custom setter */ + if ((NULL == option->val.tls_cert_key.v_mem_cert) + || (NULL == option->val.tls_cert_key.v_mem_key)) + return MHD_SC_TLS_CONF_BAD_CERT; + else + { + size_t cert_size; + size_t key_size; + size_t pass_size; + cert_size = strlen (option->val.tls_cert_key.v_mem_cert); + key_size = strlen (option->val.tls_cert_key.v_mem_key); + if ((0 == cert_size) + || (0 == key_size)) + return MHD_SC_TLS_CONF_BAD_CERT; + ++cert_size; /* Space for zero-termination */ + ++key_size; /* Space for zero-termination */ + if (NULL != option->val.tls_cert_key.v_mem_pass) + pass_size = strlen (option->val.tls_cert_key.v_mem_pass); + else + pass_size = 0; + if (0 != pass_size) + ++pass_size; /* Space for zero-termination */ + if (NULL != settings->tls_cert_key.v_mem_cert) + free (settings->tls_cert_key.v_mem_cert); // TODO: Support multiple certificates!! + settings->tls_cert_key.v_mem_cert = malloc (cert_size + key_size + pass_size); + if (NULL == settings->tls_cert_key.v_mem_cert) + return MHD_SC_DAEMON_MALLOC_FAILURE; + memcpy (settings->tls_cert_key.v_mem_cert, + option->val.tls_cert_key.v_mem_cert, + cert_size); + memcpy (settings->tls_cert_key.v_mem_cert + cert_size, + option->val.tls_cert_key.v_mem_key, + key_size); + settings->tls_cert_key.v_mem_key = + settings->tls_cert_key.v_mem_cert + cert_size; + if (0 != pass_size) + { + memcpy (settings->tls_cert_key.v_mem_cert + cert_size + key_size, + option->val.tls_cert_key.v_mem_pass, + pass_size); + settings->tls_cert_key.v_mem_pass = + settings->tls_cert_key.v_mem_cert + cert_size + key_size; + } + else + settings->tls_cert_key.v_mem_pass = NULL; + } continue; case MHD_D_O_TLS_CLIENT_CA: settings->tls_client_ca = option->val.tls_client_ca; diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c @@ -35,14 +35,14 @@ #include "mhd_sockets_macros.h" #include "sys_ip_headers.h" -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # include "sys_errno.h" #endif #ifdef MHD_USE_EPOLL # include <sys/epoll.h> #endif -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # include <fcntl.h> # ifdef MHD_USE_SELECT # ifdef HAVE_SYS_SELECT_H @@ -67,9 +67,17 @@ #include "daemon_options.h" #include "mhd_assert.h" +#include "mhd_unreachable.h" + #include "mhd_sockets_funcs.h" + +#include "mhd_lib_init.h" #include "daemon_logger.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif + #ifdef MHD_USE_THREADS # include "mhd_itc.h" # include "mhd_threads.h" @@ -99,6 +107,8 @@ dsettings_release (struct DaemonOptions *s) mhd_socket_close (s->listen_socket); if (NULL != s->bind_sa.v_sa) free (s->bind_sa.v_sa); + if (NULL != s->tls_cert_key.v_mem_cert) + free (s->tls_cert_key.v_mem_cert); free (s); } @@ -438,7 +448,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, { case MHD_AF_NONE: mhd_assert (0); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; case MHD_AF_AUTO: #ifdef HAVE_INET6 @@ -585,7 +595,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, case mhd_SKT_NO_SOCKET: default: mhd_assert (0); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } @@ -611,13 +621,13 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, if (MHD_INVALID_SOCKET == sk) { mhd_assert (NULL != p_use_sa); -#if defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT) +#if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(WSA_FLAG_NO_HANDLE_INHERIT) /* May fail before Win7 SP1 */ sk = WSASocketW (p_use_sa->sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); if (MHD_INVALID_SOCKET == sk) -#endif /* MHD_WINSOCK_SOCKETS && WSA_FLAG_NO_HANDLE_INHERIT */ +#endif /* MHD_SOCKETS_KIND_WINSOCK && WSA_FLAG_NO_HANDLE_INHERIT */ sk = socket (p_use_sa->sa_family, SOCK_STREAM | mhd_SOCK_NONBLOCK | mhd_SOCK_CLOEXEC | mhd_SOCK_NOSIGPIPE, 0); @@ -808,7 +818,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED >= d->settings->listen_addr_reuse) { -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK #ifdef SO_REUSEADDR mhd_SCKT_OPT_BOOL on_val1 = 1; if (0 != setsockopt (sk, SOL_SOCKET, SO_REUSEADDR, @@ -822,17 +832,17 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \ "The OS does not support address reuse for sockets"); #endif /* ! SO_REUSEADDR */ -#endif /* ! MHD_WINSOCK_SOCKETS */ +#endif /* ! MHD_SOCKETS_KIND_WINSOCK */ if (MHD_D_OPTION_BIND_TYPE_NOT_SHARED > d->settings->listen_addr_reuse) { -#if defined(SO_REUSEPORT) || defined(MHD_WINSOCK_SOCKETS) +#if defined(SO_REUSEPORT) || defined(MHD_SOCKETS_KIND_WINSOCK) mhd_SCKT_OPT_BOOL on_val2 = 1; if (0 != setsockopt (sk, SOL_SOCKET, -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK SO_REUSEPORT, -#else /* ! MHD_WINSOCK_SOCKETS */ +#else /* ! MHD_SOCKETS_KIND_WINSOCK */ SO_REUSEADDR, /* On W32 it is the same as SO_REUSEPORT on other platforms */ -#endif /* ! MHD_WINSOCK_SOCKETS */ +#endif /* ! MHD_SOCKETS_KIND_WINSOCK */ (const void *) &on_val2, sizeof (on_val2))) { mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, \ @@ -841,12 +851,12 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED; break; } -#else /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */ +#else /* ! SO_REUSEADDR && ! MHD_SOCKETS_KIND_WINSOCK */ mhd_LOG_MSG (d, MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, \ "The OS does not support address sharing for sockets"); ret = MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED; break; -#endif /* ! SO_REUSEADDR && ! MHD_WINSOCK_SOCKETS */ +#endif /* ! SO_REUSEADDR && ! MHD_SOCKETS_KIND_WINSOCK */ } } #if defined(SO_EXCLUSIVEADDRUSE) || defined(SO_EXCLBIND) @@ -962,7 +972,7 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d, case mhd_SKT_NO_SOCKET: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } d->net.listen.non_block = is_non_block; @@ -1137,6 +1147,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, struct DaemonOptions *restrict s) { enum mhd_IntPollType chosen_type; + bool fallback_syscall_allowed; mhd_assert ((mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) || \ (mhd_WM_INT_EXTERNAL_EVENTS_EDGE == d->wmode_int) || \ @@ -1148,11 +1159,13 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, ((mhd_POLL_TYPE_EXT == d->events.poll_type) || \ (mhd_POLL_TYPE_EPOLL == d->events.poll_type)))); - /* Check whether the provided parameter is in the range of expected values */ + /* Check whether the provided parameter is in the range of expected values. + Only block unsupported values. */ switch (s->poll_syscall) { case MHD_SPS_AUTO: chosen_type = mhd_POLL_TYPE_NOT_SET_YET; + /* The used value is chosen below */ break; case MHD_SPS_SELECT: mhd_assert (! mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)); @@ -1197,10 +1210,30 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type) { +#ifdef MHD_USE_EPOLL + bool edge_trig_allowed; + edge_trig_allowed = true; +# ifdef MHD_ENABLE_HTTPS + if ((edge_trig_allowed) && + (MHD_TLS_BACKEND_NONE != s->tls)) + edge_trig_allowed = mhd_tls_is_edge_trigg_supported (s); +# endif /* MHD_ENABLE_HTTPS */ +#endif /* MHD_USE_EPOLL */ + fallback_syscall_allowed = true; + if (mhd_WM_INT_HAS_EXT_EVENTS (d->wmode_int)) + { chosen_type = mhd_POLL_TYPE_EXT; + } #ifdef MHD_USE_EPOLL - else if (mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != d->wmode_int) + else if (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode) + { + fallback_syscall_allowed = false; + chosen_type = mhd_POLL_TYPE_EPOLL; /* without fallback */ + } + else if ((mhd_WM_INT_INTERNAL_EVENTS_THREAD_PER_CONNECTION != + d->wmode_int) && + (edge_trig_allowed)) chosen_type = mhd_POLL_TYPE_EPOLL; /* with possible fallback */ #endif else @@ -1214,6 +1247,8 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, #endif } } + else + fallback_syscall_allowed = false; /* Try 'epoll' if possible */ #ifdef MHD_USE_EPOLL @@ -1227,15 +1262,16 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, if (MHD_SC_OK != epoll_res) { - if ((MHD_SPS_EPOLL == s->poll_syscall) || - (MHD_WM_EXTERNAL_SINGLE_FD_WATCH == s->work_mode.mode)) + if (! fallback_syscall_allowed) return epoll_res; /* Cannot init epoll, but epoll is required */ chosen_type = mhd_POLL_TYPE_NOT_SET_YET; /* Choose again */ } } mhd_assert ((mhd_POLL_TYPE_EPOLL != d->events.poll_type) || \ (0 < d->events.data.epoll.e_fd)); -#endif /* MHD_USE_EPOLL */ +#else /* ! MHD_USE_EPOLL */ + (void) fallback_syscall_allowed; /* Mute compiler warning */ +#endif /* ! MHD_USE_EPOLL */ if (mhd_POLL_TYPE_NOT_SET_YET == chosen_type) { @@ -1304,7 +1340,7 @@ daemon_choose_and_preinit_events (struct MHD_Daemon *restrict d, case mhd_POLL_TYPE_NOT_SET_YET: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; break; } @@ -1329,9 +1365,9 @@ daemon_init_net (struct MHD_Daemon *restrict d, mhd_assert (! d->dbg.net_inited); mhd_assert (! d->dbg.net_deinited); -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX d->net.cfg.max_fd_num = s->fd_number_limit; -#endif /* MHD_POSIX_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_POSIX */ ret = daemon_choose_and_preinit_events (d, s); if (MHD_SC_OK != ret) @@ -1342,14 +1378,14 @@ daemon_init_net (struct MHD_Daemon *restrict d, /* No direct return of error codes is allowed beyond this point. Deinit/cleanup must be performed before return of any error. */ -#if defined(MHD_POSIX_SOCKETS) && defined(MHD_USE_SELECT) +#if defined(MHD_SOCKETS_KIND_POSIX) && defined(MHD_USE_SELECT) if (mhd_POLL_TYPE_SELECT == d->events.poll_type) { if ((MHD_INVALID_SOCKET == d->net.cfg.max_fd_num) || (FD_SETSIZE < d->net.cfg.max_fd_num)) d->net.cfg.max_fd_num = FD_SETSIZE; } -#endif /* MHD_POSIX_SOCKETS && MHD_USE_SELECT */ +#endif /* MHD_SOCKETS_KIND_POSIX && MHD_USE_SELECT */ if (MHD_SC_OK == ret) { @@ -1435,6 +1471,89 @@ dauth_init (struct MHD_Daemon *restrict d, #endif + +/** + * Initialise daemon TLS data + * @param d the daemon object + * @param s the user settings + * @return #MHD_SC_OK on success, + * the error code otherwise + */ +static MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) \ + MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_tls (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_StatusCodeInt ret; + + mhd_assert (! d->dbg.tls_inited); +#ifdef MHD_ENABLE_HTTPS + d->tls = NULL; +#endif + + if (MHD_TLS_BACKEND_NONE == s->tls) + { +#ifndef NDEBUG + d->dbg.tls_inited = true; +#endif + return MHD_SC_OK; + } +#ifndef MHD_ENABLE_HTTPS + mhd_LOG_MSG (d, \ + MHD_SC_TLS_DISABLED, \ + "HTTPS is not supported by this MHD build"); + return MHD_SC_TLS_DISABLED; +#else /* MHD_ENABLE_HTTPS */ + if (1) + { + enum mhd_TlsBackendAvailable tls_avail; + + tls_avail = mhd_tls_is_backend_available (s); + if (mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED == tls_avail) + { + mhd_LOG_MSG (d, \ + MHD_SC_TLS_BACKEND_UNSUPPORTED, \ + "The requested TLS backend is not supported " \ + "by this MHD build"); + return MHD_SC_TLS_BACKEND_UNSUPPORTED; + } + else if (mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE == tls_avail) + { + mhd_LOG_MSG (d, \ + MHD_SC_TLS_BACKEND_UNAVAILABLE, \ + "The requested TLS backend is not available"); + return MHD_SC_TLS_BACKEND_UNAVAILABLE; + } + } + ret = mhd_tls_daemon_init (d, + mhd_D_HAS_EDGE_TRIGG (d), + s, + &(d->tls)); + mhd_assert ((MHD_SC_OK == ret) || (NULL == d->tls)); + mhd_assert ((MHD_SC_OK != ret) || (NULL != d->tls)); +#ifndef NDEBUG + d->dbg.tls_inited = (MHD_SC_OK == ret); +#endif + return (enum MHD_StatusCode) ret; +#endif /* MHD_ENABLE_HTTPS */ +} + + +/** + * Deinitialise daemon TLS data + * @param d the daemon object + */ +MHD_FN_PAR_NONNULL_ (1) static void +daemon_deinit_tls (struct MHD_Daemon *restrict d) +{ + mhd_assert (d->dbg.tls_inited); +#ifdef MHD_ENABLE_HTTPS + if (NULL != d->tls) + mhd_tls_daemon_deinit (d->tls); +#endif +} + + /** * Initialise large buffer tracking. * @param d the daemon object @@ -1636,7 +1755,7 @@ allocate_events (struct MHD_Daemon *restrict d) default: mhd_assert (0 && "Impossible value"); } - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } @@ -1655,7 +1774,7 @@ deallocate_events (struct MHD_Daemon *restrict d) if (mhd_POLL_TYPE_NOT_SET_YET == d->events.poll_type) { mhd_assert (0 && "Wrong workflow"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return; } #ifdef MHD_USE_SELECT @@ -1871,7 +1990,7 @@ add_itc_and_listen_to_monitoring (struct MHD_Daemon *restrict d) default: mhd_assert (0 && "Impossible value"); } - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } @@ -2071,7 +2190,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d, limit_by_select = UINT_MAX; error_by_fd_setsize = false; -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX if (1) { limit_by_num = (unsigned int) d->net.cfg.max_fd_num; @@ -2107,7 +2226,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d, else limit_by_num = (unsigned int) INT_MAX; } -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) if (1) { #ifdef MHD_USE_SELECT @@ -2135,7 +2254,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d, #endif /* MHD_USE_SELECT */ (void) 0; /* Mute compiler warning */ } -#endif /* MHD_POSIX_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_POSIX */ if (error_by_fd_setsize) { mhd_LOG_MSG ( \ @@ -2154,7 +2273,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d, { /* No user configuration provided */ unsigned int suggested_limit; -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK #define TYPICAL_NOFILES_LIMIT (1024) /* The usual limit for the number of open FDs */ suggested_limit = TYPICAL_NOFILES_LIMIT; suggested_limit -= 3; /* The numbers zero, one and two are used typically */ @@ -2165,13 +2284,13 @@ set_connections_total_limits (struct MHD_Daemon *restrict d, --suggested_limit; /* One FD is used for the listening socket */ if (suggested_limit > TYPICAL_NOFILES_LIMIT) suggested_limit = 0; /* Overflow */ -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ #ifdef _WIN64 suggested_limit = 2048; #else suggested_limit = 1024; #endif -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ if (suggested_limit < num_worker_daemons) { /* Use at least one connection for every worker daemon and @@ -2249,7 +2368,7 @@ set_d_threading_type (struct MHD_Daemon *restrict d) default: mhd_assert (0 && "Impossible value"); } - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } @@ -2399,14 +2518,13 @@ init_workers_pool (struct MHD_Daemon *restrict d, if (MHD_SC_OK == res) continue; /* Process the next worker */ + /* Below is a clean-up of the current slot */ + #ifdef MHD_USE_EPOLL if (mhd_POLL_TYPE_EPOLL == worker->events.poll_type) deinit_epoll (worker); #endif /* MHD_USE_EPOLL */ - - /* Below is the clean-up of the current slot */ } - free (worker); break; } if (num_workers == i) @@ -2527,7 +2645,7 @@ daemon_deinit_threading_and_conn (struct MHD_Daemon *restrict d) deinit_workers_pool (d, d->threading.hier.pool.num); #else /* ! MHD_USE_THREADS */ mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); (void) 0; #endif /* ! MHD_USE_THREADS */ } @@ -2597,7 +2715,7 @@ start_individual_daemon_thread (struct MHD_Daemon *restrict d) else { mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; } mhd_assert (mhd_thread_handle_ID_is_valid_handle (d->threading.tid)); @@ -2757,7 +2875,7 @@ daemon_start_threads (struct MHD_Daemon *restrict d) } #else /* ! MHD_USE_THREADS */ mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; #endif /* ! MHD_USE_THREADS */ } @@ -2796,7 +2914,7 @@ daemon_stop_threads (struct MHD_Daemon *restrict d) } #else /* ! MHD_USE_THREADS */ mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return MHD_SC_INTERNAL_ERROR; #endif /* ! MHD_USE_THREADS */ } @@ -2826,32 +2944,35 @@ daemon_start_internal (struct MHD_Daemon *restrict d, if (MHD_SC_OK != res) return res; + mhd_assert (d->dbg.net_inited); + res = daemon_init_tls (d, s); - // TODO: Other init - - res = daemon_init_threading_and_conn (d, s); if (MHD_SC_OK == res) { - mhd_assert (d->dbg.net_inited); - mhd_assert (d->dbg.threading_inited); - mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); - - res = daemon_init_large_buf (d, s); + mhd_assert (d->dbg.tls_inited); + res = daemon_init_threading_and_conn (d, s); if (MHD_SC_OK == res) { - res = daemon_start_threads (d); + mhd_assert (d->dbg.threading_inited); + mhd_assert (! mhd_D_TYPE_IS_INTERNAL_ONLY (d->threading.d_type)); + + res = daemon_init_large_buf (d, s); if (MHD_SC_OK == res) { - return MHD_SC_OK; - } + res = daemon_start_threads (d); + if (MHD_SC_OK == res) + { + return MHD_SC_OK; + } - /* Below is a clean-up path */ - daemon_deinit_large_buf (d); + /* Below is a clean-up path */ + daemon_deinit_large_buf (d); + } + daemon_deinit_threading_and_conn (d); } - daemon_deinit_threading_and_conn (d); + daemon_deinit_tls (d); } - daemon_deinit_net (d); mhd_assert (MHD_SC_OK != res); return res; @@ -2909,9 +3030,13 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon) daemon_deinit_large_buf (daemon); + daemon_deinit_tls (daemon); + daemon_deinit_net (daemon); } daemon->state = mhd_DAEMON_STATE_STOPPED; /* Useful only for debugging */ free (daemon); + + mhd_lib_deinit_global_if_needed (); } diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c @@ -27,6 +27,9 @@ #include "mhd_sys_options.h" #include "events_process.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include "mhd_locks.h" #include "mhd_socket_type.h" @@ -35,7 +38,7 @@ #ifdef MHD_USE_EPOLL # include <sys/epoll.h> #endif -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # include "sys_errno.h" #endif @@ -101,15 +104,9 @@ update_conn_net_status (struct MHD_Daemon *restrict d, if (err_state) sk_state = (enum mhd_SocketNetState) (sk_state | (unsigned int) mhd_SOCKET_NET_STATE_ERROR_READY); - c->sk_ready = sk_state; + c->sk.ready = sk_state; - if ((0 != - (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info) - & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND))) - || err_state) - mhd_conn_mark_ready (c, d); - else - mhd_conn_mark_unready (c, d); + mhd_conn_mark_ready_update3 (c, err_state, d); } @@ -253,8 +250,8 @@ is_conn_excluded_from_http_comm (struct MHD_Connection *restrict c) #ifdef MHD_UPGRADE_SUPPORT if (NULL != c->upgr.c) { - mhd_assert ((MHD_CONNECTION_UPGRADED == c->state) || \ - (MHD_CONNECTION_UPGRADED_CLEANING == c->state)); + mhd_assert ((mhd_HTTP_STAGE_UPGRADED == c->stage) || \ + (mhd_HTTP_STAGE_UPGRADED_CLEANING == c->stage)); return true; } #endif /* MHD_UPGRADE_SUPPORT */ @@ -312,7 +309,7 @@ daemon_cleanup_upgraded_conns (struct MHD_Daemon *restrict d) if (NULL == c) break; - mhd_assert (MHD_CONNECTION_UPGRADED_CLEANING == c->state); + mhd_assert (mhd_HTTP_STAGE_UPGRADED_CLEANING == c->stage); mhd_upgraded_deinit (c); mhd_conn_pre_clean (c); mhd_conn_remove_from_daemon (c); @@ -339,8 +336,8 @@ close_all_daemon_conns (struct MHD_Daemon *d) c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn)) { #ifdef MHD_UPGRADE_SUPPORT - mhd_assert (MHD_CONNECTION_UPGRADING != c->state); - mhd_assert (MHD_CONNECTION_UPGRADED_CLEANING != c->state); + mhd_assert (mhd_HTTP_STAGE_UPGRADING != c->stage); + mhd_assert (mhd_HTTP_STAGE_UPGRADED_CLEANING != c->stage); if (NULL != c->upgr.c) { mhd_assert (c == c->upgr.c); @@ -389,11 +386,11 @@ fd_set_wrap (MHD_Socket fd, it is added */ mhd_assert (mhd_POLL_TYPE_SELECT == d->events.poll_type); (void) d; /* Unused with non-debug builds */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) FD_SET (fd, fs); if (*max < fd) *max = fd; -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) /* Use custom set function to take advantage of know uniqueness of * used sockets (to skip useless (for this function) check for duplicated * sockets implemented in system's macro). */ @@ -465,21 +462,21 @@ select_update_fdsets (struct MHD_Daemon *restrict d, for (c = mhd_DLINKEDL_GET_FIRST (&(d->conns),all_conn); NULL != c; c = mhd_DLINKEDL_GET_NEXT (c,all_conn)) { - mhd_assert (MHD_CONNECTION_CLOSED != c->state); + mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage); if (is_conn_excluded_from_http_comm (c)) continue; if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV)) - fd_set_wrap (c->socket_fd, + fd_set_wrap (c->sk.fd, rfds, &ret, d); if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_SEND)) - fd_set_wrap (c->socket_fd, + fd_set_wrap (c->sk.fd, wfds, &ret, d); - fd_set_wrap (c->socket_fd, + fd_set_wrap (c->sk.fd, efds, &ret, d); @@ -575,7 +572,7 @@ select_update_statuses_from_fdsets (struct MHD_Daemon *d, if (is_conn_excluded_from_http_comm (c)) continue; - sk = c->socket_fd; + sk = c->sk.fd; recv_ready = FD_ISSET (sk, rfds); send_ready = FD_ISSET (sk, wfds); err_state = FD_ISSET (sk, efds); @@ -622,16 +619,16 @@ get_all_net_updates_by_select (struct MHD_Daemon *restrict d, max_wait = get_max_wait (d); // TODO: use correct timeout value -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK if (0 == max_socket) { Sleep ((unsigned int) max_wait); return true; } -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ tmvl.tv_sec = max_wait / 1000; -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK tmvl.tv_usec = (uint_least16_t) ((max_wait % 1000) * 1000); #else tmvl.tv_usec = (int) ((max_wait % 1000) * 1000); @@ -650,7 +647,7 @@ get_all_net_updates_by_select (struct MHD_Daemon *restrict d, bool is_ignored_error; is_hard_error = false; is_ignored_error = false; -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) err = errno; if (0 != err) { @@ -658,7 +655,7 @@ get_all_net_updates_by_select (struct MHD_Daemon *restrict d, ((mhd_EBADF_OR_ZERO == err) || (mhd_EINVAL_OR_ZERO == err)); is_ignored_error = (mhd_EINTR_OR_ZERO == err); } -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) err = WSAGetLastError (); is_hard_error = ((WSAENETDOWN == err) || (WSAEFAULT == err) || (WSAEINVAL == err) || @@ -735,9 +732,9 @@ poll_update_fds (struct MHD_Daemon *restrict d, mhd_assert ((i_c - i_s) < d->conns.cfg.count_limit); mhd_assert (i_c < d->dbg.num_events_elements); - mhd_assert (MHD_CONNECTION_CLOSED != c->state); + mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage); - d->events.data.poll.fds[i_c].fd = c->socket_fd; + d->events.data.poll.fds[i_c].fd = c->sk.fd; d->events.data.poll.rel[i_c].connection = c; events = 0; if (0 != (c->event_loop_info & MHD_EVENT_LOOP_INFO_RECV)) @@ -846,7 +843,7 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d, c = d->events.data.poll.rel[i_c].connection; mhd_assert (! is_conn_excluded_from_http_comm (c)); - mhd_assert (c->socket_fd == d->events.data.poll.fds[i_c].fd); + mhd_assert (c->sk.fd == d->events.data.poll.fds[i_c].fd); revents = d->events.data.poll.fds[i_c].revents; recv_ready = (0 != (revents & (MHD_POLL_IN | POLLIN))); send_ready = (0 != (revents & (MHD_POLL_OUT | POLLOUT))); @@ -904,7 +901,7 @@ get_all_net_updates_by_poll (struct MHD_Daemon *restrict d, bool is_ignored_error; is_hard_error = false; is_ignored_error = false; -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) err = errno; if (0 != err) { @@ -912,7 +909,7 @@ get_all_net_updates_by_poll (struct MHD_Daemon *restrict d, ((mhd_EFAULT_OR_ZERO == err) || (mhd_EINVAL_OR_ZERO == err)); is_ignored_error = (mhd_EINTR_OR_ZERO == err); } -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) err = WSAGetLastError (); is_hard_error = ((WSAENETDOWN == err) || (WSAEFAULT == err) || (WSAEINVAL == err)); @@ -1103,7 +1100,7 @@ process_all_events_and_data (struct MHD_Daemon *restrict d) case mhd_POLL_TYPE_NOT_SET_YET: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); MHD_PANIC ("Daemon data integrity broken"); } if (d->events.act_req.accept) @@ -1179,7 +1176,7 @@ process_listening_and_itc_only (struct MHD_Daemon *restrict d) else { mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); MHD_PANIC ("Daemon data integrity broken"); } // TODO: Accept connections diff --git a/src/mhd2/http_status_str.c b/src/mhd2/http_status_str.c @@ -32,6 +32,7 @@ #include "sys_base_types.h" #include "mhd_public_api.h" #include "mhd_str_macros.h" +#include "mhd_arr_num_elems.h" #define UNUSED_STATUS {0, NULL} @@ -165,7 +166,7 @@ struct mhd_HttpStatusesBlock const struct MHD_String *const data; }; -#define STATUSES_BLOCK(m) { (sizeof(m) / sizeof(m[0])), m} +#define STATUSES_BLOCK(m) { mhd_ARR_NUM_ELEMS (m), m} static const struct mhd_HttpStatusesBlock statuses[] = { STATUSES_BLOCK (invalid_hundred), diff --git a/src/mhd2/lib_get_info.c b/src/mhd2/lib_get_info.c @@ -0,0 +1,219 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/lib_get_info.c + * @brief The implementation of MHD_lib_get_info_*() functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_assert.h" + +#include "mhd_str_types.h" +#include "mhd_str_macros.h" + +#ifndef VERSION +# include "mhd_str.h" +#endif + +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_choice.h" +/* Include all supported TLS backends headers */ +# if defined(MHD_USE_GNUTLS) +# include "tls_gnu_funcs.h" +# endif +# if defined(MHD_USE_OPENSSL) +# include "tls_open_funcs.h" +# endif +#endif + +#include "mhd_lib_init.h" + +#include "mhd_public_api.h" + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_OUT_ (2) +MHD_FN_PURE_ enum MHD_StatusCode +MHD_lib_get_info_fixed_sz (enum MHD_LibInfoFixed info_type, + union MHD_LibInfoFixedData *return_data, + size_t return_data_size) +{ + switch (info_type) + { + case MHD_LIB_INFO_FIXED_VERSION_NUM: + if (sizeof(return_data->v_uint32) > return_data_size) + return MHD_SC_INFO_GET_BUFF_TOO_SMALL; +#if ! MHD_VERSION +#error MHD_VERSION is not defined +#endif + return_data->v_uint32 = MHD_VERSION; + return MHD_SC_OK; + case MHD_LIB_INFO_FIXED_VERSION_STR: + if (sizeof(return_data->v_string) <= return_data_size) + { +#ifdef VERSION + static const struct MHD_String ver_str = + mhd_MSTR_INIT (VERSION); + return_data->v_string.len = ver_str.len; + return_data->v_string.cstr = ver_str.cstr; + return MHD_SC_OK; +#else /* ! VERSION */ + static char str_buf[10] = + {0, 0, 0, 0, 0, 0, 0, 0, 1, 1}; /* Larger than needed */ + if (0 != str_buf[9]) + { + uint_fast32_t ver_num = MHD_VERSION; + uint8_t digit; + + digit = (uint8_t) (ver_num >> 24); + mhd_bin_to_hex (digit, + 1, + str_buf); + str_buf[3] = '.'; + digit = (uint8_t) (ver_num >> 16); + mhd_bin_to_hex (digit, + 1, + str_buf + 4); + str_buf[6] = '.'; + digit = (uint8_t) (ver_num >> 8); + mhd_bin_to_hex (digit, + 1, + str_buf + 7); + str_buf[9] = 0; + } + return_data->v_string.len = 9; + return_data->v_string.cstr = str_buf; + return MHD_SC_OK; +#endif /* ! VERSION */ + } + return MHD_SC_INFO_GET_BUFF_TOO_SMALL; + case MHD_LIB_INFO_FIXED_HAS_MESSAGES: + case MHD_LIB_INFO_FIXED_HAS_THREADS: + case MHD_LIB_INFO_FIXED_HAS_DEBUG: + case MHD_LIB_INFO_FIXED_HAS_COOKIE_PARSER: + case MHD_LIB_INFO_FIXED_HAS_POST_PARSER: + case MHD_LIB_INFO_FIXED_HAS_UPGRADE: + case MHD_LIB_INFO_FIXED_HAS_BASIC_AUTH: + case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH: + case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_RFC2069: + case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_MD5: + case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_SHA256: + case MHD_LIB_INFO_FIXED_TYPE_DIGEST_AUTH_SHA512_256: + case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_AUTH_INT: + case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_ALGO_SESSION: + case MHD_LIB_INFO_FIXED_HAS_DIGEST_AUTH_USERHASH: + case MHD_LIB_INFO_FIXED_TYPE_SOCKETS_POLLING: + case MHD_LIB_INFO_FIXED_HAS_AGGREGATE_FD: + case MHD_LIB_INFO_FIXED_TYPE_IPv6: + case MHD_LIB_INFO_FIXED_HAS_TCP_FASTOPEN: + case MHD_LIB_INFO_FIXED_HAS_AUTODETECT_BIND_PORT: + case MHD_LIB_INFO_FIXED_HAS_SENDFILE: + case MHD_LIB_INFO_FIXED_HAS_AUTOSUPPRESS_SIGPIPE: + case MHD_LIB_INFO_FIXED_HAS_THREAD_NAMES: + case MHD_LIB_INFO_FIXED_TYPE_ITC: + case MHD_LIB_INFO_FIXED_HAS_LARGE_FILE: + mhd_assert (0 && "Not implemented yet"); + break; + case MHD_LIB_INFO_FIXED_TYPE_TLS: + if (sizeof(return_data->v_tls) <= return_data_size) + { +#ifndef MHD_ENABLE_HTTPS + return_data->v_tls.tls_supported = MHD_NO; + return_data->v_tls.backend_gnutls = MHD_NO; + return_data->v_tls.backend_openssl = MHD_NO; +#else + return_data->v_tls.tls_supported = MHD_YES; +# ifdef MHD_USE_GNUTLS + return_data->v_tls.backend_gnutls = MHD_YES; +# else /* ! MHD_USE_GNUTLS */ + return_data->v_tls.backend_gnutls = MHD_NO; +# endif /* ! MHD_USE_GNUTLS */ +# ifdef MHD_USE_OPENSSL + return_data->v_tls.backend_openssl = MHD_YES; +# else /* ! MHD_USE_OPENSSL */ + return_data->v_tls.backend_openssl = MHD_NO; +# endif /* ! MHD_USE_OPENSSL */ +#endif + return MHD_SC_OK; + } + return MHD_SC_INFO_GET_BUFF_TOO_SMALL; + case MHD_LIB_INFO_FIXED_HAS_TLS_KEY_PASSWORD: + mhd_assert (0 && "Not implemented yet"); + break; + case MHD_LIB_INFO_FIXED_SENTINEL: + default: + break; + } + return MHD_SC_INFO_TYPE_UNKNOWN; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ (2) +MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode +MHD_lib_get_info_dynamic_sz (enum MHD_LibInfoDynamic info_type, + union MHD_LibInfoDynamicData *return_data, + size_t return_data_size) +{ + switch (info_type) + { + case MHD_LIB_INFO_DYNAMIC_INITED: + mhd_assert (0 && "Not implemented yet"); + break; + case MHD_LIB_INFO_DYNAMIC_TYPE_TLS: + if (sizeof(return_data->v_tls) <= return_data_size) + { +#ifndef MHD_ENABLE_HTTPS + return_data->v_tls.tls_supported = MHD_NO; + return_data->v_tls.backend_gnutls = MHD_NO; + return_data->v_tls.backend_openssl = MHD_NO; +#else + bool gnutls_avail; + bool openssl_avail; + + if (! mhd_lib_init_global_if_needed ()) + return MHD_SC_INFO_GET_TYPE_UNAVAILALBE; + + gnutls_avail = mhd_tls_gnu_is_inited_fine (); + openssl_avail = mhd_tls_open_is_inited_fine (); + + return_data->v_tls.tls_supported = + (gnutls_avail || openssl_avail) ? + MHD_YES : MHD_NO; + return_data->v_tls.backend_gnutls = gnutls_avail ? MHD_YES : MHD_NO; + return_data->v_tls.backend_openssl = openssl_avail ? MHD_YES : MHD_NO; + + mhd_lib_deinit_global_if_needed (); +#endif + return MHD_SC_OK; + } + return MHD_SC_INFO_GET_BUFF_TOO_SMALL; + case MHD_LIB_INFO_DYNAMIC_SENTINEL: + default: + break; + } + return MHD_SC_INFO_TYPE_UNKNOWN; +} diff --git a/src/mhd2/mhd_arr_num_elems.h b/src/mhd2/mhd_arr_num_elems.h @@ -0,0 +1,37 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_arr_num_elements.h + * @brief The definition of mhd_ARR_NUM_ELEMS macro + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_ARR_NUM_ELEMS_H +#define MHD_ARR_NUM_ELEMS_H 1 + +#include "mhd_sys_options.h" + +/** + * Get the number of elements in the array + */ +#define mhd_ARR_NUM_ELEMS(arr) (sizeof(arr) / sizeof(arr[0])) + +#endif /* ! MHD_ARR_NUM_ELEMS_H */ diff --git a/src/mhd2/mhd_assert.h b/src/mhd2/mhd_assert.h @@ -78,11 +78,4 @@ # endif /* ! HAVE_ASSERT */ #endif /* NDEBUG */ -#ifdef _DEBUG -# ifdef MHD_UNREACHABLE_ -# undef MHD_UNREACHABLE_ -# endif -# define MHD_UNREACHABLE_ ((void) 0) -#endif - #endif /* ! MHD_ASSERT_H */ diff --git a/src/mhd2/mhd_conn_socket.h b/src/mhd2/mhd_conn_socket.h @@ -0,0 +1,169 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_conn_socket.h + * @brief The definition of the connection-specific socket data + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_CONN_SOCKET_H +#define MHD_CONN_SOCKET_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_socket_type.h" +#include "mhd_tristate.h" +#include "mhd_socket_error.h" + + +struct sockaddr_storage; /* Forward declaration */ + +/** + * The network states for connected sockets + * An internal version of #MHD_FdState. Keep in sync! + */ +enum MHD_FIXED_FLAGS_ENUM_ mhd_SocketNetState +{ + /** + * No active states of the socket + */ + mhd_SOCKET_NET_STATE_NOTHING = 0 + , + /** + * The socket is ready for receiving + */ + mhd_SOCKET_NET_STATE_RECV_READY = 1 << 0 + , + /** + * The socket is ready for sending + */ + mhd_SOCKET_NET_STATE_SEND_READY = 1 << 1 + , + /** + * The socket has some unrecoverable error + */ + mhd_SOCKET_NET_STATE_ERROR_READY = 1 << 2 +}; + +/** + * Connection-specific socket state data + */ +struct mhd_ConnSocketState +{ + /** + * The current state of TCP_NODELAY socket setting + */ + enum mhd_Tristate nodelay; + +// #ifndef MHD_SOCKETS_KIND_WINSOCK // TODO: conditionally use in the code + /** + * The current state of TCP_CORK / TCP_NOPUSH socket setting + */ + enum mhd_Tristate corked; +// #endif + + /** + * Set to 'true' when the remote side shut down write/send and + * __the last byte from the remote has been read__. + */ + bool rmt_shut_wr; + + /** + * The type of the error when the socket disconnected early + */ + enum mhd_SocketError discnt_err; +}; + +/** + * Connection-specific socket properties + */ +struct mhd_ConnSocketProperties +{ + /** + * The type of the socket: TCP/IP or non TCP/IP (a UNIX domain socket, a pipe) + */ + enum mhd_Tristate is_nonip; + + /** + * true if the socket is non-blocking, false otherwise. + */ + bool is_nonblck; + + /** + * true if the socket has set SIGPIPE suppression + */ + bool has_spipe_supp; +}; + + +/** + * The connection socket remote address information + */ +struct mhd_ConnSocketAddr +{ + /** + * The remote address. + * Allocated by malloc() (not in the connection memory pool!). + * Could be NULL if the address is not known. + */ + struct sockaddr_storage *data; + + /** + * The size of the address pointed by @a data. + * Zero is @a data is NULL. + */ + size_t size; +}; + +/** + * Connection-specific socket data + */ +struct mhd_ConnSocket +{ + /** + * The network socket. + */ + MHD_Socket fd; + + /** + * Connection-specific socket state data + */ + struct mhd_ConnSocketState state; + + /** + * The receive / send / error readiness the socket + */ + enum mhd_SocketNetState ready; + + /** + * Connection-specific socket properties + */ + struct mhd_ConnSocketProperties props; + + /** + * The connection socket remote address information + */ + struct mhd_ConnSocketAddr addr; +}; + +#endif /* ! MHD_CONN_SOCKET_H */ diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h @@ -37,7 +37,8 @@ #include "sys_bool_type.h" #include "sys_base_types.h" -#include "mhd_socket_type.h" + +#include "mhd_conn_socket.h" #include "mhd_threads.h" @@ -55,6 +56,10 @@ #include "mhd_public_api.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_choice.h" /* For the TLS struct forward declaration */ +#endif + /** * Minimum reasonable size by which MHD tries to increment read/write buffers. * We usually begin with half the available pool space for the @@ -74,6 +79,106 @@ struct MHD_Connection; /* forward declaration */ +#define mhd_CONN_FLAG_RECV (1u << 0) +#define mhd_CONN_FLAG_SEND (1u << 1) +#define mhd_CONN_FLAG_TLS (1u << 2) +#define mhd_CONN_FLAG_HANDSHAKE (1u << 3) +#define mhd_CONN_FLAG_CLOSING (1u << 4) +#define mhd_CONN_FLAG_ERROR (1u << 6) +#define mhd_CONN_FLAG_CLOSED (1u << 7) + +/** + * The states of connection TLS layer + * The bits (1 << 0) | (1 << 1) in enum values match the same bits in + * enum MHD_ConnectionEventLoopInfo and in enum mhd_SocketNetState values + */ +enum MHD_FIXED_ENUM_ mhd_ConnState +{ + /** + * TLS not started / plain TCP communication + */ + mhd_CONN_STATE_TCP_CONNECTED = 0 + , + /** + * TLS handshake in progress, need to receive the data + */ + mhd_CONN_STATE_TLS_HANDSHAKE_RECV = + mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_HANDSHAKE | mhd_CONN_FLAG_RECV + , + /** + * TLS handshake in progress, need to send the data + */ + mhd_CONN_STATE_TLS_HANDSHAKE_SEND = + mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_HANDSHAKE | mhd_CONN_FLAG_SEND + , + /** + * TLS connection established, HTTP communication is performing + */ + mhd_CONN_STATE_TLS_CONNECTED = mhd_CONN_FLAG_TLS + , + /** + * Sending TLS message for shutting down TLS communication on the MHD side + */ + mhd_CONN_STATE_TLS_SHUT_WR_SENDING = + mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_SEND + , + /** + * Waiting to receive message from remote for shutting down TLS communication + */ + mhd_CONN_STATE_TLS_LINGERING = + mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_RECV + , + /** + * TLS communication gracefully closed. + * This state should be avoided. Use #mhd_CONN_STATE_TCP_CONNECTED or + * #mhd_CONN_STATE_CLOSED. + */ + mhd_CONN_STATE_TLS_CLOSED = mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_CLOSED + , + /** + * TLS communication broken + */ + mhd_CONN_STATE_TLS_FAILED = mhd_CONN_FLAG_TLS | mhd_CONN_FLAG_ERROR +#if 0 // TODO: Extend to TCP states + , + /** + * Setting TCP shutdown WR + */ + mhd_CONN_STATE_TCP_SHUT_WR_SENDING = + mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_SEND + , + /** + * Waiting for EOF from the remote side + */ + mhd_CONN_STATE_TCP_LINGERING = + mhd_CONN_FLAG_CLOSING | mhd_CONN_FLAG_RECV +#endif + , + /** + * TCP communication closed + */ + mhd_CONN_STATE_CLOSED = mhd_CONN_FLAG_CLOSED +}; + +#ifdef MHD_ENABLE_HTTPS +/** + * The status of TLS buffer for incoming (receive) data + */ +enum mhd_TlsBufDataIn +{ + /** + * No data in pending in the TLS buffer + */ + mhd_TLS_BUF_NO_DATA = 0 + , + /** + * The incoming data in already pending in the TLS buffer + */ + mhd_TLS_BUF_HAS_DATA_IN = mhd_SOCKET_NET_STATE_RECV_READY +}; +#endif /* MHD_ENABLE_HTTPS */ + + /** * What is this connection waiting for? */ @@ -114,267 +219,239 @@ enum MHD_FIXED_FLAGS_ENUM_ MHD_ConnectionEventLoopInfo /** - * The network states for connected sockets - * An internal version of #MHD_FdState. Keep in sync! + * The reason for the connection closure */ -enum MHD_FIXED_FLAGS_ENUM_ mhd_SocketNetState -{ - /** - * No active states of the socket - */ - mhd_SOCKET_NET_STATE_NOTHING = 0 - , - /** - * The socket is ready for receiving - */ - mhd_SOCKET_NET_STATE_RECV_READY = 1 << 0 - , - /** - * The socket is ready for sending - */ - mhd_SOCKET_NET_STATE_SEND_READY = 1 << 1 - , - /** - * The socket has some unrecoverable error - */ - mhd_SOCKET_NET_STATE_ERROR_READY = 1 << 2 -}; - - -/** - * The reason for the socket closure - */ -enum mhd_SocketClosureReason +enum mhd_ConnClosureReason { /** * The socket is not closed / closing. */ - mhd_SCOKET_CLOSURE_REASON_NO_CLOSURE = 0 + mhd_CONN_CLOSURE_REASON_NO_CLOSURE = 0 , /** * Socket has to be closed because HTTP protocol successfully finished data * exchange. */ - mhd_SCOKET_CLOSURE_REASON_PROTOCOL_SUCCESS + mhd_CONN_CLOSURE_REASON_PROTOCOL_SUCCESS , /** * Socket has to be closed because remote side violated some HTTP * specification requirements or request processed with an error. * The HTTP error response should be sent. */ - mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_SOFT + mhd_CONN_CLOSURE_REASON_PROTOCOL_FAILURE_SOFT , /** * Timeout expired */ - mhd_SCOKET_CLOSURE_REASON_TIMEOUT + mhd_CONN_CLOSURE_REASON_TIMEOUT , /** * Socket has to be closed because received data cannot be interpreted as * valid HTTP data. */ - mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_HARD + mhd_CONN_CLOSURE_REASON_PROTOCOL_FAILURE_HARD , /** * Unrecoverable TLS error */ - mhd_SCOKET_CLOSURE_REASON_TLS_ERROR + mhd_CONN_CLOSURE_REASON_TLS_ERROR , /** * The remote side closed connection in abortive way */ - mhd_SCOKET_CLOSURE_REASON_REMOTE_HARD_DISCONN + mhd_CONN_CLOSURE_REASON_REMOTE_HARD_DISCONN , /** * The connection has been broken for some reason */ - mhd_SCOKET_CLOSURE_REASON_CONN_BROKEN + mhd_CONN_CLOSURE_REASON_CONN_BROKEN }; /** * States in a state machine for a connection. * - * The main transitions are any-state to #MHD_CONNECTION_CLOSED, any - * state to state+1, #MHD_CONNECTION_FOOTERS_SENT to - * #MHD_CONNECTION_INIT. #MHD_CONNECTION_CLOSED is the terminal state - * and #MHD_CONNECTION_INIT the initial state. + * The main transitions are any-state to #mhd_HTTP_STAGE_CLOSED, any + * state to state+1, #mhd_HTTP_STAGE_FOOTERS_SENT to + * #mhd_HTTP_STAGE_INIT. #mhd_HTTP_STAGE_CLOSED is the terminal state + * and #mhd_HTTP_STAGE_INIT the initial state. * * Note that transitions for *reading* happen only after the input has * been processed; transitions for *writing* happen after the * respective data has been put into the write buffer (the write does * not have to be completed yet). A transition to - * #MHD_CONNECTION_CLOSED or #MHD_CONNECTION_INIT requires the write + * #mhd_HTTP_STAGE_CLOSED or #mhd_HTTP_STAGE_INIT requires the write * to be complete. */ -enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE +enum MHD_FIXED_ENUM_ mhd_HttpStage { /** * Connection just started (no headers received). * Waiting for the line with the request type, URL and version. */ - MHD_CONNECTION_INIT = 0 + mhd_HTTP_STAGE_INIT = 0 , /** * Part of the request line was received. * Wait for complete line. */ - MHD_CONNECTION_REQ_LINE_RECEIVING + mhd_HTTP_STAGE_REQ_LINE_RECEIVING , /** * We got the URL (and request type and version). Wait for a header line. * * A milestone state. No received data is processed in this state. */ - MHD_CONNECTION_REQ_LINE_RECEIVED + mhd_HTTP_STAGE_REQ_LINE_RECEIVED , /** * Receiving request headers. Wait for the rest of the headers. */ - MHD_CONNECTION_REQ_HEADERS_RECEIVING + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING , /** * We got the request headers. Process them. */ - MHD_CONNECTION_HEADERS_RECEIVED + mhd_HTTP_STAGE_HEADERS_RECEIVED , /** * We have processed the request headers. Call application callback. */ - MHD_CONNECTION_HEADERS_PROCESSED + mhd_HTTP_STAGE_HEADERS_PROCESSED , /** * We have processed the headers and need to send 100 CONTINUE. */ - MHD_CONNECTION_CONTINUE_SENDING + mhd_HTTP_STAGE_CONTINUE_SENDING , /** * We have sent 100 CONTINUE (or do not need to). Read the message body. */ - MHD_CONNECTION_BODY_RECEIVING + mhd_HTTP_STAGE_BODY_RECEIVING , /** * We got the request body. * * A milestone state. No received data is processed in this state. */ - MHD_CONNECTION_BODY_RECEIVED + mhd_HTTP_STAGE_BODY_RECEIVED , /** * We are reading the request footers. */ - MHD_CONNECTION_FOOTERS_RECEIVING + mhd_HTTP_STAGE_FOOTERS_RECEIVING , /** * We received the entire footer. * * A milestone state. No data is receiving in this state. */ - MHD_CONNECTION_FOOTERS_RECEIVED + mhd_HTTP_STAGE_FOOTERS_RECEIVED , /** * We received the entire request. * * A milestone state. No data is receiving in this state. */ - MHD_CONNECTION_FULL_REQ_RECEIVED + mhd_HTTP_STAGE_FULL_REQ_RECEIVED , /** * Finished receiving request data: either complete request received or * MHD is going to send reply early, without getting full request. */ - MHD_CONNECTION_REQ_RECV_FINISHED + mhd_HTTP_STAGE_REQ_RECV_FINISHED , /** * Finished reading of the request and the response is ready. * Switch internal logic from receiving to sending, prepare connection * sending the reply and build the reply header. */ - MHD_CONNECTION_START_REPLY + mhd_HTTP_STAGE_START_REPLY , /** * We have prepared the response headers in the write buffer. * Send the response headers. */ - MHD_CONNECTION_HEADERS_SENDING + mhd_HTTP_STAGE_HEADERS_SENDING , /** * We have sent the response headers. Get ready to send the body. */ - MHD_CONNECTION_HEADERS_SENT + mhd_HTTP_STAGE_HEADERS_SENT #ifdef MHD_UPGRADE_SUPPORT , /** * Sending special HTTP "Upgrade" headers */ - MHD_CONNECTION_UPGRADE_HEADERS_SENDING + mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING #endif /* MHD_UPGRADE_SUPPORT */ , /** * We are waiting for the client to provide more * data of a non-chunked body. */ - MHD_CONNECTION_UNCHUNKED_BODY_UNREADY + mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY , /** * We are ready to send a part of a non-chunked body. Send it. */ - MHD_CONNECTION_UNCHUNKED_BODY_READY + mhd_HTTP_STAGE_UNCHUNKED_BODY_READY , /** * We are waiting for the client to provide a chunk of the body. */ - MHD_CONNECTION_CHUNKED_BODY_UNREADY + mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY , /** * We are ready to send a chunk. */ - MHD_CONNECTION_CHUNKED_BODY_READY + mhd_HTTP_STAGE_CHUNKED_BODY_READY , /** * We have sent the chunked response body. Prepare the footers. */ - MHD_CONNECTION_CHUNKED_BODY_SENT + mhd_HTTP_STAGE_CHUNKED_BODY_SENT , /** * We have prepared the response footer. Send it. */ - MHD_CONNECTION_FOOTERS_SENDING + mhd_HTTP_STAGE_FOOTERS_SENDING , /** * We have sent the entire reply. * Shutdown connection or restart processing to get a new request. */ - MHD_CONNECTION_FULL_REPLY_SENT + mhd_HTTP_STAGE_FULL_REPLY_SENT #ifdef MHD_UPGRADE_SUPPORT , /** * Transition to "Upgraded" state */ - MHD_CONNECTION_UPGRADING + mhd_HTTP_STAGE_UPGRADING , /** * Sending and receiving data on HTTP-Upgraded connection channel. * Normal data processing and connection handling is not performed * by MHD anymore. */ - MHD_CONNECTION_UPGRADED + mhd_HTTP_STAGE_UPGRADED , /** * Closing HTTP-Upgraded connection */ - MHD_CONNECTION_UPGRADED_CLEANING + mhd_HTTP_STAGE_UPGRADED_CLEANING #endif /* MHD_UPGRADE_SUPPORT */ , /** * Finished regular connection processing. * Initial buffers cleanup and freeing. */ - MHD_CONNECTION_PRE_CLOSING + mhd_HTTP_STAGE_PRE_CLOSING , /** * This connection is to be closed. */ - MHD_CONNECTION_CLOSED + mhd_HTTP_STAGE_CLOSED }; @@ -383,6 +460,7 @@ struct mhd_ConnDebugData bool closing_started; bool pre_cleaned; bool removed_from_daemon; + bool tls_inited; }; /** @@ -423,20 +501,29 @@ struct MHD_Connection mhd_DLNKDL_LINKS (MHD_Connection,all_conn); /** - * The state of the connected socket + * The connection socket data */ - enum mhd_SocketNetState sk_ready; + struct mhd_ConnSocket sk; +#ifdef MHD_ENABLE_HTTPS /** - * The type of the error when disconnected early + * Connection-specific TLS data. + * NULL if TLS is not used (plain HTTP connection). + * Allocated (and freed) together with struct MHD_Connection, cannot be + * deallocated separately. */ - enum mhd_SocketError sk_discnt_err; + struct mhd_TlsConnData *tls; /** - * Set to 'true' when the client shut down write/send and - * __the last byte from the remote has been read__. + * The state of the communication layer */ - bool sk_rmt_shut_wr; + enum mhd_ConnState conn_state; + + /** + * Status of TLS buffer for the incoming data + */ + enum mhd_TlsBufDataIn tls_has_data_in; +#endif /* MHD_ENABLE_HTTPS */ /** * 'true' if connection is in 'process ready' list, @@ -530,12 +617,6 @@ struct MHD_Connection */ char *write_buffer; - /** - * Foreign address (of length @e addr_len). MALLOCED (not - * in pool!). - */ - struct sockaddr_storage *addr; - #if defined(MHD_USE_THREADS) /** * Thread handle for this connection (if we are using @@ -580,11 +661,6 @@ struct MHD_Connection size_t continue_message_write_offset; /** - * Length of the foreign address. - */ - size_t addr_len; - - /** * Last time this connection had any activity * (reading or writing). */ @@ -598,40 +674,6 @@ struct MHD_Connection uint_fast64_t connection_timeout_ms; /** - * Socket for this connection. Set to #MHD_INVALID_SOCKET if - * this connection has died (daemon should clean - * up in that case). - */ - MHD_Socket socket_fd; - - /** - * The type of the socket: TCP/IP or non TCP/IP (a UNIX domain socket, a pipe) - */ - enum mhd_Tristate is_nonip; - - /** - * true if @a socket_fd is non-blocking, false otherwise. - */ - bool sk_nonblck; - - /** - * true if connection socket has set SIGPIPE suppression - */ - bool sk_spipe_suppress; - -// #ifndef MHD_WINSOCK_SOCKETS // TODO: conditionally use in the code - /** - * Tracks TCP_CORK / TCP_NOPUSH of the connection socket. - */ - enum mhd_Tristate sk_corked; -// #endif - - /** - * Tracks TCP_NODELAY state of the connection socket. - */ - enum mhd_Tristate sk_nodelay; - - /** * Some error happens during processing the connection therefore this * connection must be closed. * The error may come from the client side (like wrong request format), @@ -664,7 +706,7 @@ struct MHD_Connection /** * State in the FSM for this connection. */ - enum MHD_CONNECTION_STATE state; + enum mhd_HttpStage stage; /** * What is this connection waiting for? @@ -679,5 +721,32 @@ struct MHD_Connection #endif }; +#ifdef MHD_ENABLE_HTTPS +/** + * Returns non-zero if connection has TLS enabled or zero otherwise + */ +# define mhd_C_HAS_TLS(c) (((c)->tls) ? (! 0) : (0)) +#else /* ! MHD_ENABLE_HTTPS */ +/** + * Returns non-zero if connection has TLS enabled or zero otherwise + */ +# define mhd_C_HAS_TLS(c) (0) +#endif /* ! MHD_ENABLE_HTTPS */ + + +#ifdef MHD_ENABLE_HTTPS +/** + * Returns #mhd_SOCKET_NET_STATE_RECV_READY if connection has incoming data + * pending in TLS buffers + */ +# define mhd_C_HAS_TLS_DATA_IN(c) \ + (((c)->tls) ? ((unsigned int) ((c)->tls_has_data_in)) : (0u)) +#else /* ! MHD_ENABLE_HTTPS */ +/** + * Returns #mhd_SOCKET_NET_STATE_RECV_READY if connection has incoming data + * pending in TLS buffers + */ +# define mhd_C_HAS_TLS_DATA_IN(c) (0) +#endif /* ! MHD_ENABLE_HTTPS */ #endif /* ! MHD_CONNECTION_H */ diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h @@ -35,6 +35,10 @@ #include "mhd_public_api.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_choice.h" +#endif + #ifdef MHD_USE_THREADS # include "mhd_threads.h" # include "mhd_itc_types.h" @@ -461,7 +465,7 @@ struct mhd_ListenSocket */ struct mhd_DaemonNetworkSettings { -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX /** * The maximum number for the network FDs. * The valid FD number must be less then @a max_fd_num. @@ -873,6 +877,7 @@ struct mhd_daemon_debug { bool net_inited; bool net_deinited; + bool tls_inited; bool events_allocated; unsigned int num_events_elements; bool events_fully_inited; @@ -913,6 +918,15 @@ struct MHD_Daemon */ struct mhd_DaemonNetwork net; +#ifdef MHD_ENABLE_HTTPS + /** + * The pointer to the daemon TLS data. + * If set to non-NULL then HTTPS protocol is used, if set to NULL then + * plain HTTP protocol used. + */ + struct mhd_TlsDaemonData *tls; +#endif + #ifdef MHD_USE_THREADS /* Threading data */ @@ -959,7 +973,7 @@ struct MHD_Daemon }; -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX /** * Checks whether @a fd socket number fits limitations for the @a d_ptr daemon */ @@ -977,6 +991,13 @@ struct MHD_Daemon # define mhd_D_IS_USING_EPOLL(d) (0) #endif +/** + * Check whether the daemon has edge-triggered sockets polling + */ +#define mhd_D_HAS_EDGE_TRIGG(d) \ + ((mhd_WM_INT_EXTERNAL_EVENTS_EDGE == (d)->wmode_int) || \ + mhd_D_IS_USING_EPOLL (d)) + #ifdef MHD_USE_THREADS # define mhd_D_HAS_THREADS(d) mhd_WM_INT_HAS_THREADS ((d)->wmode_int) #else @@ -1002,4 +1023,17 @@ struct MHD_Daemon (mhd_D_IS_USING_EPOLL (d) || \ (mhd_WM_INT_EXTERNAL_EVENTS_EDGE ==((d)->wmode_int))) +#ifdef MHD_ENABLE_HTTPS +/** + * Returns non-zero if daemon has TLS enabled or zero otherwise + */ +# define mhd_D_HAS_TLS(d) (((d)->tls) ? (! 0) : (0)) +#else +/** + * Returns non-zero if daemon has TLS enabled or zero otherwise + */ +# define mhd_D_HAS_TLS(d) (0) +#endif + + #endif /* ! MHD_DAEMON_H */ diff --git a/src/mhd2/mhd_iovec.h b/src/mhd2/mhd_iovec.h @@ -50,7 +50,7 @@ #include "mhd_limits.h" -#if defined(MHD_WINSOCK_SOCKETS) +#if defined(MHD_SOCKETS_KIND_WINSOCK) /** * Internally used I/O vector type for use with winsock. * Binary matches system "WSABUF". diff --git a/src/mhd2/mhd_itc.c b/src/mhd2/mhd_itc.c @@ -25,19 +25,21 @@ * @author Karlson2k (Evgeny Grin) */ +#include "mhd_sys_options.h" + #include "mhd_itc.h" #if defined(MHD_ITC_PIPE_) # ifdef MHD_HAVE_MHD_ITC_NONBLOCKING # include "mhd_sockets_funcs.h" -# ifndef MHD_POSIX_SOCKETS +# ifndef MHD_SOCKETS_KIND_POSIX #error Pipe-based ITC can be used only with POSIX sockets # endif MHD_INTERNAL bool mhd_itc_nonblocking (struct mhd_itc *pitc) { - return mhd_socket_nonblocking ((MHD_Socket) pitc->sk[0]) && - mhd_socket_nonblocking ((MHD_Socket) pitc->sk[1]); + return mhd_socket_nonblocking ((MHD_Socket) pitc->fd[0]) && + mhd_socket_nonblocking ((MHD_Socket) pitc->fd[1]); } diff --git a/src/mhd2/mhd_itc.h b/src/mhd2/mhd_itc.h @@ -32,6 +32,8 @@ */ #ifndef MHD_ITC_H #define MHD_ITC_H 1 +#include "mhd_sys_options.h" + #include "mhd_itc_types.h" #include "mhd_panic.h" diff --git a/src/mhd2/mhd_lib_init.c b/src/mhd2/mhd_lib_init.c @@ -19,71 +19,431 @@ */ /** - * @file src/mhd2/mhd_lib_init_impl.c + * @file src/mhd2/mhd_lib_init.c * @brief Library global initialisers and de-initialisers * @author Karlson2k (Evgeny Grin) */ #include "mhd_sys_options.h" -#include "mhd_lib_init.h" + #include "mhd_panic.h" -#include "mhd_mono_clock.h" + +#include "sys_base_types.h" +#include "sys_bool_type.h" + +#include "mhd_locks.h" + #include "mhd_socket_type.h" -#include "mhd_send.h" -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK # include <winsock2.h> #endif -void -mhd_lib_global_init (void) +#include "mhd_assert.h" + +#ifndef NDEBUG +# include <stdio.h> /* For debug error reporting */ +# include <stdlib.h> /* For debug error exit */ +#endif + +#include "mhd_mono_clock.h" +#include "mhd_send.h" +#include "mhd_tls_funcs.h" + +#include "mhd_lib_init.h" +#include "mhd_lib_init_auto.h" + +#ifdef AIF_W32_USR_DLLMAIN_NAME +# include <windows.h> /* For DisableThreadLibraryCalls() */ +#endif + +#if defined(mhd_AUTOINIT_FUNCS_USE) +/** + * The function is automatically called to perform global lazy initialisation + */ +# define mhd_INIT_LAZY_BY_FUNC 1 +#elif defined(mhd_MUTEX_INITIALISER_STAT) +/** + * Global lazy initialisation is performed by defined variables with static + * initialisation values + */ +# define mhd_INIT_LAZY_BY_STATIC 1 +#endif + +#if defined(mhd_INIT_LAZY_BY_FUNC) || defined(mhd_INIT_LAZY_BY_STATIC) +/** + * Global lazy initialisation is automatic (either by automatic functions or + * by static initialisation) + */ +# define mhd_INIT_LAZY_AUTOMATIC 1 +#endif + +/* + * *** Minimal (lazy) initialisation section *** + */ + +/** + * The magic value to determine the library initialisation status + */ +#define mhd_LIB_INIT_MARKER_VALUE 0xB167A105 /* "Big Talos" */ + + +#ifndef mhd_INIT_LAZY_BY_STATIC +# ifdef mhd_INIT_LAZY_BY_FUNC +/* Markers of performed lazy initialisation */ +/* Do not initialise statically to avoid breaking by too early automatic + initialisation in function, which is then overwritten by library or + application initialisation. */ +/** + * The indicator of performed global lazy initialisation. + * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_marker; +/** + * The indicator of performed global lazy initialisation. + * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_Nmarker; +# else /* ! mhd_INIT_LAZY_BY_FUNC */ +/* Markers of performed lazy initialisation */ +/** + * The indicator of performed global lazy initialisation. + * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_marker = 0; +/** + * The indicator of performed global lazy initialisation. + * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_Nmarker = 0; +# endif + +/* Variables used for full initialisation */ +/** + * The number of user of library global resources. + * In practice the value should correspond to number of running daemons plus + * number of any possible executed functions with one use of global resources. + */ +static volatile size_t mhd_lib_use_counter; +/** + * Indicates that library was already fully initialised at least one time. + * Some resources that do not require re-initialisation, skipped from repeated + * global initialisation (after deinitialisation). + */ +static volatile bool mhd_lib_fully_inited_once; +# ifdef MHD_USE_THREADS + +/** + * The mutex to control access to full global initialisers and deinitialisers + */ +static mhd_mutex mhd_init_mutex; +# endif /* MHD_USE_THREADS */ +#else /* mhd_INIT_LAZY_BY_STATIC */ +/* Markers of performed lazy initialisation */ +/** + * The indicator of performed global lazy initialisation. + * Have #mhd_LIB_INIT_MARKER_VALUE value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_marker = + (uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE; +/** + * The indicator of performed global lazy initialisation. + * Have (~ #mhd_LIB_INIT_MARKER_VALUE) value when initialised. + */ +static volatile uint_fast32_t mhd_lib_global_init_Nmarker = + (uint_fast32_t) ~((uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE); +/* Variables used for full initialisation */ +/** + * The number of user of library global resources. + * In practice the value should correspond to number of running daemons plus + * number of any possible executed functions with one use of global resources. + */ +static volatile size_t mhd_lib_use_counter = 0; +/** + * Indicates that library was already fully initialised at least one time. + * Some resources that do not require re-initialisation, skipped from repeated + * global initialisation (after deinitialisation). + */ +static volatile bool mhd_lib_fully_inited_once = false; +/** + * The mutex to control access to full global initialisers and deinitialisers + */ +mhd_MUTEX_STATIC_DEFN_INIT (mhd_init_mutex); +#endif /* mhd_INIT_LAZY_BY_STATIC */ + + +/** + * Check whether the markers of initialisation set to "initialised" values. + */ +#define mhd_LIB_INIT_LAZY_IS_PERFORMED() \ + ((mhd_lib_global_init_marker == \ + ((uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE)) \ + && (mhd_lib_global_init_marker == ~mhd_lib_global_init_Nmarker)) + +/** + * Perform global lazy initialisation. + * If library is initialised statically, this function must never be called + * unless automatic initialisation has failed. + * This function does not perform any checking whether the library has been + * initialised before. + * @return 'true' if succeed, + * 'false' if failed + */ +static bool +mhd_lib_global_lazy_init (void) { - mhd_panic_init_default (); + mhd_panic_init_default (); /* Just set a few variables to NULL */ + if (! mhd_mutex_init (&mhd_init_mutex)) + return false; + mhd_lib_fully_inited_once = false; + mhd_lib_use_counter = 0; + mhd_lib_global_init_marker = (uint_fast32_t) mhd_LIB_INIT_MARKER_VALUE; + mhd_lib_global_init_Nmarker = (uint_fast32_t) ~mhd_lib_global_init_marker; + return true; +} + + +#ifdef mhd_AUTOINIT_FUNCS_USE + +/** + * Perform de-initialisation of the resources previously initialised by + * #mhd_lib_global_lazy_init(). + * This function does not perform any checking whether the library has been + * initialised or de-initialised before. + */ +static void +mhd_lib_global_lazy_deinit (void) +{ + mhd_lib_global_init_Nmarker = 0u; + mhd_lib_global_init_marker = 0u; + (void) mhd_mutex_destroy (&mhd_init_mutex); +} -#if defined(MHD_WINSOCK_SOCKETS) - if (1) + +#endif /* mhd_AUTOINIT_FUNCS_USE */ + + +/* + * *** The automatically called functions section *** + */ + +#ifdef mhd_AUTOINIT_FUNCS_USE + +void +mhd_lib_global_init_auto (void) +{ + if (! mhd_lib_global_lazy_init ()) { - WSADATA wsd; - if ((0 != WSAStartup (MAKEWORD (2, 2), &wsd)) || (MAKEWORD (2, 2) != wsd. - wVersion)) - MHD_PANIC ("Failed to initialise WinSock."); + (void) 0; + /* Do not abort in non-debug builds, weak workarounds will be used */ +#ifndef NDEBUG + MHD_PANIC ("Failed to initialise the MHD library"); +#endif /* ! NDEBUG */ } -#endif /* MHD_WINSOCK_SOCKETS */ - MHD_monotonic_msec_counter_init(); - mhd_send_init_static_vars(); } void -mhd_lib_global_deinit (void) +mhd_lib_global_deinit_auto (void) +{ +#ifndef NDEBUG + if (! mhd_LIB_INIT_LAZY_IS_PERFORMED ()) + { + fprintf (stderr, "Automatic MHD library initialisation has not been " + "performed, but the library de-initialisation is called.\n"); + fflush (stderr); + abort (); + } + if (0 != mhd_lib_use_counter) + { + fprintf (stderr, "Automatic MHD library de-initialisation started, but " + "some MHD resources are still in use by the application.\n"); + fflush (stderr); + } +#endif /* ! NDEBUG */ + mhd_lib_global_lazy_deinit (); +} + + +# ifdef AIF_W32_USR_DLLMAIN_NAME + +AIF_DECL_USR_DLLMAIN /* Declare the function */ + +/* MHD is used on W32 as DLL library with DLL runtime lib */ +/** + * Special automatically called function for DLL initialisation on W32. + * @param hinst the DLL module handle + * @param reason the code of the call reason + * @param pReserved NULL is statically loaded, non-NULL is loaded dynamically + * @return TRUE if succeed (always), + * FALSE if failed + */ +BOOL WINAPI +AUTOINIT_FUNCS_USR_DLLMAIN_NAME (HINSTANCE hinst, + DWORD reason, + LPVOID pReserved) +{ + (void) pReserved; /* Not used */ + + /* Disable calls with DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages */ + if (AIF_W32_DLL_PROCESS_ATTACH == reason) + (void) DisableThreadLibraryCalls ((HMODULE) hinst); + + return TRUE; +} + + +# endif /* AIF_W32_USR_DLLMAIN_NAME */ + +#endif /* mhd_AUTOINIT_FUNCS_USE */ + + +/* + * *** Full global initialisation, deinitialisation and re-initialisaion *** + */ + +#if defined(MHD_SOCKETS_KIND_WINSOCK) +/** + * Initialise W32 sockets + * @return 'true' if succeed, + * 'false' if failed + */ +MHD_static_inline_ bool +mhd_lib_sockets_init_w32 (void) +{ + WSADATA wsd; + if (0 != WSAStartup (MAKEWORD (2, 2), &wsd)) + return false; + if (MAKEWORD (2, 2) != wsd.wVersion) + { + WSACleanup (); + return false; + } + return true; +} + + +/** + * De-initialise W32 sockets + */ +MHD_static_inline_ void +mhd_lib_sockets_deinit_w32 (void) { - MHD_monotonic_msec_counter_finish(); -#if defined(MHD_WINSOCK_SOCKETS) (void) WSACleanup (); -#endif /* MHD_WINSOCK_SOCKETS */ } -#ifndef _AUTOINIT_FUNCS_ARE_SUPPORTED -static volatile int mhd_lib_global_inited = 0; -static volatile int mhd_lib_global_not_inited = ! 0; +#else /* ! MHD_SOCKETS_KIND_WINSOCK */ +/* No-op implementations */ +# define mhd_lib_sockets_init_w32() (true) +# define mhd_lib_sockets_deinit_w32() ((void) 0) +#endif /* ! MHD_SOCKETS_KIND_WINSOCK */ -MHD_EXTERN_ void -MHD_lib_global_check_init (void) +/** + * Perform full initialisation of MHD library global resources. + * Must be called only with initialisation lock held. + * @return 'true' if succeed, + * 'false' if failed + */ +static bool +mhd_lib_global_full_init_once (void) { - if ((! mhd_lib_global_inited) || (mhd_lib_global_not_inited)) - mhd_lib_global_init (); - mhd_lib_global_inited = ! 0; - mhd_lib_global_not_inited = 0; + mhd_assert (mhd_LIB_INIT_LAZY_IS_PERFORMED ()); + mhd_assert (! mhd_lib_fully_inited_once); + mhd_assert (0 == mhd_lib_use_counter); + + if (! mhd_lib_sockets_init_w32 ()) + return false; + mhd_mclock_init_once (); + mhd_send_init_once (); + mhd_tls_global_init_once (); + + mhd_lib_fully_inited_once = true; + + return true; } -MHD_EXTERN_ void -MHD_lib_global_check_deinit (void) +/** + * Release library global resources allocated + * by #mhd_lib_global_full_init_once() + */ +static void +mhd_lib_global_full_deinit (void) { - if ((mhd_lib_global_inited) && (! mhd_lib_global_not_inited)) - mhd_lib_global_deinit (); - mhd_lib_global_inited = 0; - mhd_lib_global_not_inited = ! 0; + mhd_tls_global_deinit (); + mhd_mclock_deinit (); + mhd_lib_sockets_deinit_w32 (); +} + + +/** + * Re-initialise library global resources after + * de-initialistion by #mhd_lib_global_full_deinit(). + * This function can be called many times. + * @return 'true' if succeed, + * 'false' if failed + */ +static bool +mhd_lib_global_full_re_init (void) +{ + mhd_assert (mhd_lib_fully_inited_once); + if (! mhd_lib_sockets_init_w32 ()) + return false; + mhd_mclock_re_init (); + mhd_tls_global_re_init (); + + return true; } -#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ +/* + * *** Automatic global initialisation and deinitialisation for daemons *** + */ + +MHD_INTERNAL bool +mhd_lib_init_global_if_needed (void) +{ + bool ret; + if (! mhd_LIB_INIT_LAZY_IS_PERFORMED ()) + { +#if defined (mhd_INIT_LAZY_AUTOMATIC) && ! defined(NDEBUG) + /* Problem detected: the library must be already initialised + automatically, but it is not. */ + abort (); /* abort if this is a debug build */ +#else /* !mhd_INIT_LAZY_AUTOMATIC || NDEBUG */ + if (! mhd_lib_global_lazy_init ()) /* Not thread safe, but no choice here */ + return false; +#endif /* !mhd_INIT_LAZY_AUTOMATIC || NDEBUG */ + } + + if (! mhd_mutex_lock (&mhd_init_mutex)) + return false; + if (0 == mhd_lib_use_counter) + { + if (! mhd_lib_fully_inited_once) + ret = mhd_lib_global_full_init_once (); + else + ret = mhd_lib_global_full_re_init (); + } + else + { + mhd_assert (mhd_lib_fully_inited_once); + ret = true; + } + if (ret) + ++mhd_lib_use_counter; + mhd_mutex_unlock_chk (&mhd_init_mutex); + + return ret; +} + + +MHD_INTERNAL void +mhd_lib_deinit_global_if_needed (void) +{ + mhd_assert (0 != mhd_lib_use_counter); + + mhd_mutex_lock_chk (&mhd_init_mutex); + if (0 == --mhd_lib_use_counter) + mhd_lib_global_full_deinit (); + mhd_mutex_unlock_chk (&mhd_init_mutex); +} diff --git a/src/mhd2/mhd_lib_init.h b/src/mhd2/mhd_lib_init.h @@ -27,40 +27,24 @@ #ifndef MHD_LIB_INIT_H #define MHD_LIB_INIT_H 1 #include "mhd_sys_options.h" -#include "autoinit_funcs.h" -/** - * Initialise library global resources - */ -void -mhd_lib_global_init (void); - -/** - * Deinitialise and free library global resources - */ -void -mhd_lib_global_deinit (void); - -#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED -# define MHD_GLOBAL_INIT_CHECK() ((void) 0) -#else /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ -/* The functions are exported, but not declared in public header */ +#include "sys_bool_type.h" /** - * Check whether the library was initialised and initialise if needed + * Check whether the library was initialised and initialise if needed. + * Increment number of active users of library global resources. + * @return 'true' if succeed, + * 'false' if failed */ -MHD_EXTERN_ void -MHD_lib_global_check_init (void); +MHD_INTERNAL bool +mhd_lib_init_global_if_needed (void); /** - * Check whether the library has been de-initialised and de-initialise if needed + * Decrement number of the library active users of global global resources and + * deinitialise the library if no active users left. */ -MHD_EXTERN_ void -MHD_lib_global_check_deinit (void) - -# define MHD_GLOBAL_INIT_CHECK() MHD_lib_global_check_init () - -#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ +MHD_INTERNAL void +mhd_lib_deinit_global_if_needed (void); #endif /* ! MHD_LIB_INIT_H */ diff --git a/src/mhd2/mhd_lib_init_auto.h b/src/mhd2/mhd_lib_init_auto.h @@ -0,0 +1,72 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_lib_init_auto.h + * @brief Declarations for the library global initialiser and deinitialiser + * that called automatically at startup and shutdown of the application + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_LIB_INIT_AUTO_H +#define MHD_LIB_INIT_AUTO_H 1 +#include "mhd_sys_options.h" + +#if (defined(__MINGW32__) || defined(__MINGW64__)) \ + || (defined(_WIN32) && defined(_MT) && defined(_DLL)) +# define mhd_INIT_USE_MHD_DLLMAIN 1 +#endif + +#ifdef mhd_INIT_USE_MHD_DLLMAIN +# define AUTOINIT_FUNCS_CALL_USR_DLLMAIN 1 /* Call of additional DLL init functions on W32 */ +# define AUTOINIT_FUNCS_USR_DLLMAIN_NAME mhd_DllMain /* The name of additional DLL init function on W32 */ +# define AUTOINIT_FUNCS_DECLARE_USR_DLLMAIN 1 /* Automatically declare this function */ +#endif +#define AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C 1 /* This compiler is supported directly */ +#include "autoinit_funcs.h" + +#ifdef AIF_AUTOINIT_FUNCS_ARE_SUPPORTED +/* Use automatically called initialisation functions */ +# define mhd_AUTOINIT_FUNCS_USE 1 +#elif defined(AIF_PRAGMA_INIT_SUPPORTED) && defined(AIF_PRAGMA_FINI_SUPPORTED) +/* Use automatically called initialisation functions */ +# define mhd_AUTOINIT_FUNCS_USE 1 +/* Use "#pragma" to set initialisation functions */ +# define mhd_AUTOINIT_FUNCS_PRAGMA 1 +#endif + + +#ifdef mhd_AUTOINIT_FUNCS_USE +/** + * Perform the minimal initialisation of the library + */ +void +mhd_lib_global_init_auto (void); + +/** + * Deinitialise resources previously initialised by #mhd_lib_global_lazy_init() + */ +void +mhd_lib_global_deinit_auto (void); + +#endif /* mhd_AUTOINIT_FUNCS_USE */ + + +#endif /* ! MHD_LIB_INIT_AUTO_H */ diff --git a/src/mhd2/mhd_lib_init_impl.h b/src/mhd2/mhd_lib_init_impl.h @@ -32,7 +32,7 @@ #endif #define MHD_LIB_INIT_IMPL_H 1 -/* Due to the bug in GCC/binutils, on some platforms (at least on W32) +/* Due to peculiarities of linking, on some platforms (at least on W32) * the automatic initialisation functions are not called when library is used * as a static library and no function is used/referred from the same * object/module/c-file. @@ -42,35 +42,22 @@ #else /* MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C */ #include "mhd_sys_options.h" -#include "mhd_lib_init.h" +#include "mhd_lib_init_auto.h" -/* Forward declarations */ -void -mhd_lib_global_init_wrap (void); -void -mhd_lib_global_deinit_wrap (void); +#ifdef mhd_AUTOINIT_FUNCS_USE +# ifndef mhd_AUTOINIT_FUNCS_PRAGMA +/* Call automatically initialiser and deinitialiser functions */ +AIF_SET_INIT_AND_DEINIT_FUNCS (mhd_lib_global_init_auto, \ + mhd_lib_global_deinit_auto); +# else +/* Call automatically initialiser function */ +#pragma init(mhd_lib_global_init_auto) +/* Call automatically deinitialiser function */ +#pragma fini(mhd_lib_global_deinit_auto) +# endif -void -mhd_lib_global_init_wrap (void) -{ - mhd_lib_global_init (); -} - - -void -mhd_lib_global_deinit_wrap (void) -{ - mhd_lib_global_deinit (); -} - - -#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED - -_SET_INIT_AND_DEINIT_FUNCS (mhd_lib_global_init_wrap, \ - mhd_lib_global_deinit_wrap); - -#endif /* _AUTOINIT_FUNCS_ARE_SUPPORTED */ +#endif /* AIF_AUTOINIT_FUNCS_ARE_SUPPORTED */ #endif /* MHD_LIB_INIT_IMPL_H_IN_DAEMON_CREATE_C */ diff --git a/src/mhd2/mhd_locks.h b/src/mhd2/mhd_locks.h @@ -41,15 +41,15 @@ #ifdef MHD_USE_THREADS -#if defined(MHD_USE_W32_THREADS) -# define MHD_W32_MUTEX_ 1 +#if defined(mhd_THREADS_KIND_W32) +# define mhd_MUTEX_KIND_W32_CS 1 # if _WIN32_WINNT >= 0x0602 /* Win8 or later */ # include <synchapi.h> # else # include <windows.h> # endif -#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS) -# define MHD_PTHREAD_MUTEX_ 1 +#elif defined(HAVE_PTHREAD_H) && defined(mhd_THREADS_KIND_POSIX) +# define mhd_MUTEX_KIND_PTHREAD 1 # include <pthread.h> # ifdef HAVE_STDDEF_H # include <stddef.h> /* for NULL */ @@ -62,20 +62,20 @@ #include "mhd_panic.h" -#if defined(MHD_PTHREAD_MUTEX_) +#if defined(mhd_MUTEX_KIND_PTHREAD) typedef pthread_mutex_t mhd_mutex; -#elif defined(MHD_W32_MUTEX_) +#elif defined(mhd_MUTEX_KIND_W32_CS) typedef CRITICAL_SECTION mhd_mutex; #endif -#if defined(MHD_PTHREAD_MUTEX_) +#if defined(mhd_MUTEX_KIND_PTHREAD) /** * Initialise a new mutex. * @param pmutex the pointer to the mutex * @return nonzero on success, zero otherwise */ # define mhd_mutex_init(pmutex) (! (pthread_mutex_init ((pmutex), NULL))) -#elif defined(MHD_W32_MUTEX_) +#elif defined(mhd_MUTEX_KIND_W32_CS) # if _WIN32_WINNT < 0x0600 /* Before Vista */ /** @@ -97,7 +97,7 @@ typedef CRITICAL_SECTION mhd_mutex; # endif #endif -#ifdef MHD_W32_MUTEX_ +#ifdef mhd_MUTEX_KIND_W32_CS # if _WIN32_WINNT < 0x0600 /* Before Vista */ /** @@ -133,24 +133,31 @@ typedef CRITICAL_SECTION mhd_mutex; # define mhd_mutex_init_short(pmutex) mhd_mutex_init ((pmutex)) #endif -#if defined(MHD_PTHREAD_MUTEX_) +#if defined(mhd_MUTEX_KIND_PTHREAD) # if defined(PTHREAD_MUTEX_INITIALIZER) /** - * Define static mutex and statically initialise it. + * The value to statically initialise mutex */ -# define MHD_MUTEX_STATIC_DEFN_INIT_(m) \ - static mhd_mutex m = PTHREAD_MUTEX_INITIALIZER +# define mhd_MUTEX_INITIALISER_STAT PTHREAD_MUTEX_INITIALIZER # endif /* PTHREAD_MUTEX_INITIALIZER */ #endif -#if defined(MHD_PTHREAD_MUTEX_) +#ifdef mhd_MUTEX_INITIALISER_STAT +/** + * Define static mutex and statically initialise it. + */ +# define mhd_MUTEX_STATIC_DEFN_INIT(m) \ + static mhd_mutex m = mhd_MUTEX_INITIALISER_STAT +#endif + +#if defined(mhd_MUTEX_KIND_PTHREAD) /** * Destroy previously initialised mutex. * @param pmutex the pointer to the mutex * @return nonzero on success, zero otherwise */ # define mhd_mutex_destroy(pmutex) (! (pthread_mutex_destroy ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) +#elif defined(mhd_MUTEX_KIND_W32_CS) /** * Destroy previously initialised mutex. * @param pmutex the pointer to the mutex @@ -160,7 +167,7 @@ typedef CRITICAL_SECTION mhd_mutex; #endif -#if defined(MHD_PTHREAD_MUTEX_) +#if defined(mhd_MUTEX_KIND_PTHREAD) /** * Acquire a lock on previously initialised mutex. * If the mutex was already locked by other thread, function blocks until @@ -169,7 +176,7 @@ typedef CRITICAL_SECTION mhd_mutex; * @return nonzero on success, zero otherwise */ # define mhd_mutex_lock(pmutex) (! (pthread_mutex_lock ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) +#elif defined(mhd_MUTEX_KIND_W32_CS) /** * Acquire a lock on previously initialised mutex. * If the mutex was already locked by other thread, function blocks until @@ -180,14 +187,14 @@ typedef CRITICAL_SECTION mhd_mutex; # define mhd_mutex_lock(pmutex) (EnterCriticalSection ((pmutex)), ! 0) #endif -#if defined(MHD_PTHREAD_MUTEX_) +#if defined(mhd_MUTEX_KIND_PTHREAD) /** * Unlock previously locked mutex. * @param pmutex the pointer to the mutex * @return nonzero on success, zero otherwise */ # define mhd_mutex_unlock(pmutex) (! (pthread_mutex_unlock ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) +#elif defined(mhd_MUTEX_KIND_W32_CS) /** * Unlock previously initialised and locked mutex. * @param pmutex pointer to mutex @@ -230,13 +237,15 @@ typedef CRITICAL_SECTION mhd_mutex; #else /* ! MHD_USE_THREADS */ -#define mhd_mutex_init(ignore) (! 0) -#define mhd_mutex_destroy(ignore) (! 0) -#define mhd_mutex_destroy_chk(ignore) (void) 0 -#define mhd_mutex_lock(ignore) (! 0) -#define mhd_mutex_lock_chk(ignore) (void) 0 -#define mhd_mutex_unlock(ignore) (! 0) -#define mhd_mutex_unlock_chk(ignore) (void) 0 +# define mhd_mutex_init(ignored) (! 0) +# define mhd_MUTEX_INITIALISER_STAT /* empty */ +# define mhd_MUTEX_STATIC_DEFN_INIT(ignored) /* nothing */ +# define mhd_mutex_destroy(ignored) (! 0) +# define mhd_mutex_destroy_chk(ignored) (void) 0 +# define mhd_mutex_lock(ignored) (! 0) +# define mhd_mutex_lock_chk(ignored) (void) 0 +# define mhd_mutex_unlock(ignored) (! 0) +# define mhd_mutex_unlock_chk(ignored) (void) 0 #endif /* ! MHD_USE_THREADS */ diff --git a/src/mhd2/mhd_mempool.c b/src/mhd2/mhd_mempool.c @@ -27,6 +27,8 @@ * + Update code style * + Detect mmap() in configure (it is purely optional!) */ +#include "mhd_sys_options.h" + #include "mhd_mempool.h" #ifdef HAVE_STDLIB_H # include <stdlib.h> diff --git a/src/mhd2/mhd_mono_clock.c b/src/mhd2/mhd_mono_clock.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2015-2022 Karlson2k (Evgeny Grin) + Copyright (C) 2015-2024 Karlson2k (Evgeny Grin) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -19,12 +19,12 @@ /** * @file src/mhd2/mhd_mono_clock.h - * @brief internal monotonic clock functions implementations + * @brief monotonic clock functions implementations * @author Karlson2k (Evgeny Grin) - * - * TODO: update code style */ +#include "mhd_sys_options.h" + #include "mhd_mono_clock.h" #if defined(_WIN32) && ! defined(__CYGWIN__) @@ -37,6 +37,12 @@ # endif /* HAVE_GETTIMEOFDAY */ #endif /* _WIN32 && ! __CYGWIN__ */ +#if defined(HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME) || \ + defined(HAVE_MACH_APPROXIMATE_TIME) +/* Use mach_*_time() functions family */ +# define mhd_USE_MACH_TIME 1 +#endif + #ifdef HAVE_TIME_H # include <time.h> #endif /* HAVE_TIME_H */ @@ -44,29 +50,38 @@ # include <sys/time.h> #endif /* HAVE_SYS_TIME_H */ -#ifdef HAVE_CLOCK_GET_TIME -# include <mach/mach.h> +#if defined(HAVE_CLOCK_GET_TIME) || \ + defined(mhd_USE_MACH_TIME) /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ -# include <mach/clock.h> -/* for clock_get_time() */ - -# define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) +/* also for compatibility with old headers structure */ +# include <mach/mach.h> +#endif -static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; +#ifdef HAVE_CLOCK_GET_TIME +/* for clock_get_time() */ +# include <mach/clock.h> #endif /* HAVE_CLOCK_GET_TIME */ +#if defined(mhd_USE_MACH_TIME) +# include <mach/mach_time.h> +#endif + #ifdef _WIN32 # include <windows.h> #endif /* _WIN32 */ +#include "mhd_assert.h" +#include "mhd_unreachable.h" + + #ifdef HAVE_CLOCK_GETTIME # ifdef CLOCK_REALTIME -# define _MHD_UNWANTED_CLOCK CLOCK_REALTIME +# define mhd_CLOCK_ID_UNWANTED CLOCK_REALTIME # else /* !CLOCK_REALTIME */ -# define _MHD_UNWANTED_CLOCK ((clockid_t) -2) +# define mhd_CLOCK_ID_UNWANTED ((clockid_t) -2) # endif /* !CLOCK_REALTIME */ -static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; +static clockid_t mono_clock_id = mhd_CLOCK_ID_UNWANTED; #endif /* HAVE_CLOCK_GETTIME */ /* sync clocks; reduce chance of value wrap */ @@ -75,11 +90,24 @@ static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; static time_t mono_clock_start; #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ -#if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) -/* The start value shared for timespec_get() and gettimeofday () */ -static time_t gettime_start; -#endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ -static time_t sys_clock_start; +#ifdef HAVE_CLOCK_GET_TIME +# if ! defined(SYSTEM_CLOCK) && defined(REALTIME_CLOCK) +# define SYSTEM_CLOCK REALTIME_CLOCK +# endif +# define mhd_CLOCK_SERV_INVALID ((clock_serv_t) -2) + +static clock_serv_t mono_clock_service = mhd_CLOCK_SERV_INVALID; +#endif /* HAVE_CLOCK_GET_TIME */ + +#if defined(mhd_USE_MACH_TIME) + +/* The numerator to calculate milliseconds */ +static uint_fast32_t mach_time_mls_numer = 0; +/* The denominator to calculate milliseconds */ +static uint_fast64_t mach_time_mls_denom = 0; +/* The starting value. Used to lower chance of the final value wrap. */ +static uint64_t mach_time_start; /* uint64_t is always available with mach */ +#endif #ifdef HAVE_GETHRTIME static hrtime_t hrtime_start; @@ -87,88 +115,289 @@ static hrtime_t hrtime_start; #ifdef _WIN32 # if _WIN32_WINNT >= 0x0600 -static uint64_t tick_start; +static uint64_t tick_start; /* 'uint64_t' is available on W32 always */ # else /* _WIN32_WINNT < 0x0600 */ -static uint64_t perf_freq; -static uint64_t perf_start; +static uint64_t perf_freq; /* 'uint64_t' is available on W32 always */ +static uint64_t perf_start; /* 'uint64_t' is available on W32 always */ # endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ +/* Start values for fallback sources */ +#if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) +/* The start value shared for timespec_get() and gettimeofday () */ +static time_t gettime_start; +#define mhd_HAVE_GETTIME_START_VAR 1 +#endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ +static time_t sys_clock_start; + + +#ifdef HAVE_CLOCK_GET_TIME +/** + * Initialise Darwin-specific resources for 'clock_get_time()' + * @param[out] cur_time the optional pointer to get the current time value, + * can be NULL + * @return 'true' if succeed, + * 'false' if failed + */ +static MHD_FN_PAR_OUT_ (1) MHD_FN_MUST_CHECK_RESULT_ bool +mclock_init_clock_get_time (mach_timespec_t *cur_time) +{ + mhd_assert (mhd_CLOCK_SERV_INVALID == mono_clock_service); + + if (KERN_SUCCESS == host_get_clock_service (mach_host_self (), + SYSTEM_CLOCK, + &mono_clock_service)) + return false; + + if (NULL != cur_time) + { + if (KERN_SUCCESS != clock_get_time (mono_clock_service, + cur_time)) + { + (void) mach_port_deallocate (mach_task_self (), + mono_clock_service); + mono_clock_service = mhd_CLOCK_SERV_INVALID; + return false; + } + } + + return true; +} + + +/** + * De-initialise Darwin-specific resources for 'clock_get_time()' + */ +static void +mclock_deinit_clock_get_time (void) +{ + mhd_assert (mhd_CLOCK_SERV_INVALID != mono_clock_service); + (void) mach_port_deallocate (mach_task_self (), + mono_clock_service); + mono_clock_service = mhd_CLOCK_SERV_INVALID; +} + + +#else /* HAVE_CLOCK_GET_TIME */ +/* No-op implementation */ +# define mclock_init_clock_get_time(ptr) ((void) ptr, false) +# define mclock_deinit_clock_get_time() ((void) 0) +#endif /* HAVE_CLOCK_GET_TIME */ + +#if defined(mhd_USE_MACH_TIME) + +/** + * Calculate greatest common divisor. + * Based on Euclidean algorithm as it is fast enough and more compact then + * binary GCD algorithm. + * @param a the first value + * @param b the second value + * @return the greatest common divisor, + * if either of the input values ​​is zero, the other input value returned + */ +MHD_static_inline_ uint_fast32_t +mclock_gcd (uint_fast32_t a, uint_fast32_t b) +{ + if (0 == b) + return a; + + while (1) + { + a %= b; + if (0 == a) + return b; + b %= a; + if (0 == b) + break; + } + return a; +} + + +/** + * Initialise data for mach_time functions + * @return 'true' if succeed, + * 'false' if failed + */ +static bool +mclock_init_mach_time (void) +{ + struct mach_timebase_info mach_tb_info; + uint_fast32_t comm_div; + static const uint_fast32_t nanosec_in_milisec = 1000u * 1000u; + + mhd_assert ((0 != mach_time_mls_denom) || (0 == mach_time_mls_numer)); + + if (KERN_SUCCESS != mach_timebase_info (&mach_tb_info)) + return false; + + mhd_assert (0 != mach_tb_info.numer); /* Help code analysers */ + mhd_assert (0 != mach_tb_info.denom); /* Help code analysers */ + + comm_div = mclock_gcd (mach_tb_info.numer, nanosec_in_milisec); + mach_time_mls_numer = mach_tb_info.numer / comm_div; + mach_time_mls_denom = + ((uint_fast64_t) mach_tb_info.denom) * (nanosec_in_milisec / comm_div); + + return true; +} + + +#else /* ! mhd_USE_MACH_TIME */ +# define mclock_init_mach_time() (true) +#endif /* ! mhd_USE_MACH_TIME */ /** * Type of monotonic clock source */ -enum _MHD_mono_clock_source +enum mhd_mono_clock_source { /** - * No monotonic clock + * No monotonic clock source. + */ + mhd_MCLOCK_SOUCE_NO_SOURCE = 0 + +#ifdef HAVE_CLOCK_GETTIME + , + /** + * clock_gettime() with specific clock. + * Generic standard source. */ - _MHD_CLOCK_NO_SOURCE = 0, + mhd_MCLOCK_SOUCE_GETTIME +#endif /* HAVE_CLOCK_GETTIME */ +#if defined(mhd_USE_MACH_TIME) + , /** - * clock_gettime() with specific clock + * mach_continuous_approximate_time() or mach_approximate_time() + * with coefficient. + * Darwin-specific clock source. */ - _MHD_CLOCK_GETTIME, + mhd_MCLOCK_SOUCE_MACH_TIME +#endif /* mhd_USE_MACH_TIME */ +#ifdef HAVE_CLOCK_GET_TIME + , /** - * clock_get_time() with specific clock service + * clock_get_time() with specific clock service. + * Darwin-specific clock source. */ - _MHD_CLOCK_GET_TIME, + mhd_MCLOCK_SOUCE_GET_TIME +#endif /* HAVE_CLOCK_GET_TIME */ +#ifdef HAVE_GETHRTIME + , /** - * gethrtime() / 1000000000 + * gethrtime() / 1000000 + * HP-UX and Solaris monotonic clock source. */ - _MHD_CLOCK_GETHRTIME, + mhd_MCLOCK_SOUCE_GETHRTIME +#endif /* HAVE_GETHRTIME */ +#ifdef _WIN32 +#if _WIN32_WINNT >= 0x0600 + , /** - * GetTickCount64() / 1000 + * GetTickCount64() + * W32 tick counter source. */ - _MHD_CLOCK_GETTICKCOUNT64, + mhd_MCLOCK_SOUCE_GETTICKCOUNT64 +#else /* _WIN32_WINNT < 0x0600 */ + , /** * QueryPerformanceCounter() / QueryPerformanceFrequency() + * Older W32 monotonic time source. */ - _MHD_CLOCK_PERFCOUNTER + mhd_MCLOCK_SOUCE_PERFCOUNTER +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* _WIN32 */ }; +/** + * The active source of the monotonic time + */ +static enum mhd_mono_clock_source mono_clock_source = + mhd_MCLOCK_SOUCE_NO_SOURCE; /** * Initialise milliseconds counters. */ -void -MHD_monotonic_msec_counter_init (void) +MHD_INTERNAL void +mhd_mclock_init_once (void) { #ifdef HAVE_CLOCK_GET_TIME mach_timespec_t cur_time; #endif /* HAVE_CLOCK_GET_TIME */ - enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; #ifdef HAVE_CLOCK_GETTIME struct timespec ts; - mono_clock_id = _MHD_UNWANTED_CLOCK; + mono_clock_id = mhd_CLOCK_ID_UNWANTED; #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME - mono_clock_service = _MHD_INVALID_CLOCK_SERV; + mono_clock_service = mhd_CLOCK_SERV_INVALID; #endif /* HAVE_CLOCK_GET_TIME */ - /* just a little syntactic trick to get the - various following ifdef's to work out nicely */ - if (0) + mono_clock_source = mhd_MCLOCK_SOUCE_NO_SOURCE; + + /* Try specialised fast sources */ +#ifdef _WIN32 +#if _WIN32_WINNT >= 0x0600 + /* W32 Vista or later specific monotonic clock */ + /* Available since Vista, ~15ms accuracy */ + if (1) + { + tick_start = GetTickCount64 (); + mono_clock_source = mhd_MCLOCK_SOUCE_GETTICKCOUNT64; + } + else +#else /* _WIN32_WINNT < 0x0600 */ + /* W32 specific monotonic clock */ + /* Available on Windows 2000 and later */ + if (1) + { + LARGE_INTEGER freq; + LARGE_INTEGER perf_counter; + + (void) QueryPerformanceFrequency (&freq); /* never fail on XP and later */ + (void) QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ + perf_freq = (uint64_t) freq.QuadPart; + perf_start = (uint64_t) perf_counter.QuadPart; + mono_clock_source = mhd_MCLOCK_SOUCE_PERFCOUNTER; + } + else +#endif /* _WIN32_WINNT < 0x0600 */ +#endif /* _WIN32 */ +#if defined(mhd_USE_MACH_TIME) + /* Mach (Darwin) specific monotonic clock */ + /* mach_continuous_approximate_time() counts time in suspend, + mach_approximate_time() does not count time in suspend. + Both function are fast and used as a basis for universal portable functions + on Darwin. */ + if (mclock_init_mach_time ()) { - (void) 0; /* Mute possible compiler warning */ +# ifdef HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME + mach_time_start = mach_continuous_approximate_time (); +# else /* HAVE_MACH_APPROXIMATE_TIME */ + mach_time_start = mach_approximate_time (); +# endif + mono_clock_source = mhd_MCLOCK_SOUCE_MACH_TIME; } else +#endif /* mhd_USE_MACH_TIME */ + + /* Try universally available sources */ #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_MONOTONIC_COARSE /* Linux-specific fast value-getting clock */ - /* Can be affected by frequency adjustment and don't count time in suspend, */ - /* but preferred since it's fast */ + /* Can be affected by frequency adjustment and doesn't count time + * in suspend, but preferred since it's fast */ if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, &ts)) { mono_clock_id = CLOCK_MONOTONIC_COARSE; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_MONOTONIC_COARSE */ @@ -180,10 +409,23 @@ MHD_monotonic_msec_counter_init (void) { mono_clock_id = CLOCK_MONOTONIC_FAST; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_MONOTONIC_COARSE */ +#ifdef CLOCK_UPTIME_FAST + /* FreeBSD/DragonFly fast value-getting clock */ + /* Can be affected by frequency adjustment and doesn't count time + * in suspend, but preferred since it's fast */ + if (0 == clock_gettime (CLOCK_UPTIME_FAST, + &ts)) + { + mono_clock_id = CLOCK_MONOTONIC_FAST; + mono_clock_start = ts.tv_sec; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; + } + else +#endif /* CLOCK_UPTIME_FAST */ #ifdef CLOCK_MONOTONIC_RAW_APPROX /* Darwin-specific clock */ /* Not affected by frequency adjustment, returns clock value cached at @@ -193,32 +435,46 @@ MHD_monotonic_msec_counter_init (void) { mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_MONOTONIC_RAW */ +#ifdef CLOCK_UPTIME_RAW_APPROX + /* Darwin-specific clock */ + /* Not affected by frequency adjustment, but doesn't count time in suspend. + * Returns clock value cached at context switch. + * Can be "milliseconds old", but it's fast. */ + if (0 == clock_gettime (CLOCK_UPTIME_RAW_APPROX, + &ts)) + { + mono_clock_id = CLOCK_UPTIME_RAW_APPROX; + mono_clock_start = ts.tv_sec; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; + } + else +#endif /* CLOCK_UPTIME_RAW_APPROX */ #ifdef CLOCK_MONOTONIC_RAW /* Linux and Darwin clock */ /* Not affected by frequency adjustment, - * on Linux don't count time in suspend */ + * on Linux doesn't count time in suspend */ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, &ts)) { mono_clock_id = CLOCK_MONOTONIC_RAW; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_MONOTONIC_RAW */ #ifdef CLOCK_BOOTTIME - /* Count time in suspend on Linux so it's real monotonic, */ + /* Counts time in suspend on Linux so it's real monotonic, */ /* but can be slower value-getting than other clocks */ if (0 == clock_gettime (CLOCK_BOOTTIME, &ts)) { mono_clock_id = CLOCK_BOOTTIME; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_BOOTTIME */ @@ -231,7 +487,7 @@ MHD_monotonic_msec_counter_init (void) { mono_clock_id = CLOCK_MONOTONIC; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_MONOTONIC */ @@ -243,53 +499,22 @@ MHD_monotonic_msec_counter_init (void) { mono_clock_id = CLOCK_UPTIME; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_BOOTTIME */ #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME - /* Darwin-specific monotonic clock */ + /* Darwin-specific monotonic clock source */ /* Should be monotonic as clock_set_time function always unconditionally */ - /* failed on latest kernels */ - if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self (), - SYSTEM_CLOCK, - &mono_clock_service)) && - (KERN_SUCCESS == clock_get_time (mono_clock_service, - &cur_time)) ) + /* failed on modern kernels */ + if (mclock_init_clock_get_time (&cur_time)) { mono_clock_start = cur_time.tv_sec; - mono_clock_source = _MHD_CLOCK_GET_TIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GET_TIME; } else #endif /* HAVE_CLOCK_GET_TIME */ -#ifdef _WIN32 -#if _WIN32_WINNT >= 0x0600 - /* W32 Vista or later specific monotonic clock */ - /* Available since Vista, ~15ms accuracy */ - if (1) - { - tick_start = GetTickCount64 (); - mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; - } - else -#else /* _WIN32_WINNT < 0x0600 */ - /* W32 specific monotonic clock */ - /* Available on Windows 2000 and later */ - if (1) - { - LARGE_INTEGER freq; - LARGE_INTEGER perf_counter; - - QueryPerformanceFrequency (&freq); /* never fail on XP and later */ - QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - perf_freq = (uint64_t) freq.QuadPart; - perf_start = (uint64_t) perf_counter.QuadPart; - mono_clock_source = _MHD_CLOCK_PERFCOUNTER; - } - else -#endif /* _WIN32_WINNT < 0x0600 */ -#endif /* _WIN32 */ #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_HIGHRES /* Solaris-specific monotonic high-resolution clock */ @@ -299,7 +524,7 @@ MHD_monotonic_msec_counter_init (void) { mono_clock_id = CLOCK_HIGHRES; mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; } else #endif /* CLOCK_HIGHRES */ @@ -310,28 +535,13 @@ MHD_monotonic_msec_counter_init (void) if (1) { hrtime_start = gethrtime (); - mono_clock_source = _MHD_CLOCK_GETHRTIME; + mono_clock_source = mhd_MCLOCK_SOUCE_GETHRTIME; } else #endif /* HAVE_GETHRTIME */ - { - /* no suitable clock source was found */ - mono_clock_source = _MHD_CLOCK_NO_SOURCE; - } - -#ifdef HAVE_CLOCK_GET_TIME - if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && - (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) - { - /* clock service was initialised but clock_get_time failed */ - mach_port_deallocate (mach_task_self (), - mono_clock_service); - mono_clock_service = _MHD_INVALID_CLOCK_SERV; - } -#else - (void) mono_clock_source; /* avoid compiler warning */ -#endif /* HAVE_CLOCK_GET_TIME */ + (void) 0; /* The end of if-else chain */ + /* Initialise start values for fallbacks */ #ifdef HAVE_TIMESPEC_GET if (1) { @@ -351,27 +561,40 @@ MHD_monotonic_msec_counter_init (void) gettime_start = 0; } #endif /* HAVE_GETTIMEOFDAY */ + sys_clock_start = time (NULL); +#ifdef mhd_HAVE_GETTIME_START_VAR + if (((time_t) -1) == sys_clock_start) + sys_clock_start = gettime_start; +#endif /* mhd_HAVE_GETTIME_START_VAR */ } -/** - * Deinitialise milliseconds counters by freeing any allocated resources - */ -void -MHD_monotonic_msec_counter_finish (void) -{ #ifdef HAVE_CLOCK_GET_TIME - if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) +/* Resources may be allocated only for Darwin clock_get_time() */ + +MHD_INTERNAL void +mhd_mclock_deinit (void) +{ + if (mhd_MCLOCK_SOUCE_GET_TIME == mono_clock_source) + mclock_deinit_clock_get_time (); +} + + +MHD_INTERNAL void +mhd_mclock_re_init (void) +{ + if (mhd_MCLOCK_SOUCE_GET_TIME == mono_clock_source) { - mach_port_deallocate (mach_task_self (), - mono_clock_service); - mono_clock_service = _MHD_INVALID_CLOCK_SERV; + if (! mclock_init_clock_get_time ()) + /* Fallback to full initialisation */ + mhd_mclock_init_once (); } -#endif /* HAVE_CLOCK_GET_TIME */ } +#endif /* HAVE_CLOCK_GET_TIME */ + /** * Monotonic milliseconds counter, useful for timeout calculation. * Tries to be not affected by manually setting the system real time @@ -379,72 +602,146 @@ MHD_monotonic_msec_counter_finish (void) * * @return number of microseconds from some fixed moment */ -uint_fast64_t -MHD_monotonic_msec_counter (void) +MHD_INTERNAL uint_fast64_t +mhd_monotonic_msec_counter (void) { -#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET) - struct timespec ts; -#endif /* HAVE_CLOCK_GETTIME || HAVE_TIMESPEC_GET */ + enum mhd_mono_clock_source source_to_use; + /* Optimise binary if the source is fixed */ +#if defined(_WIN32) && _WIN32_WINNT >= 0x0600 + if (1) + source_to_use = mhd_MCLOCK_SOUCE_GETTICKCOUNT64; + else +#endif /* _WIN32 && _WIN32_WINNT >= 0x0600 */ + source_to_use = mono_clock_source; + + mhd_assert (mono_clock_source == source_to_use); + + switch (source_to_use) + { + case mhd_MCLOCK_SOUCE_NO_SOURCE: + break; /* Use fallbacks */ #ifdef HAVE_CLOCK_GETTIME - if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && - (0 == clock_gettime (mono_clock_id, - &ts)) ) - return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - mono_clock_start)) - * 1000 - + (uint_fast64_t) (ts.tv_nsec / 1000000)); + case mhd_MCLOCK_SOUCE_GETTIME: + mhd_assert (mhd_CLOCK_ID_UNWANTED != mono_clock_id); + if (1) + { + struct timespec ts; + if (0 == clock_gettime (mono_clock_id, + &ts)) + return (uint_fast64_t) + (((uint_fast64_t) (ts.tv_sec - mono_clock_start)) * 1000 + + (uint_fast64_t) (ts.tv_nsec / 1000000)); + } + break; #endif /* HAVE_CLOCK_GETTIME */ + +#if defined(mhd_USE_MACH_TIME) + case mhd_MCLOCK_SOUCE_MACH_TIME: + mhd_assert (0 != mach_time_mls_numer); + mhd_assert (0 != mach_time_mls_denom); + if (1) + { + uint64_t t; +# ifdef HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME + t = mach_continuous_approximate_time () - mach_time_start; +# else /* HAVE_MACH_APPROXIMATE_TIME */ + t = mach_approximate_time () - mach_time_start; +# endif +# ifndef MHD_FAVOR_SMALL_CODE + if (1 == mach_time_mls_numer) /* Shortcut for the most common situation */ + return (uint_fast64_t) t / mach_time_mls_denom; +# endif /* MHD_FAVOR_SMALL_CODE */ + + /* Avoid float point arithmetic as it lower precision on higher values. + Two stages calculations to avoid overflow of integer values and keep + precision high enough. */ + return (((uint_fast64_t) t) / mach_time_mls_denom) * mach_time_mls_numer + + ((((uint_fast64_t) t) % mach_time_mls_denom) + * mach_time_mls_numer) / mach_time_mls_denom; + } + break; +#endif /* mhd_USE_MACH_TIME */ + #ifdef HAVE_CLOCK_GET_TIME - if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) - { - mach_timespec_t cur_time; - - if (KERN_SUCCESS == clock_get_time (mono_clock_service, - &cur_time)) - return (uint_fast64_t) (((uint_fast64_t) (cur_time.tv_sec - - mono_clock_start)) - * 1000 + (uint_fast64_t) (cur_time.tv_nsec - / 1000000)); - } + case mhd_MCLOCK_SOUCE_GET_TIME: + mhd_assert (mhd_CLOCK_SERV_INVALID != mono_clock_service); + if (1) + { + mach_timespec_t cur_time; + + if (KERN_SUCCESS == clock_get_time (mono_clock_service, + &cur_time)) + return (uint_fast64_t) + (((uint_fast64_t) (cur_time.tv_sec - mono_clock_start)) * 1000 + + (uint_fast64_t) (cur_time.tv_nsec / 1000000)); + } + break; #endif /* HAVE_CLOCK_GET_TIME */ -#if defined(_WIN32) + +#ifdef HAVE_GETHRTIME + case mhd_MCLOCK_SOUCE_GETHRTIME: + return ((uint_fast64_t) (gethrtime () - hrtime_start)) / 1000000; +#endif /* HAVE_GETHRTIME */ + +#ifdef _WIN32 #if _WIN32_WINNT >= 0x0600 - if (1) + case mhd_MCLOCK_SOUCE_GETTICKCOUNT64: return (uint_fast64_t) (GetTickCount64 () - tick_start); #else /* _WIN32_WINNT < 0x0600 */ - if (0 != perf_freq) - { - LARGE_INTEGER perf_counter; - uint_fast64_t num_ticks; - - QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start); - return ((num_ticks / perf_freq) * 1000) - + ((num_ticks % perf_freq) / (perf_freq / 1000)); - } + case mhd_MCLOCK_SOUCE_PERFCOUNTER: + mhd_assert (0 != perf_freq); + if (1) + { + LARGE_INTEGER perf_counter; + uint_fast64_t num_ticks; + + (void) QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ + num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start); + return ((num_ticks / perf_freq) * 1000) + + (((num_ticks % perf_freq) * 1000) / perf_freq); + } + break; #endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ -#ifdef HAVE_GETHRTIME - if (1) - return ((uint_fast64_t) (gethrtime () - hrtime_start)) / 1000000; -#endif /* HAVE_GETHRTIME */ + default: + mhd_assert (0 && "Impossible value"); + mhd_UNREACHABLE (); + break; + } /* Fallbacks, affected by system time change */ #ifdef HAVE_TIMESPEC_GET - if (TIME_UTC == timespec_get (&ts, TIME_UTC)) - return (uint_fast64_t) (((uint_fast64_t) (ts.tv_sec - gettime_start)) * 1000 - + (uint_fast64_t) (ts.tv_nsec / 1000000)); + if (1) + { + struct timespec ts; + if (TIME_UTC == timespec_get (&ts, TIME_UTC)) + return (uint_fast64_t) + (((uint_fast64_t) (ts.tv_sec - gettime_start)) * 1000 + + (uint_fast64_t) (ts.tv_nsec / 1000000)); + } #elif defined(HAVE_GETTIMEOFDAY) if (1) { struct timeval tv; if (0 == gettimeofday (&tv, NULL)) - return (uint_fast64_t) (((uint_fast64_t) (tv.tv_sec - gettime_start)) - * 1000 - + (uint_fast64_t) (tv.tv_usec / 1000)); + return (uint_fast64_t) + (((uint_fast64_t) (tv.tv_sec - gettime_start)) * 1000 + + (uint_fast64_t) (tv.tv_usec / 1000)); } #endif /* HAVE_GETTIMEOFDAY */ /* The last resort fallback with very low resolution */ - return (uint_fast64_t) (time (NULL) - sys_clock_start) * 1000; +#ifdef mhd_HAVE_GETTIME_START_VAR + if (1) + { + time_t time_now; + time_now = time (NULL); + if (((time_t) -1) != time_now) + return ((uint_fast64_t) (time_now - sys_clock_start)) * 1000; + } + return 0; /* No time source, should not really happen */ +#else /* ! mhd_HAVE_GETTIME_START_VAR */ + return ((uint_fast64_t) (time (NULL) - sys_clock_start)) * 1000; +#endif } diff --git a/src/mhd2/mhd_mono_clock.h b/src/mhd2/mhd_mono_clock.h @@ -28,19 +28,36 @@ #include "mhd_sys_options.h" #include "sys_base_types.h" +#include "sys_bool_type.h" /** - * Initialise milliseconds counters. + * Initialise milliseconds counters completely. + * Must be called only one time per application run. */ -void -MHD_monotonic_msec_counter_init (void); +MHD_INTERNAL void +mhd_mclock_init_once (void); +#ifdef HAVE_CLOCK_GET_TIME +/* Resources may be allocated only for Darwin clock_get_time() */ + /** * Deinitialise milliseconds counters by freeing any allocated resources */ -void -MHD_monotonic_msec_counter_finish (void); +MHD_INTERNAL void +mhd_mclock_deinit (void); + +/** + * Re-initialise monotonic clocks are de-initialisaion has been performed + */ +MHD_INTERNAL void +mhd_mclock_re_init (void); + +#else /* ! HAVE_CLOCK_GET_TIME */ +/* No-op implementation */ +# define mhd_mclock_deinit() ((void) 0) +# define mhd_mclock_re_init() ((void) 0) +#endif /* ! HAVE_CLOCK_GET_TIME */ /** @@ -50,7 +67,7 @@ MHD_monotonic_msec_counter_finish (void); * * @return number of microseconds from some fixed moment */ -uint_fast64_t -MHD_monotonic_msec_counter (void); +MHD_INTERNAL uint_fast64_t +mhd_monotonic_msec_counter (void); #endif /* MHD_MONO_CLOCK_H */ diff --git a/src/mhd2/mhd_read_file.c b/src/mhd2/mhd_read_file.c @@ -0,0 +1,177 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_read_file.c + * @brief The implementation of mhd_read_file() function + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_read_file.h" + +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if ! defined(mhd_W32_NATIVE) +# include "mhd_limits.h" +#else +/* Native W32 */ +# include <windows.h> +# include <string.h> /* for memset() */ +#endif +/** + * Read data from the file to the provided buffer + * + * @param file_fd the FD of file to read + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the read data + * @param[out] size_filled the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return #mhd_FILE_READ_OK if succeed (the @a size_filled gets the actual + * read size), + * error otherwise + */ +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) enum mhd_FileReadResult +mhd_read_file (int file_fd, + uint_fast64_t offset, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict size_filled) +{ +#if (defined(HAVE_PREAD64) || defined(HAVE_PREAD)) && ! defined(mhd_W32_NATIVE) +# ifdef HAVE_PREAD64 + const off64_t pos_off = (off64_t) offset; +# else /* HAVE_PREAD */ + const off_t pos_off = (off_t) offset; +# endif /* HAVE_PREAD */ + ssize_t res; + + *size_filled = 0; + + if ((0 > pos_off) || + (offset != (uint_fast64_t) pos_off)) + return mhd_FILE_READ_OFFSET_TOO_LARGE; + + if (0 > (ssize_t) buf_size) + buf_size = SSIZE_MAX; /* Larger sizes may result in undefined behaviour */ + +# ifdef HAVE_PREAD64 + res = pread64 (file_fd, + buf, + buf_size, + pos_off); +# else /* HAVE_PREAD */ + res = pread (file_fd, + buf, + buf_size, + pos_off); +# endif /* HAVE_PREAD */ + + if (0 > res) + return mhd_FILE_READ_ERROR; + + if (0 == res) + return mhd_FILE_READ_EOF; + + *size_filled = (size_t) res; + return mhd_FILE_READ_OK; +#elif ! defined(mhd_W32_NATIVE) + /* Multithread-unsafe emulation */ +# ifdef HAVE_LSEEK64 + const off64_t pos_off = (off64_t) offset; +# else + const off_t pos_off = (off_t) offset; +# endif + ssize_t res; + + *size_filled = 0; + + if ((0 > pos_off) || + (offset != (uint_fast64_t) pos_off)) + return mhd_FILE_READ_OFFSET_TOO_LARGE; + + if (0 > (ssize_t) buf_size) + buf_size = SSIZE_MAX; /* Larger sizes may result in undefined behaviour */ + +# ifdef HAVE_LSEEK64 + if (pos_off != lseek64 (file_fd, + pos_off, + SEEK_SET)) + return mhd_FILE_READ_ERROR; +# else + if (pos_off != lseek (file_fd, + pos_off, + SEEK_SET)) + return mhd_FILE_READ_ERROR; +# endif + + res = read (file_fd, + buf, + buf_size); + + if (0 > res) + return mhd_FILE_READ_ERROR; + + if (0 == res) + return mhd_FILE_READ_EOF; + + *size_filled = (size_t) res; + return mhd_FILE_READ_OK; + +#else /* Native W32 */ + const intptr_t sys_fd = _get_osfhandle (file_fd); + const HANDLE w_hndl = (HANDLE) sys_fd; + OVERLAPPED ovrlp; + DWORD reqReadSize; + DWORD resReadSize; + + *size_filled = 0; + if (INVALID_HANDLE_VALUE == w_hndl) + return mhd_FILE_READ_ERROR; + + memset (&ovrlp, 0, sizeof(ovrlp)); + reqReadSize = (DWORD) buf_size; + if (reqReadSize != buf_size) + reqReadSize = (DWORD) (~((DWORD) 0)); + ovrlp.Offset = (DWORD) offset; + offset >>= 32; + ovrlp.OffsetHigh = (DWORD) offset; + if (0 != (offset >> 32)) + return mhd_FILE_READ_OFFSET_TOO_LARGE; + + if (! ReadFile (w_hndl, + buf, + reqReadSize, + &resReadSize, + &ovrlp)) + return mhd_FILE_READ_ERROR; + + if (0 == resReadSize) + return mhd_FILE_READ_EOF; + + *size_filled = resReadSize; + return mhd_FILE_READ_OK; +#endif /* Native W32 */ +} diff --git a/src/mhd2/mhd_read_file.h b/src/mhd2/mhd_read_file.h @@ -0,0 +1,83 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_read_file.h + * @brief The declaration of mhd_read_file() function + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_READ_FILE_H +#define MHD_READ_FILE_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" + +/** + * Results of file reading + */ +enum mhd_FileReadResult +{ + /** + * File read succeed + */ + mhd_FILE_READ_OK = 0 + , + /** + * File read failed + */ + mhd_FILE_READ_ERROR + , + /** + * The requested offset is too large + */ + mhd_FILE_READ_OFFSET_TOO_LARGE + , + /** + * Got "end of file" + */ + mhd_FILE_READ_EOF +}; + + +/** + * Read data from the file to the provided buffer + * + * @param file_fd the FD of file to read + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the read data + * @param[out] size_filled the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return #mhd_FILE_READ_OK if succeed (the @a size_filled gets the actual + * read size), + * error otherwise + */ +MHD_INTERNAL enum mhd_FileReadResult +mhd_read_file (int file_fd, + uint_fast64_t offset, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict size_filled) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_(4, 3) MHD_FN_PAR_OUT_ (5); + + +#endif /* ! MHD_READ_FILE_H */ diff --git a/src/mhd2/mhd_recv.c b/src/mhd2/mhd_recv.c @@ -35,12 +35,17 @@ #include "mhd_sockets_macros.h" #include "mhd_limits.h" -#include "mhd_socket_error.h" +#include "mhd_assert.h" +#include "mhd_socket_error_funcs.h" + +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError -mhd_plain_recv (struct MHD_Connection *restrict c, +mhd_recv_plain (struct MHD_Connection *restrict c, size_t buf_size, char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], size_t *restrict received) @@ -52,13 +57,13 @@ mhd_plain_recv (struct MHD_Connection *restrict c, if (MHD_SCKT_SEND_MAX_SIZE_ < buf_size) buf_size = MHD_SCKT_SEND_MAX_SIZE_; - res = mhd_sys_recv (c->socket_fd, buf, buf_size); + res = mhd_sys_recv (c->sk.fd, buf, buf_size); if (0 <= res) { *received = (size_t) res; if (buf_size > (size_t) res) - c->sk_ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ - (((unsigned int) c->sk_ready) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ + (((unsigned int) c->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_RECV_READY)); return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ @@ -67,8 +72,8 @@ mhd_plain_recv (struct MHD_Connection *restrict c, err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); if (mhd_SOCKET_ERR_AGAIN == err) - c->sk_ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ - (((unsigned int) c->sk_ready) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ + (((unsigned int) c->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_RECV_READY)); @@ -76,6 +81,42 @@ mhd_plain_recv (struct MHD_Connection *restrict c, } +#ifdef MHD_ENABLE_HTTPS + +static MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_recv_tls (struct MHD_Connection *restrict c, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + /* TLS connection */ + enum mhd_SocketError res; + + mhd_assert (mhd_C_HAS_TLS (c)); + mhd_assert (0 != buf_size); + + res = mhd_tls_conn_recv (c->tls, + buf_size, + buf, + received); + c->tls_has_data_in = mhd_TLS_BUF_NO_DATA; /* Updated with the actual value below */ + + if (mhd_SOCKET_ERR_AGAIN == res) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */ + (((unsigned int) c->sk.ready) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_RECV_READY)); + else if ((mhd_SOCKET_ERR_NO_ERROR == res) && + mhd_tls_conn_has_data_in (c->tls)) + c->tls_has_data_in = mhd_TLS_BUF_HAS_DATA_IN; + + return res; +} + + +#endif /* MHD_ENABLE_HTTPS */ + MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError mhd_recv (struct MHD_Connection *restrict c, @@ -83,10 +124,16 @@ mhd_recv (struct MHD_Connection *restrict c, char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], size_t *restrict received) { - mhd_assert (MHD_INVALID_SOCKET != c->socket_fd); - mhd_assert (MHD_CONNECTION_CLOSED != c->state); - - // TODO: implement TLS support - - return mhd_plain_recv (c, buf_size, buf, received); + mhd_assert (MHD_INVALID_SOCKET != c->sk.fd); + mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage); + +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (c)) + return mhd_recv_tls (c, + buf_size, + buf, + received); +#endif /* MHD_ENABLE_HTTPS */ + + return mhd_recv_plain (c, buf_size, buf, received); } diff --git a/src/mhd2/mhd_recv.h b/src/mhd2/mhd_recv.h @@ -36,7 +36,7 @@ struct MHD_Connection; /* forward declaration */ /** * Receive the data from the network socket. - * Clear #mhd_SOCKET_NET_STATE_RECV_READY in sk_ready if necessary. + * Clear #mhd_SOCKET_NET_STATE_RECV_READY in sk.ready if necessary. * * @param c the connection to use * @param buf_size the size of the @a buf buffer @@ -49,7 +49,7 @@ struct MHD_Connection; /* forward declaration */ MHD_INTERNAL enum mhd_SocketError mhd_recv (struct MHD_Connection *restrict c, size_t buf_size, - char buffer[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)], + char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], size_t *restrict received) MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4); diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h @@ -366,7 +366,7 @@ struct MHD_Request /** * Number of bytes we had in the HTTP header, set once we - * pass #MHD_CONNECTION_HEADERS_RECEIVED. + * pass #mhd_HTTP_STAGE_HEADERS_RECEIVED. * This includes the request line, all request headers, the header section * terminating empty line, with all CRLF (or LF) characters. */ @@ -375,8 +375,8 @@ struct MHD_Request /** * The union of the size of all request field lines (headers) and * the starting point of the first request field line (the first header). - * Until #MHD_CONNECTION_HEADERS_RECEIVED the @a start member is valid, - * staring with #MHD_CONNECTION_HEADERS_RECEIVED the @a size member is valid. + * Until #mhd_HTTP_STAGE_HEADERS_RECEIVED the @a start member is valid, + * staring with #mhd_HTTP_STAGE_HEADERS_RECEIVED the @a size member is valid. * The size includes CRLF (or LR) characters, but does not include * the terminating empty line. */ diff --git a/src/mhd2/mhd_send.c b/src/mhd2/mhd_send.c @@ -44,6 +44,7 @@ #include "sys_ip_headers.h" #include "mhd_sockets_macros.h" #include "daemon_logger.h" +#include "mhd_socket_error_funcs.h" #include "mhd_daemon.h" #include "mhd_connection.h" @@ -70,10 +71,14 @@ #include "mhd_limits.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif + #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \ - defined(MHD_WINSOCK_SOCKETS) + defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_USE_VECT_SEND 1 -#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */ +#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_SOCKETS_KIND_WINSOCK */ #ifdef mhd_USE_VECT_SEND @@ -166,7 +171,7 @@ iov_max_init_ (void) */ # define mhd_IOV_MAX mhd_iov_max_ #else /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */ -# define iov_max_init_() (void) 0 +# define iov_max_init_() ((void) 0) # if defined(IOV_MAX) /** @@ -177,11 +182,8 @@ iov_max_init_ (void) #endif /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */ -/** - * Initialises static variables - */ void -mhd_send_init_static_vars (void) +mhd_send_init_once (void) { /* FreeBSD 11 and later allow to specify read-ahead size * and handles SF_NODISKIO differently. @@ -201,26 +203,26 @@ mhd_connection_set_nodelay_state (struct MHD_Connection *connection, static const mhd_SCKT_OPT_BOOL on_val = 1; int err_code; - if (mhd_T_IS_YES (connection->is_nonip)) + if (mhd_T_IS_YES (connection->sk.props.is_nonip)) return false; - if (0 == setsockopt (connection->socket_fd, + if (0 == setsockopt (connection->sk.fd, IPPROTO_TCP, TCP_NODELAY, (const void *) (nodelay_state ? &on_val : &off_val), sizeof (off_val))) { - connection->sk_nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO; + connection->sk.state.nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO; return true; } err_code = mhd_SCKT_GET_LERR (); - if ((mhd_T_IS_NOT_YES (connection->is_nonip)) && + if ((mhd_T_IS_NOT_YES (connection->sk.props.is_nonip)) && (mhd_SCKT_ERR_IS_EINVAL (err_code) || mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) || mhd_SCKT_ERR_IS_NOTSOCK (err_code))) { - connection->is_nonip = mhd_T_YES; + connection->sk.props.is_nonip = mhd_T_YES; } else { @@ -229,7 +231,7 @@ mhd_connection_set_nodelay_state (struct MHD_Connection *connection, } #else /* ! TCP_NODELAY */ (void) nodelay_state; /* Mute compiler warnings */ - connection->sk_nodelay = mhd_T_NO; + connection->sk.state.nodelay = mhd_T_NO; #endif /* ! TCP_NODELAY */ return false; } @@ -244,26 +246,26 @@ mhd_connection_set_cork_state (struct MHD_Connection *connection, static const mhd_SCKT_OPT_BOOL on_val = 1; int err_code; - if (mhd_T_IS_YES (connection->is_nonip)) + if (mhd_T_IS_YES (connection->sk.props.is_nonip)) return false; - if (0 == setsockopt (connection->socket_fd, + if (0 == setsockopt (connection->sk.fd, IPPROTO_TCP, mhd_TCP_CORK_NOPUSH, (const void *) (cork_state ? &on_val : &off_val), sizeof (off_val))) { - connection->sk_corked = cork_state ? mhd_T_YES : mhd_T_NO; + connection->sk.state.corked = cork_state ? mhd_T_YES : mhd_T_NO; return true; } err_code = mhd_SCKT_GET_LERR (); - if ((mhd_T_IS_NOT_YES (connection->is_nonip)) && + if ((mhd_T_IS_NOT_YES (connection->sk.props.is_nonip)) && (mhd_SCKT_ERR_IS_EINVAL (err_code) || mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) || mhd_SCKT_ERR_IS_NOTSOCK (err_code))) { - connection->is_nonip = mhd_T_YES; + connection->sk.props.is_nonip = mhd_T_YES; } else { @@ -278,7 +280,7 @@ mhd_connection_set_cork_state (struct MHD_Connection *connection, #else /* ! mhd_TCP_CORK_NOPUSH */ (void) cork_state; /* Mute compiler warnings. */ - connection->sk_corked = mhd_T_NO; + connection->sk.state.corked = mhd_T_NO; #endif /* ! mhd_TCP_CORK_NOPUSH */ return false; } @@ -303,7 +305,7 @@ pre_send_setopt (struct MHD_Connection *connection, * Final piece is indicated by push_data == true. */ const bool buffer_data = (! push_data); - if (mhd_T_IS_YES (connection->is_nonip)) + if (mhd_T_IS_YES (connection->sk.props.is_nonip)) return; // TODO: support inheriting of TCP_NODELAY and TCP_NOPUSH @@ -325,7 +327,7 @@ pre_send_setopt (struct MHD_Connection *connection, #endif /* ! mhd_USE_MSG_MORE */ #ifdef mhd_TCP_CORK_NOPUSH - if (mhd_T_IS_YES (connection->sk_corked)) + if (mhd_T_IS_YES (connection->sk.state.corked)) return; /* The connection was already corked. */ /* Prefer 'cork' over 'no delay' as the 'cork' buffers better, regardless @@ -336,7 +338,7 @@ pre_send_setopt (struct MHD_Connection *connection, /* Failed to cork the connection. * Really unlikely to happen on TCP connections. */ #endif /* mhd_TCP_CORK_NOPUSH */ - if (mhd_T_IS_NO (connection->sk_nodelay)) + if (mhd_T_IS_NO (connection->sk.state.nodelay)) return; /* TCP_NODELAY was not set for the socket. * Nagle's algorithm will buffer some data. */ @@ -400,17 +402,17 @@ pre_send_setopt (struct MHD_Connection *connection, /* This is typical modern FreeBSD and OpenBSD behaviour. */ # endif /* ! mhd_NODELAY_SET_PUSH_DATA */ - if (mhd_T_IS_YES (connection->sk_corked)) + if (mhd_T_IS_YES (connection->sk.state.corked)) return; /* Socket is corked. Data can be pushed by resetting of * TCP_CORK / TCP_NOPUSH after send() */ - else if (mhd_T_IS_NO (connection->sk_corked)) + else if (mhd_T_IS_NO (connection->sk.state.corked)) { /* The socket is not corked. */ - if (mhd_T_IS_YES (connection->sk_nodelay)) + if (mhd_T_IS_YES (connection->sk.state.nodelay)) return; /* TCP_NODELAY was already set, * data will be pushed automatically by the next send() */ # ifdef mhd_NODELAY_SET_PUSH_DATA - else if (mhd_T_IS_MAYBE (connection->sk_nodelay)) + else if (mhd_T_IS_MAYBE (connection->sk.state.nodelay)) { /* Setting TCP_NODELAY may push data NOW. * Cork socket here and uncork after send(). */ @@ -473,7 +475,7 @@ pre_send_setopt (struct MHD_Connection *connection, * TCP_CORK / TCP_NOPUSH after send() */ /* The socket cannot be corked. * Really unlikely to happen on TCP connections */ - if (mhd_T_IS_YES (connection->sk_nodelay)) + if (mhd_T_IS_YES (connection->sk.state.nodelay)) return; /* TCP_NODELAY was already set, * data will be pushed by the next send() */ @@ -502,11 +504,11 @@ pre_send_setopt (struct MHD_Connection *connection, /* This is old FreeBSD and Darwin behaviour. */ /* Uncork socket if socket wasn't uncorked. */ - if (mhd_T_IS_NOT_NO (connection->sk_corked)) + if (mhd_T_IS_NOT_NO (connection->sk.state.corked)) mhd_connection_set_cork_state (connection, false); /* Set TCP_NODELAY if it wasn't set. */ - if (mhd_T_IS_NOT_YES (connection->sk_nodelay)) + if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)) mhd_connection_set_nodelay_state (connection, true); return; @@ -528,7 +530,7 @@ pre_send_setopt (struct MHD_Connection *connection, /* Buffering of data is controlled only by * Nagel's algorithm. */ /* Set TCP_NODELAY if it wasn't set. */ - if (mhd_T_IS_NOT_YES (connection->sk_nodelay)) + if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)) mhd_connection_set_nodelay_state (connection, true); #endif /* ! mhd_TCP_CORK_NOPUSH */ } @@ -551,11 +553,11 @@ zero_send (struct MHD_Connection *connection) { static const int dummy = 0; - if (mhd_T_IS_YES (connection->is_nonip)) + if (mhd_T_IS_YES (connection->sk.props.is_nonip)) return false; - mhd_assert (mhd_T_IS_NO (connection->sk_corked)); - mhd_assert (mhd_T_IS_YES (connection->sk_nodelay)); - if (0 == mhd_sys_send (connection->socket_fd, &dummy, 0)) + mhd_assert (mhd_T_IS_NO (connection->sk.state.corked)); + mhd_assert (mhd_T_IS_YES (connection->sk.state.nodelay)); + if (0 == mhd_sys_send (connection->sk.fd, &dummy, 0)) return true; mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \ "Failed to push the data by zero-sized send."); @@ -584,7 +586,7 @@ post_send_setopt (struct MHD_Connection *connection, * Final piece is indicated by push_data == true. */ const bool buffer_data = (! push_data); - if (mhd_T_IS_YES (connection->is_nonip)) + if (mhd_T_IS_YES (connection->sk.props.is_nonip)) return; if (buffer_data) return; /* Nothing to do after the send(). */ @@ -595,8 +597,8 @@ post_send_setopt (struct MHD_Connection *connection, /* Need to push data. */ #ifdef mhd_TCP_CORK_NOPUSH - if (mhd_T_IS_YES (connection->sk_nodelay) && \ - mhd_T_IS_NO (connection->sk_corked)) + if (mhd_T_IS_YES (connection->sk.state.nodelay) && \ + mhd_T_IS_NO (connection->sk.state.corked)) return; /* Data has been already pushed by last send(). */ # ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS @@ -623,7 +625,7 @@ post_send_setopt (struct MHD_Connection *connection, * resetting of TCP_CORK so next final send without MSG_MORE will push * data to the network (without additional sys-call to push data). */ - if (mhd_T_IS_NOT_YES (connection->sk_nodelay) || + if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay) || (! plain_send_next)) { if (mhd_connection_set_nodelay_state (connection, true)) @@ -675,9 +677,9 @@ post_send_setopt (struct MHD_Connection *connection, # else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ /* This is old FreeBSD or Darwin kernel. */ - if (mhd_T_IS_NO (connection->sk_corked)) + if (mhd_T_IS_NO (connection->sk.state.corked)) { - mhd_assert (mhd_T_IS_NOT_YES (connection->sk_nodelay)); + mhd_assert (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)); /* Unlikely to reach this code. * TCP_NODELAY should be turned on before send(). */ @@ -695,7 +697,7 @@ post_send_setopt (struct MHD_Connection *connection, else { #ifdef mhd_CORK_RESET_PUSH_DATA - enum mhd_Tristate old_cork_state = connection->sk_corked; + enum mhd_Tristate old_cork_state = connection->sk.state.corked; #endif /* mhd_CORK_RESET_PUSH_DATA */ /* The socket is corked or cork state is unknown. */ @@ -710,7 +712,7 @@ post_send_setopt (struct MHD_Connection *connection, /* Unlikely to reach this code. * The data should be pushed by uncorking (FreeBSD) or * the socket should be uncorked before send(). */ - if (mhd_T_IS_YES (connection->sk_nodelay) || + if (mhd_T_IS_YES (connection->sk.state.nodelay) || (mhd_connection_set_nodelay_state (connection, true))) { /* TCP_NODELAY is turned ON on uncorked socket. @@ -725,8 +727,8 @@ post_send_setopt (struct MHD_Connection *connection, #else /* ! mhd_TCP_CORK_NOPUSH */ /* Corking is not supported. Buffering is controlled * by TCP_NODELAY only. */ - mhd_assert (mhd_T_IS_NOT_YES (connection->sk_corked)); - if (mhd_T_IS_YES (connection->sk_nodelay)) + mhd_assert (mhd_T_IS_NOT_YES (connection->sk.state.corked)); + if (mhd_T_IS_YES (connection->sk.state.nodelay)) return; /* Data was already pushed by send(). */ /* Unlikely to reach this code. @@ -753,7 +755,7 @@ post_send_setopt (struct MHD_Connection *connection, static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (5) enum mhd_SocketError -mhd_plain_send (struct MHD_Connection *restrict c, +mhd_send_plain (struct MHD_Connection *restrict c, size_t buf_size, const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], bool push_data, @@ -763,6 +765,9 @@ mhd_plain_send (struct MHD_Connection *restrict c, ssize_t res; bool full_buf_sent; + mhd_assert (! mhd_C_HAS_TLS (c)); + mhd_assert (! mhd_D_HAS_TLS (c->daemon)); + if (buf_size > MHD_SCKT_SEND_MAX_SIZE_) { buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */ @@ -771,12 +776,12 @@ mhd_plain_send (struct MHD_Connection *restrict c, pre_send_setopt (c, true, push_data); #ifdef mhd_USE_MSG_MORE - res = mhd_sys_send4 (c->socket_fd, + res = mhd_sys_send4 (c->sk.fd, buf, buf_size, push_data ? 0 : MSG_MORE); #else - res = mhd_sys_send4 (c->socket_fd, + res = mhd_sys_send4 (c->sk.fd, buf, buf_size, 0); @@ -789,8 +794,8 @@ mhd_plain_send (struct MHD_Connection *restrict c, err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); if (mhd_SOCKET_ERR_AGAIN == err) - c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) c->sk_ready) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) c->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); @@ -801,8 +806,8 @@ mhd_plain_send (struct MHD_Connection *restrict c, full_buf_sent = (buf_size == (size_t) res); if (! full_buf_sent) - c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) c->sk_ready) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) c->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); @@ -818,6 +823,52 @@ mhd_plain_send (struct MHD_Connection *restrict c, } +#ifdef MHD_ENABLE_HTTPS + +static MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (5) enum mhd_SocketError +mhd_send_tls (struct MHD_Connection *restrict c, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + bool push_data, + size_t *restrict sent) +{ + /* TLS connection */ + enum mhd_SocketError res; + + mhd_assert (mhd_C_HAS_TLS (c)); + mhd_assert (mhd_D_HAS_TLS (c->daemon)); + mhd_assert (0 != buf_size); + + pre_send_setopt (c, false, push_data); + + res = mhd_tls_conn_send (c->tls, + buf_size, + buf, + sent); + + if (mhd_SOCKET_ERR_NO_ERROR != res) + { + if (mhd_SOCKET_ERR_AGAIN == res) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) c->sk.ready) + & (~(enum mhd_SocketNetState) + mhd_SOCKET_NET_STATE_SEND_READY)); + return res; + } + + /* If there is a need to push the data from network buffers + * call post_send_setopt(). */ + if (push_data && (buf_size == *sent)) + post_send_setopt (c, false, true); + + return mhd_SOCKET_ERR_NO_ERROR; +} + + +#endif /* MHD_ENABLE_HTTPS */ + MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (5) enum mhd_SocketError @@ -827,28 +878,19 @@ mhd_send_data (struct MHD_Connection *restrict connection, bool push_data, size_t *restrict sent) { - const bool tls_conn = false; // TODO: TLS support - - mhd_assert (MHD_INVALID_SOCKET != connection->socket_fd); - mhd_assert (MHD_CONNECTION_CLOSED != connection->state); + mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd); + mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); - if (tls_conn) - { - enum mhd_SocketError ret; - -#ifdef HTTPS_SUPPORT - pre_send_setopt (connection, - (! tls_conn), - push_data); - ret = mhd_SOCKET_ERR_OTHER; - mhd_assert (0 && "Not implemented yet"); -#else /* ! HTTPS_SUPPORT */ - ret = mhd_SOCKET_ERR_NOTCONN; -#endif /* ! HTTPS_SUPPORT */ - return ret; - } +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (connection)) + return mhd_send_tls (connection, + buf_size, + buf, + push_data, + sent); +#endif /* MHD_ENABLE_HTTPS */ - return mhd_plain_send (connection, + return mhd_send_plain (connection, buf_size, buf, push_data, @@ -872,7 +914,7 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, bool send_error; bool push_hdr; bool push_body; - MHD_Socket s = connection->socket_fd; + MHD_Socket s = connection->sk.fd; #ifdef mhd_USE_VECT_SEND #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) struct iovec vector[2]; @@ -887,14 +929,12 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, bool no_vec; /* Is vector-send() disallowed? */ no_vec = false; -#ifdef HTTPS_SUPPORT - no_vec = no_vec || (false); // TODO: TLS support -#endif /* HTTPS_SUPPORT */ + no_vec = no_vec || (mhd_C_HAS_TLS (connection)); #if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \ defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \ defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED) no_vec = no_vec || (! connection->daemon->sigpipe_blocked && - ! connection->sk_spipe_suppress); + ! connection->sk.props.has_spipe_supp); #endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) && mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && mhd_SEND_SPIPE_SUPPRESS_NEEDED */ @@ -903,7 +943,7 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, mhd_assert ( (NULL != body) || (0 == body_size) ); mhd_assert (MHD_INVALID_SOCKET != s); - mhd_assert (MHD_CONNECTION_CLOSED != connection->state); + mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); push_body = complete_response; @@ -949,12 +989,11 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, push_hdr, sent); - // TODO: check 'send-ready' if ((mhd_SOCKET_ERR_NO_ERROR == ret) && (header_size == *sent) && (0 != body_size) && (header_size < header_size + body_size) && - (connection->sk_nonblck)) + (connection->sk.props.is_nonblck)) { size_t sent_b; /* The header has been sent completely. @@ -978,6 +1017,8 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, } #ifdef mhd_USE_VECT_SEND + mhd_assert (! mhd_C_HAS_TLS (connection)); + if (header_size > (header_size + body_size)) { /* Return value limit */ @@ -1056,16 +1097,16 @@ mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); if (mhd_SOCKET_ERR_AGAIN == err) - connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) connection->sk_ready) + connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) connection->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); return err; } if ((header_size + body_size) > *sent) - connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) connection->sk_ready) + connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) connection->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); @@ -1135,7 +1176,7 @@ mhd_send_sendfile (struct MHD_Connection *restrict c, mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc); mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size); mhd_assert (chunk_size <= (size_t) SSIZE_MAX); - // mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS)); // TODO: TLS support + mhd_assert (! mhd_C_HAS_TLS (c)); send_size = 0; push_data = true; @@ -1181,12 +1222,12 @@ mhd_send_sendfile (struct MHD_Connection *restrict c, { ssize_t res; #ifndef HAVE_SENDFILE64 - ret = sendfile (c->socket_fd, + ret = sendfile (c->sk.fd, file_fd, &offset, send_size); #else /* HAVE_SENDFILE64 */ - res = sendfile64 (c->socket_fd, + res = sendfile64 (c->sk.fd, file_fd, &offset, send_size); @@ -1222,7 +1263,7 @@ mhd_send_sendfile (struct MHD_Connection *restrict c, freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; #endif /* SF_FLAGS */ if (0 != sendfile (file_fd, - c->socket_fd, + c->sk.fd, offset, send_size, NULL, @@ -1266,7 +1307,7 @@ mhd_send_sendfile (struct MHD_Connection *restrict c, len = (off_t) send_size; /* chunk always fit */ if (0 != sendfile (file_fd, - c->socket_fd, + c->sk.fd, offset, &len, NULL, @@ -1315,8 +1356,8 @@ mhd_send_sendfile (struct MHD_Connection *restrict c, if ((mhd_SOCKET_ERR_AGAIN == ret) || ((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes))) - c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) c->sk_ready) + c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) c->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); @@ -1365,20 +1406,21 @@ send_iov_nontls (struct MHD_Connection *restrict connection, { bool send_error; size_t items_to_send; -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK ssize_t res; #endif #ifdef HAVE_SENDMSG struct msghdr msg; -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) DWORD bytes_sent; DWORD cnt_w; -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ - // TODO: assert for non-TLS + mhd_assert (! mhd_C_HAS_TLS (connection)); + mhd_assert (! mhd_D_HAS_TLS (connection->daemon)); - mhd_assert (MHD_INVALID_SOCKET != connection->socket_fd); - mhd_assert (MHD_CONNECTION_CLOSED != connection->state); + mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd); + mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); send_error = false; items_to_send = r_iov->cnt - r_iov->sent; @@ -1403,7 +1445,7 @@ send_iov_nontls (struct MHD_Connection *restrict connection, msg.msg_flags = 0; pre_send_setopt (connection, true, push_data); - res = sendmsg (connection->socket_fd, &msg, + res = sendmsg (connection->sk.fd, &msg, mhd_MSG_NOSIGNAL | (push_data ? 0 : mhd_MSG_MORE)); if (0 < res) *sent = (size_t) res; @@ -1411,13 +1453,13 @@ send_iov_nontls (struct MHD_Connection *restrict connection, send_error = true; #elif defined(HAVE_WRITEV) pre_send_setopt (connection, false, push_data); - res = writev (connection->socket_fd, r_iov->iov + r_iov->sent, + res = writev (connection->sk.fd, r_iov->iov + r_iov->sent, items_to_send); if (0 < res) *sent = (size_t) res; else send_error = true; -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) #ifdef _WIN64 if (items_to_send > ULONG_MAX) { @@ -1430,14 +1472,14 @@ send_iov_nontls (struct MHD_Connection *restrict connection, cnt_w = (DWORD) items_to_send; #endif /* ! _WIN64 */ pre_send_setopt (connection, true, push_data); - if (0 == WSASend (connection->socket_fd, + if (0 == WSASend (connection->sk.fd, (LPWSABUF) (r_iov->iov + r_iov->sent), cnt_w, &bytes_sent, 0, NULL, NULL)) *sent = (size_t) bytes_sent; else send_error = true; -#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */ +#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_SOCKETS_KIND_WINSOCK */ #error No vector-send function available #endif @@ -1448,8 +1490,8 @@ send_iov_nontls (struct MHD_Connection *restrict connection, err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); if (mhd_SOCKET_ERR_AGAIN == err) - connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) connection->sk_ready) + connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) connection->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); @@ -1473,8 +1515,8 @@ send_iov_nontls (struct MHD_Connection *restrict connection, post_send_setopt (connection, true, push_data); else { - connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ - (((unsigned int) connection->sk_ready) + connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ + (((unsigned int) connection->sk.ready) & (~(enum mhd_SocketNetState) mhd_SOCKET_NET_STATE_SEND_READY)); if (0 != track_sent) @@ -1494,7 +1536,7 @@ send_iov_nontls (struct MHD_Connection *restrict connection, #endif /* mhd_USE_VECT_SEND */ -#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \ +#if ! defined(mhd_USE_VECT_SEND) || defined(MHD_ENABLE_HTTPS) || \ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) @@ -1521,7 +1563,7 @@ send_iov_emu (struct MHD_Connection *restrict connection, bool push_data, size_t *restrict sent) { - const bool non_blk = connection->sk_nonblck; + const bool non_blk = connection->sk.props.is_nonblck; size_t total_sent; size_t max_elelements_to_sent; @@ -1576,7 +1618,7 @@ send_iov_emu (struct MHD_Connection *restrict connection, } -#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT +#endif /* !mhd_USE_VECT_SEND || MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ @@ -1587,10 +1629,10 @@ mhd_send_iovec (struct MHD_Connection *restrict connection, size_t *restrict sent) { #ifdef mhd_USE_VECT_SEND -#if defined(HTTPS_SUPPORT) || \ +#if defined(MHD_ENABLE_HTTPS) || \ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) bool use_iov_send = true; -#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ +#endif /* MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ #endif /* mhd_USE_VECT_SEND */ mhd_assert (NULL != connection->rp.resp_iov.iov); @@ -1598,24 +1640,22 @@ mhd_send_iovec (struct MHD_Connection *restrict connection, connection->rp.response->cntn_dtype); mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent); #ifdef mhd_USE_VECT_SEND -#if defined(HTTPS_SUPPORT) || \ +#if defined(MHD_ENABLE_HTTPS) || \ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) -#ifdef HTTPS_SUPPORT use_iov_send = use_iov_send && - (true); // TODO: TLS support -#endif /* HTTPS_SUPPORT */ + (! mhd_C_HAS_TLS (connection)); #ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked || - connection->sk_spipe_suppress); + connection->sk.props.has_spipe_supp); #endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ if (use_iov_send) -#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ +#endif /* MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ return send_iov_nontls (connection, r_iov, push_data, sent); #endif /* mhd_USE_VECT_SEND */ -#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \ +#if ! defined(mhd_USE_VECT_SEND) || defined(MHD_ENABLE_HTTPS) || \ defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) return send_iov_emu (connection, r_iov, push_data, sent); -#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT +#endif /* !mhd_USE_VECT_SEND || MHD_ENABLE_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ } diff --git a/src/mhd2/mhd_send.h b/src/mhd2/mhd_send.h @@ -39,10 +39,11 @@ struct MHD_Connection; /* forward declaration */ struct mhd_iovec_track; /* forward declaration */ /** - * Initialises static variables + * Initialises static variables. + * Need to be called only once. */ void -mhd_send_init_static_vars (void); +mhd_send_init_once (void); /** @@ -143,7 +144,7 @@ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4); /** * Set required TCP_NODELAY state for connection socket * - * The function automatically updates sk_nodelay state. + * The function automatically updates sk.nodelay state. * @param connection the connection to manipulate * @param nodelay_state the requested new state of socket * @return true if succeed, false if failed or not supported @@ -158,7 +159,7 @@ MHD_FN_PAR_NONNULL_ALL_; /** * Set required cork state for connection socket * - * The function automatically updates sk_corked state. + * The function automatically updates sk.corked state. * * @param connection the connection to manipulate * @param cork_state the requested new state of socket diff --git a/src/mhd2/mhd_socket_error.c b/src/mhd2/mhd_socket_error.c @@ -1,86 +0,0 @@ -/* - This file is part of GNU libmicrohttpd - Copyright (C) 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/mhd2/mhd_socket_error.c - * @brief The definition of mhd_SocketError-related functions - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_sys_options.h" -#include "mhd_socket_error.h" -#include "sys_sockets_headers.h" -#include "mhd_sockets_macros.h" -#include "sys_sockets_types.h" - -MHD_INTERNAL enum mhd_SocketError -mhd_socket_error_get_from_sys_err (int socket_err) -{ - if (mhd_SCKT_ERR_IS_EAGAIN (socket_err)) - return mhd_SOCKET_ERR_AGAIN; - else if (mhd_SCKT_ERR_IS_CONNRESET (socket_err)) - return mhd_SOCKET_ERR_CONNRESET; - else if (mhd_SCKT_ERR_IS_EINTR (socket_err)) - return mhd_SOCKET_ERR_INTR; - else if (mhd_SCKT_ERR_IS_CONN_BROKEN (socket_err)) - return mhd_SOCKET_ERR_CONN_BROKEN; - else if (mhd_SCKT_ERR_IS_PIPE (socket_err)) - return mhd_SOCKET_ERR_PIPE; - else if (mhd_SCKT_ERR_IS_NOTCONN (socket_err)) - return mhd_SOCKET_ERR_NOTCONN; - else if (mhd_SCKT_ERR_IS_LOW_MEM (socket_err)) - return mhd_SOCKET_ERR_NOMEM; - else if (mhd_SCKT_ERR_IS_BADF (socket_err)) - return mhd_SOCKET_ERR_BADF; - else if (mhd_SCKT_ERR_IS_EINVAL (socket_err)) - return mhd_SOCKET_ERR_INVAL; - else if (mhd_SCKT_ERR_IS_OPNOTSUPP (socket_err)) - return mhd_SOCKET_ERR_OPNOTSUPP; - else if (mhd_SCKT_ERR_IS_NOTSOCK (socket_err)) - return mhd_SOCKET_ERR_NOTSOCK; - - return mhd_SOCKET_ERR_OTHER; -} - - -MHD_INTERNAL enum mhd_SocketError -mhd_socket_error_get_from_socket (MHD_Socket fd) -{ -#if defined(SOL_SOCKET) && defined(SO_ERROR) - enum mhd_SocketError err; - int sock_err; - socklen_t optlen = sizeof (sock_err); - - sock_err = 0; - if ((0 == getsockopt (fd, - SOL_SOCKET, - SO_ERROR, - (void *) &sock_err, - &optlen)) - && (sizeof(sock_err) == optlen)) - return mhd_socket_error_get_from_sys_err (sock_err); - - err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); - if ((mhd_SOCKET_ERR_NOTSOCK == err) || - (mhd_SOCKET_ERR_BADF == err)) - return err; -#endif /* SOL_SOCKET && SO_ERROR */ - return mhd_SOCKET_ERR_NOT_CHECKED; -} diff --git a/src/mhd2/mhd_socket_error.h b/src/mhd2/mhd_socket_error.h @@ -20,8 +20,7 @@ /** * @file src/mhd2/mhd_socket_error.h - * @brief The definition of the mhd_SocketError enum and related macros and - * declarations of related functions + * @brief The definition of the mhd_SocketError enum and related macros * @author Karlson2k (Evgeny Grin) */ @@ -140,22 +139,4 @@ enum MHD_FIXED_ENUM_ mhd_SocketError */ #define mhd_SOCKET_ERR_IS_BAD(err) (mhd_SOCKET_ERR_BADF <= (err)) -/** - * Map recv() / send() system socket error to the enum value - * @param socket_err the system socket error - * @return the enum value for the @a socket_err - */ -MHD_INTERNAL enum mhd_SocketError -mhd_socket_error_get_from_sys_err (int socket_err); - -/** - * Get the last socket error recoded for the given socket - * @param fd the socket to check for the error - * @return the recorded error @a fd, - * #mhd_SOCKET_ERR_NOT_CHECKED if not possible to check @a fd for - * the error - */ -MHD_INTERNAL enum mhd_SocketError -mhd_socket_error_get_from_socket (MHD_Socket fd); - #endif /* ! MHD_SOCKET_ERROR_H */ diff --git a/src/mhd2/mhd_socket_error_funcs.c b/src/mhd2/mhd_socket_error_funcs.c @@ -0,0 +1,86 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_socket_error_funcs.c + * @brief The definition of mhd_SocketError-related functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" +#include "mhd_socket_error_funcs.h" +#include "sys_sockets_headers.h" +#include "mhd_sockets_macros.h" +#include "sys_sockets_types.h" + +MHD_INTERNAL enum mhd_SocketError +mhd_socket_error_get_from_sys_err (int socket_err) +{ + if (mhd_SCKT_ERR_IS_EAGAIN (socket_err)) + return mhd_SOCKET_ERR_AGAIN; + else if (mhd_SCKT_ERR_IS_CONNRESET (socket_err)) + return mhd_SOCKET_ERR_CONNRESET; + else if (mhd_SCKT_ERR_IS_EINTR (socket_err)) + return mhd_SOCKET_ERR_INTR; + else if (mhd_SCKT_ERR_IS_CONN_BROKEN (socket_err)) + return mhd_SOCKET_ERR_CONN_BROKEN; + else if (mhd_SCKT_ERR_IS_PIPE (socket_err)) + return mhd_SOCKET_ERR_PIPE; + else if (mhd_SCKT_ERR_IS_NOTCONN (socket_err)) + return mhd_SOCKET_ERR_NOTCONN; + else if (mhd_SCKT_ERR_IS_LOW_MEM (socket_err)) + return mhd_SOCKET_ERR_NOMEM; + else if (mhd_SCKT_ERR_IS_BADF (socket_err)) + return mhd_SOCKET_ERR_BADF; + else if (mhd_SCKT_ERR_IS_EINVAL (socket_err)) + return mhd_SOCKET_ERR_INVAL; + else if (mhd_SCKT_ERR_IS_OPNOTSUPP (socket_err)) + return mhd_SOCKET_ERR_OPNOTSUPP; + else if (mhd_SCKT_ERR_IS_NOTSOCK (socket_err)) + return mhd_SOCKET_ERR_NOTSOCK; + + return mhd_SOCKET_ERR_OTHER; +} + + +MHD_INTERNAL enum mhd_SocketError +mhd_socket_error_get_from_socket (MHD_Socket fd) +{ +#if defined(SOL_SOCKET) && defined(SO_ERROR) + enum mhd_SocketError err; + int sock_err; + socklen_t optlen = sizeof (sock_err); + + sock_err = 0; + if ((0 == getsockopt (fd, + SOL_SOCKET, + SO_ERROR, + (void *) &sock_err, + &optlen)) + && (sizeof(sock_err) == optlen)) + return mhd_socket_error_get_from_sys_err (sock_err); + + err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); + if ((mhd_SOCKET_ERR_NOTSOCK == err) || + (mhd_SOCKET_ERR_BADF == err)) + return err; +#endif /* SOL_SOCKET && SO_ERROR */ + return mhd_SOCKET_ERR_NOT_CHECKED; +} diff --git a/src/mhd2/mhd_socket_error_funcs.h b/src/mhd2/mhd_socket_error_funcs.h @@ -0,0 +1,53 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_socket_error_funcs.h + * @brief The declarations of functions for getting enum mhd_SocketError values + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SOCKET_ERROR_FUNCS_H +#define MHD_SOCKET_ERROR_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_socket_type.h" +#include "mhd_socket_error.h" + +/** + * Map recv() / send() system socket error to the enum value + * @param socket_err the system socket error + * @return the enum value for the @a socket_err + */ +MHD_INTERNAL enum mhd_SocketError +mhd_socket_error_get_from_sys_err (int socket_err); + +/** + * Get the last socket error recoded for the given socket + * @param fd the socket to check for the error + * @return the recorded error @a fd, + * #mhd_SOCKET_ERR_NOT_CHECKED if not possible to check @a fd for + * the error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_socket_error_get_from_socket (MHD_Socket fd); + +#endif /* ! MHD_SOCKET_ERROR_FUNCS_H */ diff --git a/src/mhd2/mhd_socket_type.h b/src/mhd2/mhd_socket_type.h @@ -33,7 +33,7 @@ #ifndef MHD_INVALID_SOCKET # if ! defined(_WIN32) || defined(_SYS_TYPES_FD_SET) -# define MHD_POSIX_SOCKETS 1 /* The POSIX-style sockets are used */ +# define MHD_SOCKETS_KIND_POSIX 1 /* The POSIX-style sockets are used */ /** * MHD_Socket is type for socket FDs * @@ -45,7 +45,7 @@ typedef int MHD_Socket; */ # define MHD_INVALID_SOCKET (-1) # else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */ -# define MHD_WINSOCK_SOCKETS 1 /* The WinSock-style sockets are used */ +# define MHD_SOCKETS_KIND_WINSOCK 1 /* The WinSock-style sockets are used */ # include <winsock2.h> /** * MHD_Socket is type for socket FDs diff --git a/src/mhd2/mhd_sockets_funcs.c b/src/mhd2/mhd_sockets_funcs.c @@ -28,7 +28,7 @@ #include "mhd_sockets_funcs.h" #include "sys_sockets_headers.h" #include "sys_ip_headers.h" -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # ifdef HAVE_SYS_TYPES_H # include <sys/types.h> # endif @@ -38,7 +38,7 @@ # include <stdlib.h> # endif # include <fcntl.h> -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # include <windows.h> #endif #ifndef INADDR_LOOPBACK @@ -51,7 +51,7 @@ MHD_INTERNAL bool mhd_socket_nonblocking (MHD_Socket sckt) { -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) // TODO: detect constants in configure #if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL) int get_flags; @@ -68,12 +68,12 @@ mhd_socket_nonblocking (MHD_Socket sckt) if (-1 != fcntl (sckt, F_SETFL, set_flags)) return true; #endif /* F_GETFL && O_NONBLOCK && F_SETFL */ -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) unsigned long set_flag = 1; if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag)) return true; -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ return false; } @@ -82,7 +82,7 @@ mhd_socket_nonblocking (MHD_Socket sckt) MHD_INTERNAL bool mhd_socket_noninheritable (MHD_Socket sckt) { -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) // TODO: detect constants in configure #if defined(F_GETFD) && defined(FD_CLOEXEC) && defined(F_SETFD) int get_flags; @@ -99,10 +99,10 @@ mhd_socket_noninheritable (MHD_Socket sckt) if (-1 != fcntl (sckt, F_SETFD, set_flags)) return true; #endif /* F_GETFD && FD_CLOEXEC && F_SETFD */ -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) if (SetHandleInformation ((HANDLE) sckt, HANDLE_FLAG_INHERIT, 0)) return true; -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ return false; } @@ -163,7 +163,7 @@ mhd_socket_shut_wr (MHD_Socket sckt) static bool mhd_socket_blocking (MHD_Socket sckt) { -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) // TODO: detect constants in configure #if defined(F_GETFL) && defined(O_NONBLOCK) && defined(F_SETFL) int get_flags; @@ -180,12 +180,12 @@ mhd_socket_blocking (MHD_Socket sckt) if (-1 != fcntl (sckt, F_SETFL, set_flags)) return true; #endif /* F_GETFL && O_NONBLOCK && F_SETFL */ -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) unsigned long set_flag = 0; if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag)) return true; -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ return false; } diff --git a/src/mhd2/mhd_sockets_macros.h b/src/mhd2/mhd_sockets_macros.h @@ -33,7 +33,7 @@ #include "sys_base_types.h" #include "sys_sockets_headers.h" -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # include "sys_errno.h" # ifdef HAVE_UNISTD_H # include <unistd.h> @@ -41,7 +41,7 @@ # include <stdlib.h> # endif # include "sys_errno.h" -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # include <winsock2.h> #endif @@ -49,9 +49,9 @@ * Close the socket FD * @param sckt the socket to close */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_socket_close(sckt) close (sckt) -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_socket_close(sckt) closesocket (sckt) #endif @@ -91,22 +91,22 @@ /** * Last socket error */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_SCKT_GET_LERR() errno -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_GET_LERR() (WSAGetLastError ()) #endif /** * Set last socket error */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_SCKT_SET_LERR(err) do { errno = (err); } while (0) -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_SET_LERR(err) WSASetLastError ((err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # if defined(EAGAIN) && defined(EWOULDBLOCK) && \ ((EWOULDBLOCK + 0) != (EAGAIN + 0)) # define mhd_SCKT_ERR_IS_EAGAIN(err) \ @@ -118,121 +118,121 @@ # else # define mhd_SCKT_ERR_IS_EAGAIN(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_EAGAIN(err) (WSAEWOULDBLOCK == (err)) #endif #define mhd_SCKT_LERR_IS_EAGAIN() mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EAFNOSUPPORT # define mhd_SCKT_ERR_IS_AF(err) (EAFNOSUPPORT == (err)) # else # define mhd_SCKT_ERR_IS_AF(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_AF(err) (WSAEAFNOSUPPORT == (err)) #endif #define mhd_SCKT_LERR_IS_AF() mhd_SCKT_ERR_IS_AF (mhd_SCKT_GET_LERR ()) -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EINVAL # define mhd_SCKT_ERR_IS_EINVAL(err) (EINVAL == (err)) # else # define mhd_SCKT_ERR_IS_EINVAL(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_EINVAL(err) (WSAEINVAL == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EINTR # define mhd_SCKT_ERR_IS_EINTR(err) (EINTR == (err)) # else # define mhd_SCKT_ERR_IS_EINTR(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_EINTR(err) (WSAEINTR == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef ECONNRESET # define mhd_SCKT_ERR_IS_CONNRESET(err) (ECONNRESET == (err)) # else # define mhd_SCKT_ERR_IS_CONNRESET(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_CONNRESET(err) (WSAECONNRESET == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef ENOTCONN # define mhd_SCKT_ERR_IS_NOTCONN(err) (ENOTCONN == (err)) # else # define mhd_SCKT_ERR_IS_NOTCONNT(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_NOTCONN(err) (WSAENOTCONN == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EOPNOTSUPP # define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (EOPNOTSUPP == (err)) # else # define mhd_SCKT_ERR_IS_OPNOTSUPP(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (WSAEOPNOTSUPP == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef ENOPROTOOPT # define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (ENOPROTOOPT == (err)) # else # define mhd_SCKT_ERR_IS_NOPROTOOPT(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (WSAENOPROTOOPT == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EBADF # define mhd_SCKT_ERR_IS_BADF(err) (EBADF == (err)) # else # define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_BADF(err) ((void) (err), ! ! 0) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef ENOTSOCK # define mhd_SCKT_ERR_IS_NOTSOCK(err) (ENOTSOCK == (err)) # else # define mhd_SCKT_ERR_IS_NOTSOCK(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_NOTSOCK(err) (WSAENOTSOCK == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EPIPE # define mhd_SCKT_ERR_IS_PIPE(err) (EPIPE == (err)) # else # define mhd_SCKT_ERR_IS_PIPE(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_PIPE(err) (WSAESHUTDOWN == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef EINPROGRESS # define mhd_SCKT_ERR_IS_INPROGRESS(err) (EINPROGRESS == (err)) # else # define mhd_SCKT_ERR_IS_INPROGRESS(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_INPROGRESS(err) (WSAEINPROGRESS == (err)) #endif @@ -242,13 +242,13 @@ * @return boolean true is @a err match described socket error code, * boolean false otherwise. */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef ECONNABORTED # define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (ECONNABORTED == (err)) # else # define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) ((void) (err), ! ! 0) # endif -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT(err) (WSAECONNRESET == (err)) #endif @@ -258,7 +258,7 @@ * It can be keep-alive ping failure or timeout to get ACK for the * transmitted data. */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) /* + EHOSTUNREACH: probably reported by intermediate + ETIMEDOUT: probably keep-alive ping failure + ENETUNREACH: probably cable physically disconnected or similar */ @@ -267,7 +267,7 @@ ((mhd_EHOSTUNREACH_OR_ZERO == (err)) || \ (mhd_ETIMEDOUT_OR_ZERO == (err)) || \ (mhd_ENETUNREACH_OR_ZERO == (err)))) -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \ ( (WSAENETRESET == (err)) || (WSAECONNABORTED == (err)) || \ (WSAETIMEDOUT == (err)) ) @@ -278,12 +278,12 @@ * @return boolean true if @a err is any kind of "low resource" error, * boolean false otherwise. */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \ ((0 != (err)) && \ ((mhd_EMFILE_OR_ZERO == (err)) || (mhd_ENFILE_OR_ZERO == (err)) || \ (mhd_ENOMEM_OR_ZERO == (err)) || (mhd_ENOBUFS_OR_ZERO == (err)))) -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_LOW_RESOURCES(err) \ ( (WSAEMFILE == (err)) || (WSAENOBUFS == (err)) ) #endif @@ -294,16 +294,16 @@ * @return boolean true if @a err is any kind of "low memory" error, * boolean false otherwise. */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_SCKT_ERR_IS_LOW_MEM(err) \ ((0 != (err)) && \ ((mhd_ENOMEM_OR_ZERO == (err)) || (mhd_ENOBUFS_OR_ZERO == (err)))) -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_ERR_IS_LOW_MEM(err) (WSAENOBUFS == (err)) #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # ifdef HAVE_SOCKETPAIR # ifdef MHD_AF_UNIX # define mhd_socket_pair(fdarr_ptr) \ diff --git a/src/mhd2/mhd_status_code_int.h b/src/mhd2/mhd_status_code_int.h @@ -0,0 +1,42 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_status_code_int.h + * @brief The internal version of enum MHD_StatusCode + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_STATUS_CODE_INT_H +#define MHD_STATUS_CODE_INT_H 1 + +#include "mhd_sys_options.h" + +#include "sys_base_types.h" + +/** + * "Internal" version of enum MHD_StatusCode. + * + * Always mapped 1:1 to enum MHD_StatusCode, can be set only to MHD_SC_ values. + * Can be used to declare functions without including large public headers + */ +typedef uint_fast16_t mhd_StatusCodeInt; + +#endif /* ! MHD_STATUS_CODE_INT_H */ diff --git a/src/mhd2/mhd_str.c b/src/mhd2/mhd_str.c @@ -23,6 +23,8 @@ * @author Karlson2k (Evgeny Grin) */ +#include "mhd_sys_options.h" + #include "mhd_str.h" #include <string.h> diff --git a/src/mhd2/mhd_threads.c b/src/mhd2/mhd_threads.c @@ -24,9 +24,11 @@ * @author Karlson2k (Evgeny Grin) */ +#include "mhd_sys_options.h" + #include "mhd_threads.h" #include "sys_null_macro.h" -#ifdef MHD_USE_W32_THREADS +#ifdef mhd_THREADS_KIND_W32 # include <process.h> #endif #if defined(MHD_USE_THREAD_NAME_) @@ -47,7 +49,7 @@ #else /* MHD_USE_THREAD_NAME_ */ -# if defined(MHD_USE_POSIX_THREADS) +# if defined(mhd_THREADS_KIND_POSIX) # if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) # define MHD_USE_THREAD_ATTR_SETNAME 1 @@ -111,7 +113,7 @@ mhd_set_thread_name (const mhd_thread_ID_native thread_id, # define mhd_set_cur_thread_name(n) (! (pthread_setname_np ((n)))) # endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */ -# elif defined(MHD_USE_W32_THREADS) +# elif defined(mhd_THREADS_KIND_W32) # ifndef _MSC_FULL_VER #error Thread name available only for VC-compiler # else /* _MSC_FULL_VER */ @@ -167,7 +169,7 @@ mhd_set_thread_name (const mhd_thread_ID_native thread_id, # define mhd_set_cur_thread_name(n) \ mhd_set_thread_name ((mhd_thread_ID_native) (-1),(n)) # endif /* _MSC_FULL_VER */ -# endif /* MHD_USE_W32_THREADS */ +# endif /* mhd_THREADS_KIND_W32 */ #endif /* MHD_USE_THREAD_NAME_ */ @@ -189,7 +191,7 @@ mhd_create_thread (mhd_thread_handle_ID *handle_id, mhd_THREAD_START_ROUTINE start_routine, void *arg) { -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) int res; # if defined(mhd_thread_handle_ID_get_native_handle_ptr) pthread_t *const new_tid_ptr = @@ -234,7 +236,7 @@ mhd_create_thread (mhd_thread_handle_ID *handle_id, # endif /* ! mhd_thread_handle_ID_set_current_thread_ID */ return 0 == res; -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) uintptr_t thr_handle; unsigned int stack_size_w32; # if SIZEOF_SIZE_T != SIZEOF_UNSIGNED_INT @@ -262,7 +264,7 @@ mhd_create_thread (mhd_thread_handle_ID *handle_id, thr_handle); return true; -#endif /* MHD_USE_W32_THREADS */ +#endif /* mhd_THREADS_KIND_W32 */ } diff --git a/src/mhd2/mhd_threads.h b/src/mhd2/mhd_threads.h @@ -38,12 +38,12 @@ #include "mhd_sys_options.h" -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) # include <pthread.h> # ifndef MHD_USE_THREADS # define MHD_USE_THREADS 1 # endif -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) # include <windows.h> # ifndef MHD_USE_THREADS # define MHD_USE_THREADS 1 @@ -56,12 +56,12 @@ #include "sys_base_types.h" #include "sys_thread_entry_type.h" -#if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS) -# error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined -#endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */ +#if defined(mhd_THREADS_KIND_POSIX) && defined(mhd_THREADS_KIND_W32) +# error Both mhd_THREADS_KIND_POSIX and mhd_THREADS_KIND_W32 are defined +#endif /* mhd_THREADS_KIND_POSIX && mhd_THREADS_KIND_W32 */ #ifndef MHD_NO_THREAD_NAMES -# if defined(MHD_USE_POSIX_THREADS) +# if defined(mhd_THREADS_KIND_POSIX) # if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ @@ -70,7 +70,7 @@ defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) # define MHD_USE_THREAD_NAME_ # endif /* HAVE_PTHREAD_SETNAME_NP */ -# elif defined(MHD_USE_W32_THREADS) +# elif defined(mhd_THREADS_KIND_W32) # ifdef _MSC_FULL_VER /* Thread names only available with VC compiler */ # define MHD_USE_THREAD_NAME_ @@ -80,19 +80,19 @@ /* ** Thread handle - used to control the thread ** */ -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) /** * The native type to control the thread from other threads */ typedef pthread_t mhd_thread_handle_native; -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) /** * The native type to control the thread from other threads */ typedef HANDLE mhd_thread_handle_native; #endif -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) # if defined(__gnu_linux__) || \ (defined(__linux__) && defined(__GLIBC__)) /* The next part of code is disabled because it relies on undocumented @@ -107,7 +107,7 @@ typedef HANDLE mhd_thread_handle_native; ((mhd_thread_handle_native) 0) # endif /* Disabled code */ # endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */ -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) /* On W32 the invalid value for thread handle is described directly in the official documentation. */ /** @@ -115,9 +115,9 @@ typedef HANDLE mhd_thread_handle_native; */ # define mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID \ ((mhd_thread_handle_native) NULL) -#endif /* MHD_USE_W32_THREADS */ +#endif /* mhd_THREADS_KIND_W32 */ -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) /** * Wait until specified thread is ended and free the thread handle on success. * @param native_handle the handle to watch @@ -125,7 +125,7 @@ typedef HANDLE mhd_thread_handle_native; */ # define mhd_join_thread(native_handle) \ (! pthread_join ((native_handle), NULL)) -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) /** * Wait until specified thread is ended and the free thread handle on success. * @param native_handle the handle to watch @@ -211,7 +211,7 @@ typedef mhd_thread_handle_native mhd_thread_handle; /* ** Thread ID - used to check threads match ** */ -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) /** * The native type used to check whether the current thread matches * the expected thread @@ -229,7 +229,7 @@ typedef pthread_t mhd_thread_ID_native; */ # define mhd_thread_ID_native_equal(id1,id2) \ (pthread_equal ((id1),(id2))) -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) /** * The native type used to check whether the current thread matches * the expected thread @@ -258,7 +258,7 @@ typedef DWORD mhd_thread_ID_native; mhd_thread_ID_native_equal ((id), mhd_thread_ID_native_current ()) -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) # if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) /** * The native invalid value for native thread ID @@ -266,13 +266,13 @@ typedef DWORD mhd_thread_ID_native; # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID # endif /* mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID */ -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) /** * The native invalid value for native thread ID */ # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ ((mhd_thread_ID_native) 0) -#endif /* MHD_USE_W32_THREADS */ +#endif /* mhd_THREADS_KIND_W32 */ #if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) /** @@ -356,7 +356,7 @@ typedef mhd_thread_ID_native mhd_thread_ID; mhd_thread_ID_set_native ((ID_ptr),mhd_thread_ID_native_current ()) -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) # if defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \ ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) # error \ @@ -366,7 +366,7 @@ typedef mhd_thread_ID_native mhd_thread_ID; # error \ MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined # endif -#endif /* MHD_USE_POSIX_THREADS */ +#endif /* mhd_THREADS_KIND_POSIX */ /* When staring a new thread, the kernel (and thread implementation) may * pause the calling (initial) thread and start the new thread. @@ -396,7 +396,7 @@ typedef mhd_thread_ID_native mhd_thread_ID; /* * handle - must be valid when other thread knows that particular thread is started. * ID - must be valid when code is executed inside thread */ -#if defined(MHD_USE_POSIX_THREADS) && \ +#if defined(mhd_THREADS_KIND_POSIX) && \ defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \ defined(mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID) && \ defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \ @@ -408,7 +408,7 @@ union mhd_thread_handle_ID_ }; typedef union mhd_thread_handle_ID_ mhd_thread_handle_ID; # define MHD_THREAD_HANDLE_ID_IS_UNION 1 -#else /* !MHD_USE_POSIX_THREADS +#else /* !mhd_THREADS_KIND_POSIX || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ @@ -419,7 +419,7 @@ struct mhd_thread_handle_ID_ mhd_thread_ID ID; /**< To be used in the thread itself */ }; typedef struct mhd_thread_handle_ID_ mhd_thread_handle_ID; -#endif /* !MHD_USE_POSIX_THREADS +#endif /* !mhd_THREADS_KIND_POSIX || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD || !mhd_THREAD_HANDLE_NATIVE_VALUE_INVALID || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ @@ -471,9 +471,9 @@ typedef struct mhd_thread_handle_ID_ mhd_thread_handle_ID; mhd_thread_ID_is_valid ((hndl_id).ID) #if defined(MHD_THREAD_HANDLE_ID_IS_UNION) -# if defined(MHD_USE_W32_THREADS) +# if defined(mhd_THREADS_KIND_W32) # error mhd_thread_handle_ID cannot be a union with W32 threads -# endif /* MHD_USE_W32_THREADS */ +# endif /* mhd_THREADS_KIND_W32 */ /** * Set current thread ID in the variable pointed by the @a hndl_id_ptr */ diff --git a/src/mhd2/mhd_tls_choice.h b/src/mhd2/mhd_tls_choice.h @@ -0,0 +1,228 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_tls_choice.h + * @brief The TLS backend compile-time selection header + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_CHOICE_H +#define MHD_TLS_CHOICE_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_ENABLE_HTTPS +#error This header should be used only if HTTPS is enabled +#endif + +/* ** Helper macros ** */ + +/** + * Concatenate three arguments literally + */ +#define mhd_MACRO_CONCAT3_(a,b,c) a ## b ## c +/** + * Concatenate three arguments after expansion + */ +#define mhd_MACRO_CONCAT3(a,b,c) mhd_MACRO_CONCAT3_ (a,b,c) + + +/* ** Enumerate TLS backends ** */ + +/* * GnuTLS * */ + +#ifdef MHD_USE_GNUTLS +/** + * Defined to one if GnuTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_GNU_ENABLED (1) +#else +/** + * Defined to one if GnuTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_GNU_ENABLED (0) +#endif + +/** + * Return non-zero if GnuTLS is supported + */ +#define mhd_TLS_GNU_IS_SUPPORTED() (! ! mhd_TLS_GNU_ENABLED) + +/* * OpenSSL * */ + +#ifdef MHD_USE_OPENSSL +/** + * Defined to one if OpenSSL is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_OPEN_ENABLED (1) +#else +/** + * Defined to one if GnuTLS is enabled at build time or to zero if not enabled + */ +# define mhd_TLS_OPEN_ENABLED (0) +#endif + +/** + * Return non-zero if OpenSSL is supported + */ +#define mhd_TLS_OPEN_IS_SUPPORTED() (! ! mhd_TLS_GNU_ENABLED) + +/** + * Defined to the number of enabled TLS backends + */ +#define mhd_TLS_NUM_BACKENDS \ + (mhd_TLS_GNU_ENABLED + mhd_TLS_OPEN_ENABLED) + +#if mhd_TLS_NUM_BACKENDS == 0 +#error At least one TLS backend must be enabled if this header is included +#endif + +#if mhd_TLS_NUM_BACKENDS > 1 +/** + * Defined to '1' if Multi-TLS should be used + */ +# define MHD_USE_MULTITLS +#endif + +/* Sanity check */ +#if defined(MHD_USE_MULTITLS) && ! defined(mhd_HAVE_SEVERAL_TLS_BACKENDS) +#error several TLS backends set by configure, but only one available for code +#endif +#if ! defined(MHD_USE_MULTITLS) && defined(mhd_HAVE_SEVERAL_TLS_BACKENDS) +#error several TLS backends available for code, but ony one set by configure +#endif + +#ifdef MHD_USE_MULTITLS +/** + * Defined to one if Multi-TLS is enabled at build time or + * to zero if not enabled + */ +# define mhd_TLS_MULTI_ENABLED (1) +#else +/** + * Defined to one if Multi-TLS is enabled at build time or + * to zero if not enabled + */ +# define mhd_TLS_MULTI_ENABLED (0) +#endif +/** + * Return non-zero if Multi-TLS is supported + */ +#define mhd_TLS_MULTI_IS_SUPPORTED() (! ! mhd_TLS_MULTI_ENABLED) + + +#if defined(MHD_USE_MULTITLS) +/** + * The TLS back-end identifier for function names + */ +# define mhd_TLS_FUNC_NAME_ID multi +/** + * The TLS back-end identifier for data names + */ +# define mhd_TLS_DATA_NAME_ID Multi +/** + * The TLS back-end identifier for macro names + */ +# define mhd_TLS_MACRO_NAME_ID MULTI +#elif defined(MHD_USE_GNUTLS) +/** + * The TLS back-end identifier for function names + */ +# define mhd_TLS_FUNC_NAME_ID gnu +/** + * The TLS back-end identifier for data names + */ +# define mhd_TLS_DATA_NAME_ID Gnu +/** + * The TLS back-end identifier for macro names + */ +# define mhd_TLS_MACRO_NAME_ID GNU +#elif defined(MHD_USE_OPENSSL) +/** + * The TLS back-end identifier for function names + */ +# define mhd_TLS_FUNC_NAME_ID open +/** + * The TLS back-end identifier for data names + */ +# define mhd_TLS_DATA_NAME_ID Open +/** + * The TLS back-end identifier for macro names + */ +# define mhd_TLS_MACRO_NAME_ID OPEN +#endif + + +/* ** Functions replacement macros to simplify the code ** */ + +#ifndef MHD_USE_GNUTLS +/** + * Check whether GnuTLS backend was successfully initialised globally + */ +# define mhd_tls_gnu_is_inited_fine() (! ! 0) +#endif + +#ifndef MHD_USE_OPENSSL +/** + * Check whether OpenSSL backend was successfully initialised globally + */ +# define mhd_tls_open_is_inited_fine() (! ! 0) +#endif + + +/* ** Functions names and structures names macros ** */ + +/** + * Form function name specific for the selected TLS backend + */ +#define mhd_TLS_DATA(name_suffix) \ + mhd_MACRO_CONCAT3 (mhd_Tls,mhd_TLS_DATA_NAME_ID,name_suffix) + +/** + * Form name of the data specific for the selected TLS backend + */ +#define mhd_TLS_FUNC(name_suffix) \ + mhd_MACRO_CONCAT3 (mhd_tls_,mhd_TLS_FUNC_NAME_ID,name_suffix) + +/** + * The name of the structure that holds daemon-specific TLS data + */ +#define mhd_TlsDaemonData mhd_TLS_DATA (DaemonData) +/** + * The name of the structure that holds connection-specific TLS data + */ +#define mhd_TlsConnData mhd_TLS_DATA (ConnData) + + +/* ** Forward declarations ** */ + +/** + * The structure with daemon-specific TLS data + */ +struct mhd_TlsDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific TLS data + */ +struct mhd_TlsConnData; /* Forward declaration */ + + +#endif /* ! MHD_TLS_CHOICE_H */ diff --git a/src/mhd2/mhd_tls_enums.h b/src/mhd2/mhd_tls_enums.h @@ -0,0 +1,72 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_tls_enums.h + * @brief The definition of internal enums used for TLS communication + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_ENUMS_H +#define MHD_TLS_ENUMS_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_ENABLE_HTTPS +#error This header should be used only if HTTPS is enabled +#endif + +/** + * Result of performing TLS procedure + */ +enum MHD_FIXED_ENUM_ mhd_TlsProcedureResult +{ + /** + * Completed successfully + */ + mhd_TLS_PROCED_SUCCESS = 0 + , + /** + * In progress, receive operation interrupted + */ + mhd_TLS_PROCED_RECV_INTERRUPTED + , + /** + * In progress, send operation interrupted + */ + mhd_TLS_PROCED_SEND_INTERRUPTED + , + /** + * In progress, need to receive more data + */ + mhd_TLS_PROCED_RECV_MORE_NEEDED + , + /** + * In progress, need to send more data + */ + mhd_TLS_PROCED_SEND_MORE_NEEDED + , + /** + * Procedure failed + */ + mhd_TLS_PROCED_FAILED +}; + +#endif /* ! MHD_TLS_ENUMS_H */ diff --git a/src/mhd2/mhd_tls_funcs.c b/src/mhd2/mhd_tls_funcs.c @@ -0,0 +1,67 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_tls_funcs.c + * @brief The TLS backend generic functions implementation + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "mhd_tls_funcs.h" + +/* Include all supported TLS backends headers */ +#if defined(MHD_USE_GNUTLS) +# include "tls_gnu_funcs.h" +#endif +#if defined(MHD_USE_OPENSSL) +# include "tls_open_funcs.h" +#endif + +#include "mhd_assert.h" + +#include "mhd_public_api.h" +#include "daemon_options.h" + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ +enum mhd_TlsBackendAvailable +mhd_tls_is_backend_available (struct DaemonOptions *s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE != s->tls); + if (MHD_TLS_BACKEND_ANY == s->tls) + return (mhd_tls_gnu_is_inited_fine () + || mhd_tls_open_is_inited_fine ()) ? + mhd_TLS_BACKEND_AVAIL_OK : + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; +#ifdef MHD_USE_GNUTLS + if (MHD_TLS_BACKEND_GNUTLS == s->tls) + return mhd_tls_gnu_is_inited_fine () ? + mhd_TLS_BACKEND_AVAIL_OK : + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; +#endif +#ifdef MHD_USE_OPENSSL + if (MHD_TLS_BACKEND_OPENSSL == s->tls) + return mhd_tls_open_is_inited_fine () ? + mhd_TLS_BACKEND_AVAIL_OK : + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE; +#endif + return mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED; +} diff --git a/src/mhd2/mhd_tls_funcs.h b/src/mhd2/mhd_tls_funcs.h @@ -0,0 +1,222 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_tls_funcs.h + * @brief The TLS backend functions generic declaration, mapped to specific TLS + * backend at compile-time + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_FUNCS_H +#define MHD_TLS_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_tls_choice.h" +#ifndef MHD_ENABLE_HTTPS +#error This header should be used only if HTTPS is enabled +#endif + +#if defined(MHD_USE_MULTITLS) +# include "tls_multi_funcs.h" +#elif defined(MHD_USE_GNUTLS) +# include "tls_gnu_funcs.h" +#elif defined(MHD_USE_OPENSSL) +# include "tls_open_funcs.h" +#endif + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Perform one-time global initialisation of TLS backend + */ +#define mhd_tls_global_init_once() mhd_TLS_FUNC (_global_init_once)() + +/** + * Perform de-initialisation of TLS backend + */ +#define mhd_tls_global_deinit() mhd_TLS_FUNC (_global_deinit)() + +/** + * Perform re-initialisation of TLS backend + */ +#define mhd_tls_global_re_init() mhd_TLS_FUNC (_global_re_init)() + +/* ** Daemon initialisation / de-initialisation ** */ + +/** + * Check whether OpenSSL backend supports edge-triggered sockets polling + * @param s the daemon settings + * @return 'true' if the backend supports edge-triggered sockets polling, + * 'false' if edge-triggered sockets polling cannot be used + */ +#define mhd_tls_is_edge_trigg_supported(s) \ + mhd_TLS_FUNC (_is_edge_trigg_supported)((s)) + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param et if 'true' then sockets polling uses edge-triggering + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +#define mhd_tls_daemon_init(d,et,s,p_d_tls) \ + mhd_TLS_FUNC (_daemon_init)((d),(et),(s),(p_d_tls)) + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_daemon_deinit(d_tls) \ + mhd_TLS_FUNC (_daemon_deinit)((d_tls)) + + +/* ** Connection initialisation / de-initialisation ** */ + +/** + * Get size size of the connection's TLS settings + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_conn_get_tls_size(d_tls) \ + mhd_TLS_FUNC (_conn_get_tls_size)(d_tls) + +/** + * Initialise connection TLS settings + * @param d_tls the daemon TLS settings + * @param sk data about the socket for the connection + * @param[out] c_tls the pointer to the allocated space for + * the connection TLS settings + * @return 'true' on success, + * 'false' otherwise + */ +#define mhd_tls_conn_init(d_tls,sk,c_tls) \ + mhd_TLS_FUNC (_conn_init)((d_tls),(sk),(c_tls)) + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +#define mhd_tls_conn_deinit(c_tls) \ + mhd_TLS_FUNC (_conn_deinit)((c_tls)) + + +/* ** TLS connection establishing ** */ + +/** + * Perform TLS handshake + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +#define mhd_tls_conn_handshake(c_tls) \ + mhd_TLS_FUNC (_conn_handshake)((c_tls)) + +/** + * Perform shutdown of TLS layer + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +#define mhd_tls_conn_shutdown(c_tls) \ + mhd_TLS_FUNC (_conn_shutdown)((c_tls)) + +/* ** Data sending and receiving over TLS connection ** */ + +/** + * Receive the data from the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +#define mhd_tls_conn_recv(c_tls,buf_size,buf,received) \ + mhd_TLS_FUNC (_conn_recv)((c_tls),(buf_size),(buf),(received)) + +/** + * Check whether any incoming data is pending in the TLS buffers + * + * @param c_tls the connection TLS handle + * @return 'true' if any incoming remote data is already pending (the TLS recv() + * call can be performed), + * 'false' otherwise + */ +#define mhd_tls_conn_has_data_in(c_tls) \ + mhd_TLS_FUNC (_conn_has_data_in)((c_tls)) + +/** + * Send data to the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buffer_size the size of the @a buffer (in bytes) + * @param buffer content of the buffer to send + * @param[out] sent the pointer to get amount of actually sent bytes + * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets + * the sent size) or socket error + */ +#define mhd_tls_conn_send(c_tls,buf_size,buf,sent) \ + mhd_TLS_FUNC (_conn_send)((c_tls),(buf_size),(buf),(sent)) + + +/* ** General information function ** */ + +/** + * Result of TLS backend availability check + */ +enum mhd_TlsBackendAvailable +{ + /** + * The TLS backend is available and can be used + */ + mhd_TLS_BACKEND_AVAIL_OK = 0 + , + /** + * The TLS backend support is not enabled in this MHD build + */ + mhd_TLS_BACKEND_AVAIL_NOT_SUPPORTED + , + /** + * The TLS backend supported, but not available + */ + mhd_TLS_BACKEND_AVAIL_NOT_AVAILABLE +}; + +/** + * Check whether the requested TLS backend is available + * @param s the daemon settings + * @return 'mhd_TLS_BACKEND_AVAIL_OK' if requested backend is available, + * error code otherwise + */ +MHD_INTERNAL enum mhd_TlsBackendAvailable +mhd_tls_is_backend_available (struct DaemonOptions *s) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_; + +#endif /* ! MHD_TLS_FUNCS_H */ diff --git a/src/mhd2/mhd_unreachable.h b/src/mhd2/mhd_unreachable.h @@ -0,0 +1,55 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/mhd_unreachable.h + * @brief The definition of the mhd_UNREACHABLE() macro + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_UNREACHABLE_H +#define MHD_UNREACHABLE_H 1 + +#include "mhd_sys_options.h" + +#if ! defined(NDEBUG) +# include "mhd_assert.h" +#elif defined (MHD_UNREACHABLE_NEEDS_STDDEF_H) +# include <stddef.h> +#endif + +/** + * mhd_UNREACHABLE() should be used in locations where it is known in advance + * that the code must be not reachable. + * It should give compiler a hint to exclude some code paths from the final + * binary. + */ +#ifdef NDEBUG +# ifdef MHD_UNREACHABLE_KEYWORD +# define mhd_UNREACHABLE() MHD_UNREACHABLE_KEYWORD +# else +# define mhd_UNREACHABLE() ((void) 0) +# endif +#else +# define mhd_UNREACHABLE() \ + mhd_assert (0 && "This code should be unreachable") +#endif + +#endif /* ! MHD_UNREACHABLE_H */ diff --git a/src/mhd2/post_parser_funcs.c b/src/mhd2/post_parser_funcs.c @@ -29,6 +29,9 @@ #include "post_parser_funcs.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include "mhd_post_parser.h" #include <string.h> @@ -183,7 +186,7 @@ detect_post_enc (struct MHD_Connection *restrict c) { const struct MHD_StringNullable *h_cnt_tp; - mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state); + mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); h_cnt_tp = mhd_request_get_value_st (&(c->rq), MHD_VK_HEADER, @@ -399,7 +402,7 @@ init_post_parse_data (struct MHD_Connection *restrict c) case MHD_HTTP_POST_ENCODING_OTHER: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); } } @@ -416,7 +419,7 @@ mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c) { mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result); c->discard_request = true; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return false; } } @@ -427,7 +430,7 @@ mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c) { mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result); c->discard_request = true; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return false; } } @@ -550,10 +553,10 @@ report_low_lbuf_mem (struct MHD_Connection *restrict c) "parse POST request."); c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM; - if (c->state < MHD_CONNECTION_FULL_REQ_RECEIVED) + if (c->stage < mhd_HTTP_STAGE_FULL_REQ_RECEIVED) { c->discard_request = true; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; } @@ -771,15 +774,15 @@ process_complete_field_all (struct MHD_Connection *restrict c, mhd_assert ((0 == enc_start) || \ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc)); - mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage); mhd_assert (value_start + value_len <= *pfield_next_pos); - mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \ + mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \ (value_start + value_len < *pfield_next_pos)); mhd_assert (*pfield_next_pos <= *pdata_size); mhd_assert ((name_start + name_len < value_start) || \ (0 == value_start)); mhd_assert (value_start + value_len <= *pfield_next_pos); - mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \ + mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \ (name_start + name_len < *pfield_next_pos)); mhd_assert ((filename_start + filename_len < value_start) || \ (0 == value_start)); @@ -889,7 +892,7 @@ process_complete_field_all (struct MHD_Connection *restrict c, &encoding_i, &value_i)) { - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \ "The request POST data cannot be parsed completely " \ "because there is not enough pool memory."); @@ -933,14 +936,14 @@ process_complete_field (struct MHD_Connection *restrict c, size_t value_start, size_t value_len) { - mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage); mhd_assert (value_start + value_len <= *pfield_next_pos); - mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \ + mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \ (value_start + value_len < *pfield_next_pos)); mhd_assert ((name_start + name_len < value_start) || \ (0 == value_start)); mhd_assert (name_start + name_len <= *pfield_next_pos); - mhd_assert ((MHD_CONNECTION_FULL_REQ_RECEIVED <= c->state) || \ + mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \ (name_start + name_len < *pfield_next_pos)); mhd_assert (field_start <= name_start); mhd_assert ((field_start <= value_start) || (0 == value_start)); @@ -1018,7 +1021,7 @@ process_partial_value_all (struct MHD_Connection *restrict c, const struct MHD_UploadAction *act; bool res; - mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage); mhd_assert (*pnext_pos <= *pdata_size); mhd_assert (part_value_start + part_value_len <= *pnext_pos); mhd_assert (0 != part_value_start); @@ -1119,7 +1122,7 @@ process_partial_value (struct MHD_Connection *restrict c, size_t part_value_start, size_t part_value_len) { - mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage); mhd_assert (part_value_start + part_value_len <= *pnext_pos); mhd_assert (name_start + name_len < part_value_start); mhd_assert (0 != part_value_start); @@ -1324,11 +1327,11 @@ parse_post_urlenc (struct MHD_Connection *restrict c, continue; /* Process the next char */ default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } @@ -1761,7 +1764,7 @@ parse_post_mpart (struct MHD_Connection *restrict c, mhd_assert (0 && "broken quoting must be detected " \ "earlier by " \ "mhd_str_starts_with_token_req_param()"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); mf->st = mhd_POST_MPART_ST_FORMAT_ERROR; continue; } @@ -1783,7 +1786,7 @@ parse_post_mpart (struct MHD_Connection *restrict c, mhd_assert (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res); mhd_assert (0 && "The presence of the token was " "checked earlier"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); } if (hdr_has_name) @@ -1798,7 +1801,7 @@ parse_post_mpart (struct MHD_Connection *restrict c, mhd_assert (0 && "broken quoting must be detected " \ "earlier by " \ "mhd_str_starts_with_token_req_param()"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); mf->st = mhd_POST_MPART_ST_FORMAT_ERROR; continue; } @@ -1840,7 +1843,7 @@ parse_post_mpart (struct MHD_Connection *restrict c, p_data->some_data_provided ? MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT : MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; /* Stop parsing the upload */ } mhd_assert (0 != mf->f.name_len); @@ -2063,15 +2066,15 @@ parse_post_mpart (struct MHD_Connection *restrict c, p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; } - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } @@ -2289,7 +2292,7 @@ parse_post_text (struct MHD_Connection *restrict c, continue; /* Process the next char */ default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); enc_broken = true; break; } @@ -2320,7 +2323,7 @@ parse_post_text (struct MHD_Connection *restrict c, MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; } tf->st = mhd_POST_TEXT_ST_NOT_STARTED; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; } @@ -2423,10 +2426,10 @@ mhd_stream_post_parse (struct MHD_Connection *restrict c, case MHD_HTTP_POST_ENCODING_OTHER: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); p_data->parse_result = MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; } if (data_size_before_parse == p_data->lbuf_used) @@ -2568,7 +2571,7 @@ check_post_leftovers_urlenc (struct MHD_Connection *restrict c, case mhd_POST_UENC_ST_AT_AMPRSND: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; return false; } @@ -2705,7 +2708,7 @@ check_post_leftovers_mpart (struct MHD_Connection *restrict c, case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; return false; } @@ -2837,7 +2840,7 @@ check_post_leftovers_text (struct MHD_Connection *restrict c, case mhd_POST_TEXT_ST_AT_CR: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT; return false; } @@ -2905,10 +2908,10 @@ check_post_leftovers (struct MHD_Connection *restrict c) case MHD_HTTP_POST_ENCODING_OTHER: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); p_data->parse_result = MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; break; } return true; diff --git a/src/mhd2/request_get_value.c b/src/mhd2/request_get_value.c @@ -119,7 +119,7 @@ mhd_stream_has_header_token (const struct MHD_Connection *restrict c, { struct mhd_RequestField *f; - mhd_assert (MHD_CONNECTION_START_REPLY >= c->state); + mhd_assert (mhd_HTTP_STAGE_START_REPLY >= c->stage); for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields); NULL != f; diff --git a/src/mhd2/respond_with_error.c b/src/mhd2/respond_with_error.c @@ -58,7 +58,7 @@ respond_with_error_len (struct MHD_Connection *c, struct MHD_Response *err_res; mhd_assert (! c->stop_with_error); /* Do not send error twice */ - mhd_assert (MHD_CONNECTION_REQ_RECV_FINISHED >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage); /* Discard most of the request data */ @@ -119,5 +119,5 @@ respond_with_error_len (struct MHD_Connection *c, return; } c->rp.response = err_res; - c->state = MHD_CONNECTION_START_REPLY; + c->stage = mhd_HTTP_STAGE_START_REPLY; } diff --git a/src/mhd2/response_from.c b/src/mhd2/response_from.c @@ -228,9 +228,9 @@ MHD_response_from_iovec ( if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size) || (((size_t) total_size) != total_size)) return NULL; /* Larger than send function may report as success */ -#if defined(MHD_POSIX_SOCKETS) || ! defined(_WIN64) +#if defined(MHD_SOCKETS_KIND_POSIX) || ! defined(_WIN64) i_cp++; -#else /* ! MHD_POSIX_SOCKETS && _WIN64 */ +#else /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ if (1) { size_t i_add; @@ -242,7 +242,7 @@ MHD_response_from_iovec ( if (i_cp < i_add) return NULL; /* Counter overflow */ } -#endif /* ! MHD_POSIX_SOCKETS && _WIN64 */ +#endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ } if (0 == total_size) { @@ -278,7 +278,7 @@ MHD_response_from_iovec ( if (0 == element_size) continue; /* skip zero-sized elements */ -#if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64) +#if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64) while (mhd_IOV_ELMN_MAX_SIZE < element_size) { iov_copy[i_cp].iov_base = (char *) mhd_DROP_CONST (buf); @@ -287,7 +287,7 @@ MHD_response_from_iovec ( element_size -= mhd_IOV_ELMN_MAX_SIZE; i_cp++; } -#endif /* MHD_WINSOCK_SOCKETS && _WIN64 */ +#endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */ iov_copy[i_cp].iov_base = mhd_DROP_CONST (buf); iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size; i_cp++; diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c @@ -28,6 +28,9 @@ #include "stream_funcs.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include <string.h> #ifdef MHD_USE_EPOLL # include <sys/epoll.h> @@ -37,7 +40,6 @@ #include "mhd_daemon.h" #include "mhd_connection.h" #include "mhd_response.h" -#include "mhd_assert.h" #include "mhd_mempool.h" #include "mhd_str.h" #include "mhd_str_macros.h" @@ -52,6 +54,10 @@ #include "conn_mark_ready.h" #include "stream_process_reply.h" +#ifdef MHD_ENABLE_HTTPS +# include "mhd_tls_funcs.h" +#endif + #include "mhd_public_api.h" @@ -297,13 +303,13 @@ mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c, size_t opt_headers_size; size_t host_field_line_size; - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED < c->stage); mhd_assert (MHD_PROC_RECV_HEADERS <= stage); mhd_assert ((0 == add_element_size) || (NULL != add_element)); c->rq.too_large = true; - if (MHD_CONNECTION_HEADERS_RECEIVED > c->state) + if (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage) { mhd_assert (NULL != c->rq.field_lines.start); opt_headers_size = @@ -344,7 +350,7 @@ mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c, if (is_host_header) { const bool is_parsed = ! ( - (MHD_CONNECTION_HEADERS_RECEIVED > c->state) && + (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage) && (add_element_size == c->read_buffer_offset) && (c->read_buffer == add_element) ); size_t actual_element_size; @@ -554,7 +560,7 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c, c->rp.response = NULL; c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE; - c->state = MHD_CONNECTION_INIT; + c->stage = mhd_HTTP_STAGE_INIT; c->event_loop_info = (0 == c->read_buffer_offset) ? MHD_EVENT_LOOP_INFO_RECV : MHD_EVENT_LOOP_INFO_PROCESS; @@ -601,7 +607,7 @@ mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c) if (0 == timeout) return false; - now = MHD_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round + now = mhd_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round since_actv = now - c->last_activity; /* Keep the next lines in sync with #connection_get_wait() to avoid * undesired side-effects like busy-waiting. */ @@ -652,7 +658,7 @@ mhd_stream_update_activity_mark (struct MHD_Connection *restrict c) return; /* Skip update of activity for connections without timeout timer. */ - c->last_activity = MHD_monotonic_msec_counter (); // TODO: Get and use time value one time per round + c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round if (mhd_D_HAS_THR_PER_CONN (d)) return; /* each connection has personal timeout */ @@ -716,6 +722,21 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, end_code = MHD_REQUEST_ENDED_BY_APP_ABORT; sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION; break; + case mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE: + close_hard = true; + end_code = MHD_REQUEST_ENDED_FILE_ERROR; + sc = MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE; + break; + case mhd_CONN_CLOSE_FILE_READ_ERROR: + close_hard = true; + end_code = MHD_REQUEST_ENDED_FILE_ERROR; + sc = MHD_SC_REPLY_FILE_READ_ERROR; + break; + case mhd_CONN_CLOSE_FILE_TOO_SHORT: + close_hard = true; + end_code = MHD_REQUEST_ENDED_BY_APP_ERROR; + sc = MHD_SC_REPLY_FILE_TOO_SHORT; + break; case mhd_CONN_CLOSE_INT_ERROR: close_hard = true; end_code = MHD_REQUEST_ENDED_NO_RESOURCES; @@ -726,14 +747,14 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, break; case mhd_CONN_CLOSE_SOCKET_ERR: close_hard = true; - switch (c->sk_discnt_err) + switch (c->sk.state.discnt_err) { case mhd_SOCKET_ERR_NOMEM: end_code = MHD_REQUEST_ENDED_NO_RESOURCES; break; case mhd_SOCKET_ERR_REMT_DISCONN: close_hard = false; - end_code = (MHD_CONNECTION_INIT == c->state) ? + end_code = (mhd_HTTP_STAGE_INIT == c->stage) ? MHD_REQUEST_ENDED_COMPLETED_OK /* Not used */ : MHD_REQUEST_ENDED_CLIENT_ABORT; break; @@ -758,7 +779,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, case mhd_SOCKET_ERR_INTR: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); } break; case mhd_CONN_CLOSE_DAEMON_SHUTDOWN: @@ -767,7 +788,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, break; case mhd_CONN_CLOSE_TIMEDOUT: - if (MHD_CONNECTION_INIT == c->state) + if (mhd_HTTP_STAGE_INIT == c->stage) { close_hard = false; end_code = MHD_REQUEST_ENDED_COMPLETED_OK; /* Not used */ @@ -796,7 +817,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, default: mhd_assert (0 && "Unreachable code"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); end_code = MHD_REQUEST_ENDED_COMPLETED_OK; close_hard = false; } @@ -806,7 +827,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, #ifdef MHD_UPGRADE_SUPPORT if (mhd_CONN_CLOSE_UPGRADE == reason) { - mhd_assert (MHD_CONNECTION_UPGRADING == c->state); + mhd_assert (mhd_HTTP_STAGE_UPGRADING == c->stage); c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED; } else @@ -816,21 +837,42 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, if (close_hard) { /* Use abortive closing, send RST to remote to indicate a problem */ - (void) mhd_socket_set_hard_close (c->socket_fd); - c->state = MHD_CONNECTION_PRE_CLOSING; + (void) mhd_socket_set_hard_close (c->sk.fd); + c->stage = mhd_HTTP_STAGE_PRE_CLOSING; c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; } else { - if (mhd_socket_shut_wr (c->socket_fd) && (! c->sk_rmt_shut_wr)) + bool use_graceful_closing; + + mhd_assert (! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err)); + + use_graceful_closing = true; +#ifdef MHD_ENABLE_HTTPS + if (mhd_C_HAS_TLS (c)) + { + if ((0 != (((unsigned int) c->sk.ready) + & mhd_SOCKET_NET_STATE_SEND_READY)) + || c->sk.props.is_nonblck) + use_graceful_closing = + (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls)); + } +#endif /* MHD_ENABLE_HTTPS */ + else if (1) + { + use_graceful_closing = mhd_socket_shut_wr (c->sk.fd); + if (use_graceful_closing) + use_graceful_closing = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */ + } + if (use_graceful_closing) { (void) 0; // TODO: start local lingering phase - c->state = MHD_CONNECTION_PRE_CLOSING; // TODO: start local lingering phase + c->stage = mhd_HTTP_STAGE_PRE_CLOSING; // TODO: start local lingering phase c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase } else { /* No need / not possible to linger */ - c->state = MHD_CONNECTION_PRE_CLOSING; + c->stage = mhd_HTTP_STAGE_PRE_CLOSING; c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; } } @@ -845,7 +887,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, #endif /* ! HAVE_LOG_FUNCTIONALITY */ #if 0 // TODO: notification callback - mhd_assert ((MHD_CONNECTION_INIT != c->state) || (! c->rq.app_aware)); + mhd_assert ((mhd_HTTP_STAGE_INIT != c->stage) || (! c->rq.app_aware)); if ( (NULL != d->notify_completed) && (c->rq.app_aware) ) d->notify_completed (d->notify_completed_cls, @@ -893,7 +935,7 @@ mhd_conn_pre_clean_part1 (struct MHD_Connection *restrict c) event.data.ptr = NULL; if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd, EPOLL_CTL_DEL, - c->socket_fd, + c->sk.fd, &event)) { mhd_LOG_MSG (c->daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED, @@ -939,7 +981,7 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c) mhd_pool_destroy (c->pool); c->pool = NULL; - c->state = MHD_CONNECTION_CLOSED; + c->stage = mhd_HTTP_STAGE_CLOSED; #ifndef NDEBUG c->dbg.pre_cleaned = true; #endif diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h @@ -198,6 +198,21 @@ enum mhd_ConnCloseReason */ mhd_CONN_CLOSE_APP_ABORTED , + /** + * File-backed response too large (unsupported by OS) file offset + */ + mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE + , + /** + * Error reading file-backed response + */ + mhd_CONN_CLOSE_FILE_READ_ERROR + , + /** + * File-backed response has file smaller than specified by application + */ + mhd_CONN_CLOSE_FILE_TOO_SHORT + , /* Hard problem while receiving or sending */ /** diff --git a/src/mhd2/stream_process_reply.c b/src/mhd2/stream_process_reply.c @@ -34,6 +34,9 @@ #include "sys_bool_type.h" #include "sys_base_types.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include <string.h> #ifdef HAVE_TIME_H # include <time.h> @@ -45,7 +48,6 @@ #include "mhd_connection.h" #include "daemon_logger.h" -#include "mhd_assert.h" #include "mhd_str.h" #include "http_status_str.h" @@ -53,6 +55,8 @@ #include "stream_funcs.h" #include "request_get_value.h" +#include "mhd_read_file.h" + #include "mhd_public_api.h" MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void @@ -136,7 +140,7 @@ get_conn_reuse (struct MHD_Connection *c) return mhd_CONN_MUST_CLOSE; mhd_assert ( (! c->stop_with_error) || (c->discard_request)); - if ((c->sk_rmt_shut_wr) || (c->discard_request)) + if ((c->sk.state.rmt_shut_wr) || (c->discard_request)) return mhd_CONN_MUST_CLOSE; if (rp->cfg.close_forced) @@ -302,13 +306,11 @@ setup_reply_properties (struct MHD_Connection *restrict c) c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV; break; case mhd_RESPONSE_CONTENT_DATA_FILE: -#if 0 // TODO: TLS support - if (use_tls) + if (mhd_C_HAS_TLS (c)) { c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; break; } -#endif #ifdef MHD_USE_SENDFILE if (r->cntn.file.use_sf) { @@ -324,7 +326,7 @@ setup_reply_properties (struct MHD_Connection *restrict c) case mhd_RESPONSE_CONTENT_DATA_INVALID: default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; break; } @@ -892,7 +894,7 @@ mhd_stream_build_header_response (struct MHD_Connection *restrict c) "No memory in the pool for the reply headers."); return false; } - c->state = MHD_CONNECTION_HEADERS_SENDING; + c->stage = mhd_HTTP_STAGE_HEADERS_SENDING; return true; } @@ -958,7 +960,7 @@ preprocess_dcc_action (struct MHD_Connection *restrict c, break; } mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_INT_ERROR, "Impossible code path"); @@ -966,10 +968,72 @@ preprocess_dcc_action (struct MHD_Connection *restrict c, } +/** + * Read next portion of the response file to the buffer, based on information + * about amount of response content position. + * Handle file read errors, update response position. + * @param c the stream to use + * @param r the response to use + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the file data + * @param[out] size_filled the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return 'true' if succeed (size_filled and response position are updated), + * 'false' if failed (the stream is closed) + */ +static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool +read_response_file (struct MHD_Connection *restrict c, + struct MHD_Response *const restrict r, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict size_filled) +{ + mhd_assert (r == c->rp.response); + mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype); + + switch (mhd_read_file (r->cntn.file.fd, + r->cntn.file.offset + c->rp.rsp_cntn_read_pos, + buf_size, + buf, + size_filled)) + { + case mhd_FILE_READ_OK: + break; + case mhd_FILE_READ_ERROR: + mhd_STREAM_ABORT (c, \ + mhd_CONN_CLOSE_FILE_READ_ERROR, \ + "Error reading file for file-backed response."); + return false; + case mhd_FILE_READ_OFFSET_TOO_LARGE: + mhd_STREAM_ABORT (c, \ + mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE, \ + "The offset for file-backed response is too large " \ + "for this platform."); + return false; + case mhd_FILE_READ_EOF: + mhd_STREAM_ABORT (c, \ + mhd_CONN_CLOSE_FILE_TOO_SHORT, \ + "The file for file-backed response is smaller " \ + "than specified by application."); + return false; + default: + mhd_assert (0 && "Impossible value"); + mhd_UNREACHABLE (); + c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; + return false; + } + + c->rp.rsp_cntn_read_pos += *size_filled; + return true; +} + + MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) { struct MHD_Response *const restrict r = c->rp.response; + uint64_t left_to_send; mhd_assert (c->rp.props.send_reply_body); mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size); @@ -978,11 +1042,20 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) if (0 == r->cntn_size) { /* 0-byte response is always ready */ - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; return true; } mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc); + mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); + + if (MHD_SIZE_UNKNOWN == r->cntn_size) + left_to_send = MHD_SIZE_UNKNOWN; + else + left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; + + mhd_assert (0 != left_to_send); + if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) { (void) 0; /* Nothing to do, buffers are ready */ @@ -992,10 +1065,13 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) { const struct MHD_DynamicContentCreatorAction *act; - const size_t size_to_fill = + size_t size_to_fill = c->write_buffer_size - c->write_buffer_append_offset; size_t filled; + if (size_to_fill > left_to_send) + size_to_fill = (size_t) left_to_send; + mhd_assert (c->write_buffer_append_offset < c->write_buffer_size); mhd_assert (NULL == c->rp.app_act_ctx.connection); @@ -1017,7 +1093,7 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); mhd_assert (c->rp.props.end_by_closing); - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; return true; } @@ -1038,22 +1114,29 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) } else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) { - // TODO: implement fallback - mhd_assert (0 && "Not implemented yet"); - c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; - c->rp.rsp_cntn_read_pos = r->cntn_size; + size_t size_to_fill = + c->write_buffer_size - c->write_buffer_append_offset; + size_t filled; + + if (size_to_fill > left_to_send) + size_to_fill = (size_t) left_to_send; + + if (! read_response_file (c, + r, + size_to_fill, + c->write_buffer + c->write_buffer_append_offset, + &filled)) + return true; /* Error, the stream is closed */ + + c->write_buffer_append_offset += filled; } else { mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; c->rp.rsp_cntn_read_pos = r->cntn_size; } - - mhd_assert (0 && "Not implemented yet"); - c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; - c->rp.rsp_cntn_read_pos = r->cntn_size; } else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) { @@ -1088,12 +1171,12 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) else { mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; c->rp.rsp_cntn_read_pos = r->cntn_size; } - c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY; + c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY; return false; } @@ -1151,7 +1234,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) if ((0 == left_to_send) && (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype)) { - c->state = MHD_CONNECTION_CHUNKED_BODY_SENT; + c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; return true; } else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype) @@ -1185,7 +1268,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) { mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); - c->state = MHD_CONNECTION_CHUNKED_BODY_SENT; + c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; return true; } @@ -1204,6 +1287,17 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) c->rp.rsp_cntn_read_pos += filled; c->write_buffer_append_offset += filled; } + else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) + { + if (! read_response_file (c, + r, + size_to_fill, + c->write_buffer + max_chunk_hdr_len, + &filled)) + return true; /* Error, the stream is closed */ + + c->write_buffer_append_offset += filled; + } else { mhd_assert (0 && "Not implemented yet"); @@ -1229,7 +1323,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) else c->rp.rsp_cntn_read_pos = r->cntn_size; - c->state = MHD_CONNECTION_CHUNKED_BODY_READY; + c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY; return false; } @@ -1253,7 +1347,7 @@ prep_chunked_footer_inn (struct MHD_Connection *restrict c) // struct MHD_HTTP_Res_Header *pos; mhd_assert (c->rp.props.chunked); - mhd_assert (MHD_CONNECTION_CHUNKED_BODY_SENT == c->state); + mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage); mhd_assert (NULL != c->rp.response); buf_size = mhd_stream_maximize_write_buffer (c); @@ -1313,6 +1407,6 @@ mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c) "No memory in the pool for the reply chunked footer."); return true; } - c->state = MHD_CONNECTION_FOOTERS_SENDING; + c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING; return false; } diff --git a/src/mhd2/stream_process_request.c b/src/mhd2/stream_process_request.c @@ -35,6 +35,9 @@ #include "sys_bool_type.h" #include "sys_base_types.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include "sys_malloc.h" #include "mhd_str_types.h" @@ -47,7 +50,6 @@ #include "mhd_connection.h" #include "daemon_logger.h" -#include "mhd_assert.h" #include "mhd_panic.h" #include "mhd_mempool.h" @@ -616,18 +618,18 @@ get_request_line_inner (struct MHD_Connection *restrict c) RFC 9112, section 2.2 */ const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl)); - mhd_assert (MHD_CONNECTION_INIT == c->state || \ - MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_INIT == c->stage || \ + mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (NULL == c->rq.method || \ - MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \ - MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \ 0 != c->rq.hdrs.rq_line.proc_pos); if (0 == c->read_buffer_offset) { - mhd_assert (MHD_CONNECTION_INIT == c->state); + mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); return false; /* No data to process */ } p = c->rq.hdrs.rq_line.proc_pos; @@ -641,7 +643,7 @@ get_request_line_inner (struct MHD_Connection *restrict c) /* Skip empty lines before the request line. See RFC 9112, section 2.2 */ bool is_empty_line; - mhd_assert (MHD_CONNECTION_INIT == c->state); + mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); mhd_assert (NULL == c->rq.method); mhd_assert (NULL == c->rq.url); mhd_assert (0 == c->rq.url_len); @@ -692,7 +694,7 @@ get_request_line_inner (struct MHD_Connection *restrict c) } /* All empty lines are skipped */ - c->state = MHD_CONNECTION_REQ_LINE_RECEIVING; + c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVING; /* Read and parse the request line */ mhd_assert (1 <= c->read_buffer_offset); @@ -871,7 +873,7 @@ get_request_line_inner (struct MHD_Connection *restrict c) - (size_t) (c->rq.version - read_buffer))) { - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage); return true; /* Unsupported / broken HTTP version */ } read_buffer[p] = 0; /* Zero terminate the HTTP version strings */ @@ -1232,7 +1234,7 @@ process_request_target (struct MHD_Connection *c) { size_t params_len; - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (NULL == c->rq.url); mhd_assert (0 == c->rq.url_len); mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); @@ -1282,7 +1284,7 @@ process_request_target (struct MHD_Connection *c) 0, NULL), ERR_RSP_MSG_REQUEST_TOO_BIG); - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING != c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING != c->stage); return false; } @@ -1333,7 +1335,7 @@ send_redirect_fixed_rq_target (struct MHD_Connection *restrict c) size_t i; size_t o; - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri); mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ c->rq.req_target_len); @@ -1428,10 +1430,10 @@ mhd_stream_get_request_line (struct MHD_Connection *restrict c) } return false; } - if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state) + if (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage) return true; /* Error in the request */ - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); mhd_assert (NULL == c->rq.url); mhd_assert (0 == c->rq.url_len); mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); @@ -1453,7 +1455,7 @@ mhd_stream_get_request_line (struct MHD_Connection *restrict c) if (! process_request_target (c)) return true; /* Error in processing */ - c->state = MHD_CONNECTION_REQ_LINE_RECEIVED; + c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVED; return true; } @@ -1463,7 +1465,7 @@ mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c) { c->rq.field_lines.start = c->read_buffer; mhd_stream_reset_rq_hdr_proc_state (c); - c->state = MHD_CONNECTION_REQ_HEADERS_RECEIVING; + c->stage = mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING; } @@ -1604,9 +1606,9 @@ get_req_header (struct MHD_Connection *restrict c, (void) process_footers; /* Unused parameter in non-debug and no messages */ - mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \ - c->state); + mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ + c->stage); p = c->rq.hdrs.hdr.proc_pos; @@ -2033,9 +2035,9 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c, struct MHD_String hdr_value; enum mhd_HdrLineReadRes res; - mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \ - c->state); + mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ + c->stage); #ifndef NDEBUG hdr_name.cstr = NULL; @@ -2044,9 +2046,9 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c, res = get_req_header (c, process_footers, &hdr_name, &hdr_value); if (MHD_HDR_LINE_READING_GOT_HEADER == res) { - mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \ - c->state); + mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ + c->stage); mhd_assert (NULL != hdr_name.cstr); mhd_assert (NULL != hdr_value.cstr); /* Values must be zero-terminated and must not have binary zeros */ @@ -2093,29 +2095,29 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c, else handle_req_footers_no_space (c, hdr_name.cstr, add_element_size); - mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state); + mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED < c->stage); return true; } /* Reset processing state */ mhd_stream_reset_rq_hdr_proc_state (c); - mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \ - c->state); + mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ + c->stage); /* Read the next header (field) line */ continue; } else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res) { - mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \ - c->state); + mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ + c->stage); return false; } else if (MHD_HDR_LINE_READING_DATA_ERROR == res) { mhd_assert ((process_footers ? \ - MHD_CONNECTION_FOOTERS_RECEIVING : \ - MHD_CONNECTION_REQ_HEADERS_RECEIVING) < c->state); + mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ + mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) < c->stage); mhd_assert (c->stop_with_error); mhd_assert (c->discard_request); return true; @@ -2184,7 +2186,7 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c, (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1); if ('\r' == *(c->read_buffer - 2)) c->rq.field_lines.size--; - c->state = MHD_CONNECTION_HEADERS_RECEIVED; + c->stage = mhd_HTTP_STAGE_HEADERS_RECEIVED; if (mhd_BUF_INC_SIZE > c->read_buffer_size) { @@ -2215,7 +2217,7 @@ mhd_stream_get_request_headers (struct MHD_Connection *restrict c, } } else - c->state = MHD_CONNECTION_FOOTERS_RECEIVED; + c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVED; return true; } @@ -2536,7 +2538,7 @@ parse_cookie_header (struct MHD_Connection *restrict connection, break; default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } @@ -2828,7 +2830,7 @@ mhd_stream_parse_request_headers (struct MHD_Connection *restrict c) c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */ } - c->state = MHD_CONNECTION_HEADERS_PROCESSED; + c->stage = mhd_HTTP_STAGE_HEADERS_PROCESSED; return; } @@ -2844,8 +2846,8 @@ static MHD_FN_PAR_NONNULL_ALL_ bool need_100_continue (struct MHD_Connection *restrict c) { mhd_assert (MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver)); - mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state); - mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state); + mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage); + mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); if (! c->rq.have_expect_100) return false; /* "100 Continue" has not been requested by the client */ @@ -2958,7 +2960,7 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) { case mhd_ACTION_RESPONSE: c->rp.response = c->rq.app_act.head_act.data.response; - c->state = MHD_CONNECTION_REQ_RECV_FINISHED; + c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; return true; case mhd_ACTION_UPLOAD: if (0 != c->rq.cntn.cntn_size) @@ -2967,33 +2969,33 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) return true; if (need_100_continue (c)) { - c->state = MHD_CONNECTION_CONTINUE_SENDING; + c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; return true; } - c->state = MHD_CONNECTION_BODY_RECEIVING; + c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; return (0 != c->read_buffer_offset); } - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; #ifdef HAVE_POST_PARSER case mhd_ACTION_POST_PARSE: if (0 == c->rq.cntn.cntn_size) { c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY; - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; return true; } if (! mhd_stream_prepare_for_post_parse (c)) { - mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVED < c->state); + mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVED < c->stage); return true; } if (need_100_continue (c)) { - c->state = MHD_CONNECTION_CONTINUE_SENDING; + c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; return true; } - c->state = MHD_CONNECTION_BODY_RECEIVING; + c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; return true; #endif /* HAVE_POST_PARSER */ case mhd_ACTION_SUSPEND: @@ -3002,7 +3004,7 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) #ifdef MHD_UPGRADE_SUPPORT case mhd_ACTION_UPGRADE: mhd_assert (0 == c->rq.cntn.cntn_size); - c->state = MHD_CONNECTION_UPGRADE_HEADERS_SENDING; + c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; return false; #endif /* MHD_UPGRADE_SUPPORT */ case mhd_ACTION_ABORT: @@ -3013,7 +3015,7 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) mhd_assert (0 && "Impossible value"); break; } - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return false; } @@ -3052,7 +3054,7 @@ mhd_stream_process_upload_action (struct MHD_Connection *restrict c, { case mhd_UPLOAD_ACTION_RESPONSE: c->rp.response = c->rq.app_act.upl_act.data.response; - c->state = MHD_CONNECTION_REQ_RECV_FINISHED; + c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; return true; case mhd_UPLOAD_ACTION_CONTINUE: memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); @@ -3064,8 +3066,8 @@ mhd_stream_process_upload_action (struct MHD_Connection *restrict c, case mhd_UPLOAD_ACTION_UPGRADE: mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); mhd_assert (! c->rq.have_chunked_upload || \ - MHD_CONNECTION_FULL_REQ_RECEIVED == c->state); - c->state = MHD_CONNECTION_UPGRADE_HEADERS_SENDING; + mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage); + c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; return false; #endif /* MHD_UPGRADE_SUPPORT */ case mhd_UPLOAD_ACTION_ABORT: @@ -3076,7 +3078,7 @@ mhd_stream_process_upload_action (struct MHD_Connection *restrict c, mhd_assert (0 && "Impossible value"); break; } - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return false; } @@ -3265,7 +3267,7 @@ process_request_chunked_body (struct MHD_Connection *restrict c) if (0 == chunk_size) { /* The final (termination) chunk */ c->rq.cntn.cntn_size = c->rq.cntn.recv_size; - c->state = MHD_CONNECTION_BODY_RECEIVED; + c->stage = mhd_HTTP_STAGE_BODY_RECEIVED; state_updated = true; break; } @@ -3302,8 +3304,8 @@ process_request_chunked_body (struct MHD_Connection *restrict c) // TODO: support one chunk in-place processing? mhd_assert ((0 == cntn_data_ready) || \ (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ - (MHD_CONNECTION_BODY_RECEIVING != c->state)); - if (MHD_CONNECTION_BODY_RECEIVING != c->state) + (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); + if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) c->discard_request = true; c->rq.cntn.recv_size += size_provided; } @@ -3436,15 +3438,15 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c) c->read_buffer); mhd_assert ((0 == size_provided) || \ (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ - (MHD_CONNECTION_BODY_RECEIVING != c->state)); - if (MHD_CONNECTION_BODY_RECEIVING != c->state) + (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); + if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) c->discard_request = true; read_buf_reuse = true; c->rq.cntn.proc_size += cntn_data_ready; if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) { - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; state_updated = true; } } @@ -3464,7 +3466,7 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c) read_buf_reuse = true; if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) { - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; state_updated = true; } } @@ -3568,7 +3570,7 @@ mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c) if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size) c->discard_request = true; mhd_assert (NULL != c->rp.response); - c->state = MHD_CONNECTION_START_REPLY; + c->stage = mhd_HTTP_STAGE_START_REPLY; return true; } @@ -3629,24 +3631,24 @@ handle_recv_no_space (struct MHD_Connection *c, { mhd_assert (MHD_PROC_RECV_INIT <= stage); mhd_assert (MHD_PROC_RECV_FOOTERS >= stage); - mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state); + mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage); mhd_assert ((MHD_PROC_RECV_INIT != stage) || \ - (MHD_CONNECTION_INIT == c->state)); + (mhd_HTTP_STAGE_INIT == c->stage)); mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \ - (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state)); + (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_URI != stage) || \ - (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state)); + (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \ - (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state)); + (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \ - (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state)); + (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage)); mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */ mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ - (MHD_CONNECTION_BODY_RECEIVING == c->state)); + (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ - (MHD_CONNECTION_BODY_RECEIVING == c->state)); + (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \ - (MHD_CONNECTION_FOOTERS_RECEIVING == c->state)); + (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage)); mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ (! c->rq.have_chunked_upload)); mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ @@ -3844,7 +3846,7 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) c->read_buffer_size); if ((rbuff_grow_desired) && - (MHD_CONNECTION_BODY_RECEIVING == c->state)) + (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)) { if (! c->rq.have_chunked_upload) { @@ -3892,12 +3894,12 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) { enum MHD_ProcRecvDataStage stage; - switch (c->state) + switch (c->stage) { - case MHD_CONNECTION_INIT: + case mhd_HTTP_STAGE_INIT: stage = MHD_PROC_RECV_INIT; break; - case MHD_CONNECTION_REQ_LINE_RECEIVING: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd) stage = MHD_PROC_RECV_METHOD; else if (0 == c->rq.req_target_len) @@ -3905,45 +3907,45 @@ mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) else stage = MHD_PROC_RECV_HTTPVER; break; - case MHD_CONNECTION_REQ_HEADERS_RECEIVING: + case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: stage = MHD_PROC_RECV_HEADERS; break; - case MHD_CONNECTION_BODY_RECEIVING: + case mhd_HTTP_STAGE_BODY_RECEIVING: stage = c->rq.have_chunked_upload ? MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL; break; - case MHD_CONNECTION_FOOTERS_RECEIVING: + case mhd_HTTP_STAGE_FOOTERS_RECEIVING: stage = MHD_PROC_RECV_FOOTERS; break; - case MHD_CONNECTION_REQ_LINE_RECEIVED: - case MHD_CONNECTION_HEADERS_RECEIVED: - case MHD_CONNECTION_HEADERS_PROCESSED: - case MHD_CONNECTION_CONTINUE_SENDING: - case MHD_CONNECTION_BODY_RECEIVED: - case MHD_CONNECTION_FOOTERS_RECEIVED: - case MHD_CONNECTION_FULL_REQ_RECEIVED: - case MHD_CONNECTION_REQ_RECV_FINISHED: - case MHD_CONNECTION_START_REPLY: - case MHD_CONNECTION_HEADERS_SENDING: - case MHD_CONNECTION_HEADERS_SENT: - case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY: - case MHD_CONNECTION_UNCHUNKED_BODY_READY: - case MHD_CONNECTION_CHUNKED_BODY_UNREADY: - case MHD_CONNECTION_CHUNKED_BODY_READY: - case MHD_CONNECTION_CHUNKED_BODY_SENT: - case MHD_CONNECTION_FOOTERS_SENDING: - case MHD_CONNECTION_FULL_REPLY_SENT: - case MHD_CONNECTION_PRE_CLOSING: - case MHD_CONNECTION_CLOSED: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: + case mhd_HTTP_STAGE_HEADERS_RECEIVED: + case mhd_HTTP_STAGE_HEADERS_PROCESSED: + case mhd_HTTP_STAGE_CONTINUE_SENDING: + case mhd_HTTP_STAGE_BODY_RECEIVED: + case mhd_HTTP_STAGE_FOOTERS_RECEIVED: + case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: + case mhd_HTTP_STAGE_REQ_RECV_FINISHED: + case mhd_HTTP_STAGE_START_REPLY: + case mhd_HTTP_STAGE_HEADERS_SENDING: + case mhd_HTTP_STAGE_HEADERS_SENT: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: + case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_CHUNKED_BODY_READY: + case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: + case mhd_HTTP_STAGE_FOOTERS_SENDING: + case mhd_HTTP_STAGE_FULL_REPLY_SENT: + case mhd_HTTP_STAGE_PRE_CLOSING: + case mhd_HTTP_STAGE_CLOSED: #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADE_HEADERS_SENDING: - case MHD_CONNECTION_UPGRADING: - case MHD_CONNECTION_UPGRADED: - case MHD_CONNECTION_UPGRADED_CLEANING: + case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: + case mhd_HTTP_STAGE_UPGRADING: + case mhd_HTTP_STAGE_UPGRADED: + case mhd_HTTP_STAGE_UPGRADED_CLEANING: #endif /* MHD_UPGRADE_SUPPORT */ default: mhd_assert (0); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); stage = MHD_PROC_RECV_BODY_NORMAL; } diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c @@ -34,7 +34,11 @@ #include "sys_bool_type.h" #include "sys_base_types.h" +#include "mhd_assert.h" +#include "mhd_unreachable.h" + #include "mhd_str_macros.h" +#include "mhd_socket_error_funcs.h" #include "mhd_daemon.h" #include "mhd_connection.h" @@ -51,147 +55,153 @@ # include "upgrade_proc.h" #endif /* MHD_UPGRADE_SUPPORT */ -/** - * Update current processing state: need to receive, need to send. - * Mark stream as ready or not ready for processing. - * Grow the receive buffer if neccesary, close stream if no buffer space left, - * but connection needs to receive. - * @param c the connection to update - * @return true if connection states updated successfully, - * false if connection has been prepared for closing - */ -static MHD_FN_PAR_NONNULL_ALL_ bool -update_active_state (struct MHD_Connection *restrict c) +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c) { - /* Do not update states of suspended connection */ - mhd_assert (! c->suspended); - - if (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) - { - mhd_assert (0 && "Should be handled earlier"); - mhd_conn_start_closing_skt_err (c); - return false; - } - -#if 0 // def HTTPS_SUPPORT // TODO: implement TLS support - if (MHD_TLS_CONN_NO_TLS != connection->tls_state) - { /* HTTPS connection. */ - switch (connection->tls_state) - { - } - } -#endif /* HTTPS_SUPPORT */ - switch (c->state) +#ifdef MHD_ENABLE_HTTPS + mhd_assert (! mhd_C_HAS_TLS (c) || \ + (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state)); + mhd_assert (mhd_C_HAS_TLS (c) || \ + (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state)); +#endif /* MHD_ENABLE_HTTPS */ + + switch (c->stage) { - case MHD_CONNECTION_INIT: - case MHD_CONNECTION_REQ_LINE_RECEIVING: + case mhd_HTTP_STAGE_INIT: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; break; - case MHD_CONNECTION_REQ_LINE_RECEIVED: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_REQ_HEADERS_RECEIVING: + case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; break; - case MHD_CONNECTION_HEADERS_RECEIVED: - case MHD_CONNECTION_HEADERS_PROCESSED: + case mhd_HTTP_STAGE_HEADERS_RECEIVED: + case mhd_HTTP_STAGE_HEADERS_PROCESSED: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_CONTINUE_SENDING: + case mhd_HTTP_STAGE_CONTINUE_SENDING: c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; - case MHD_CONNECTION_BODY_RECEIVING: + case mhd_HTTP_STAGE_BODY_RECEIVING: c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; break; - case MHD_CONNECTION_BODY_RECEIVED: + case mhd_HTTP_STAGE_BODY_RECEIVED: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_FOOTERS_RECEIVING: + case mhd_HTTP_STAGE_FOOTERS_RECEIVING: c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; break; - case MHD_CONNECTION_FOOTERS_RECEIVED: + case mhd_HTTP_STAGE_FOOTERS_RECEIVED: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_FULL_REQ_RECEIVED: + case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: mhd_assert (0 && "Should not be possible"); c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS; break; - case MHD_CONNECTION_REQ_RECV_FINISHED: + case mhd_HTTP_STAGE_REQ_RECV_FINISHED: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_START_REPLY: + case mhd_HTTP_STAGE_START_REPLY: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_HEADERS_SENDING: + case mhd_HTTP_STAGE_HEADERS_SENDING: /* headers in buffer, keep writing */ c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; - case MHD_CONNECTION_HEADERS_SENT: + case mhd_HTTP_STAGE_HEADERS_SENT: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADE_HEADERS_SENDING: + case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; #endif /* MHD_UPGRADE_SUPPORT */ - case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: mhd_assert (0 && "Should not be possible"); c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS; break; - case MHD_CONNECTION_UNCHUNKED_BODY_READY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; - case MHD_CONNECTION_CHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: mhd_assert (0 && "Should not be possible"); c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS; break; - case MHD_CONNECTION_CHUNKED_BODY_READY: + case mhd_HTTP_STAGE_CHUNKED_BODY_READY: c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; - case MHD_CONNECTION_CHUNKED_BODY_SENT: + case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_FOOTERS_SENDING: + case mhd_HTTP_STAGE_FOOTERS_SENDING: c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND; break; - case MHD_CONNECTION_FULL_REPLY_SENT: + case mhd_HTTP_STAGE_FULL_REPLY_SENT: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADING: + case mhd_HTTP_STAGE_UPGRADING: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_UPGRADED: + case mhd_HTTP_STAGE_UPGRADED: mhd_assert (0 && "Should not be possible"); c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED; break; - case MHD_CONNECTION_UPGRADED_CLEANING: + case mhd_HTTP_STAGE_UPGRADED_CLEANING: mhd_assert (0 && "Should be unreachable"); c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; break; #endif /* MHD_UPGRADE_SUPPORT */ - case MHD_CONNECTION_PRE_CLOSING: + case mhd_HTTP_STAGE_PRE_CLOSING: mhd_assert (0 && "Should be unreachable"); c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; break; - case MHD_CONNECTION_CLOSED: + case mhd_HTTP_STAGE_CLOSED: mhd_assert (0 && "Should be unreachable"); c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; - return false; /* do nothing, not even reading */ + break; default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); } +} + + +/** + * Update current processing state: need to receive, need to send. + * Mark stream as ready or not ready for processing. + * Grow the receive buffer if neccesary, close stream if no buffer space left, + * but connection needs to receive. + * @param c the connection to update + * @return true if connection states updated successfully, + * false if connection has been prepared for closing + */ +static MHD_FN_PAR_NONNULL_ALL_ bool +update_active_state (struct MHD_Connection *restrict c) +{ + /* Do not update states of suspended connection */ + mhd_assert (! c->suspended); + + if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)) + { + mhd_assert (0 && "Should be handled earlier"); + mhd_conn_start_closing_skt_err (c); + return false; + } + + mhd_conn_event_loop_state_update (c); if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)) { @@ -208,14 +218,9 @@ update_active_state (struct MHD_Connection *restrict c) mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info); /* Sockets errors must be already handled */ - mhd_assert (0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)); + mhd_assert (0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)); - if (0 != - (((unsigned int) c->sk_ready) & ((unsigned int) c->event_loop_info) - & (MHD_EVENT_LOOP_INFO_RECV | MHD_EVENT_LOOP_INFO_SEND))) - mhd_conn_mark_ready (c, c->daemon); - else - mhd_conn_mark_unready (c, c->daemon); + mhd_conn_mark_ready_update (c); return true; } @@ -230,12 +235,12 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) /* 'daemon' is not used if epoll is not available and asserts are disabled */ (void) d; /* Mute compiler warning */ - if ((c->sk_rmt_shut_wr) && (MHD_CONNECTION_START_REPLY > c->state)) + if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage)) { if (0 == c->read_buffer_offset) { /* Read buffer is empty, connection state is actual */ mhd_conn_start_closing (c, - (MHD_CONNECTION_INIT == c->state) ? + (mhd_HTTP_STAGE_INIT == c->stage) ? mhd_CONN_CLOSE_HTTP_COMPLETED : mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY, NULL); @@ -251,14 +256,14 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) (void) 0; } - if ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) || - (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY))) + if ((mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err) || + (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))) { - mhd_assert ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) || \ - mhd_SOCKET_ERR_IS_HARD (c->sk_discnt_err)); - if ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) || - (mhd_SOCKET_ERR_NOT_CHECKED == c->sk_discnt_err)) - c->sk_discnt_err = mhd_socket_error_get_from_socket (c->socket_fd); + mhd_assert ((mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err) || \ + mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err)); + if ((mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err) || + (mhd_SOCKET_ERR_NOT_CHECKED == c->sk.state.discnt_err)) + c->sk.state.discnt_err = mhd_socket_error_get_from_socket (c->sk.fd); mhd_conn_start_closing_skt_err (c); return false; } @@ -275,60 +280,63 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) while (! c->suspended) { -#ifdef HTTPS_SUPPORT - // TODO: support TLS, handshake -#endif /* HTTPS_SUPPORT */ - switch (c->state) +#ifdef MHD_ENABLE_HTTPS + mhd_assert (! mhd_C_HAS_TLS (c) || \ + (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state)); + mhd_assert (mhd_C_HAS_TLS (c) || \ + (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state)); +#endif /* MHD_ENABLE_HTTPS */ + switch (c->stage) { - case MHD_CONNECTION_INIT: - case MHD_CONNECTION_REQ_LINE_RECEIVING: + case mhd_HTTP_STAGE_INIT: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: if (mhd_stream_get_request_line (c)) { - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage); mhd_assert ((MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver)) \ || (c->discard_request)); continue; } - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING >= c->stage); break; - case MHD_CONNECTION_REQ_LINE_RECEIVED: + case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: mhd_stream_switch_to_rq_headers_proc (c); - mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED != c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED != c->stage); continue; - case MHD_CONNECTION_REQ_HEADERS_RECEIVING: + case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: if (mhd_stream_get_request_headers (c, false)) { - mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < c->state); - mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == c->state) || \ + mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING < c->stage); + mhd_assert ((mhd_HTTP_STAGE_HEADERS_RECEIVED == c->stage) || \ (c->discard_request)); continue; } - mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage); break; - case MHD_CONNECTION_HEADERS_RECEIVED: + case mhd_HTTP_STAGE_HEADERS_RECEIVED: mhd_stream_parse_request_headers (c); - mhd_assert (c->state != MHD_CONNECTION_HEADERS_RECEIVED); + mhd_assert (c->stage != mhd_HTTP_STAGE_HEADERS_RECEIVED); continue; - case MHD_CONNECTION_HEADERS_PROCESSED: + case mhd_HTTP_STAGE_HEADERS_PROCESSED: if (mhd_stream_call_app_request_cb (c)) { - mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED < c->state); + mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED < c->stage); continue; } // TODO: add assert break; - case MHD_CONNECTION_CONTINUE_SENDING: + case mhd_HTTP_STAGE_CONTINUE_SENDING: if (c->continue_message_write_offset == mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY)) { #ifdef MHD_UPGRADE_SUPPORT c->rp.sent_100_cntn = true; #endif /* MHD_UPGRADE_SUPPORT */ - c->state = MHD_CONNECTION_BODY_RECEIVING; + c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; continue; } break; - case MHD_CONNECTION_BODY_RECEIVING: + case mhd_HTTP_STAGE_BODY_RECEIVING: mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size); mhd_assert (! c->discard_request); mhd_assert (NULL == c->rp.response); @@ -340,7 +348,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) mhd_assert (! c->discard_request); mhd_assert (NULL == c->rp.response); break; - case MHD_CONNECTION_BODY_RECEIVED: + case mhd_HTTP_STAGE_BODY_RECEIVED: mhd_assert (! c->discard_request); mhd_assert (NULL == c->rp.response); mhd_assert (c->rq.have_chunked_upload); @@ -348,97 +356,97 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) c->rq.num_cr_sp_replaced = 0; c->rq.skipped_broken_lines = 0; mhd_stream_reset_rq_hdr_proc_state (c); - c->state = MHD_CONNECTION_FOOTERS_RECEIVING; + c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVING; continue; - case MHD_CONNECTION_FOOTERS_RECEIVING: + case mhd_HTTP_STAGE_FOOTERS_RECEIVING: mhd_assert (c->rq.have_chunked_upload); if (mhd_stream_get_request_headers (c, true)) { - mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < c->state); - mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == c->state) || \ + mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING < c->stage); + mhd_assert ((mhd_HTTP_STAGE_FOOTERS_RECEIVED == c->stage) || \ (c->discard_request)); continue; } - mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == c->state); + mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage); break; - case MHD_CONNECTION_FOOTERS_RECEIVED: + case mhd_HTTP_STAGE_FOOTERS_RECEIVED: mhd_assert (c->rq.have_chunked_upload); - c->state = MHD_CONNECTION_FULL_REQ_RECEIVED; + c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; continue; - case MHD_CONNECTION_FULL_REQ_RECEIVED: + case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: if (mhd_stream_call_app_final_upload_cb (c)) { - mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING != c->state); + mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING != c->stage); continue; } break; - case MHD_CONNECTION_REQ_RECV_FINISHED: + case mhd_HTTP_STAGE_REQ_RECV_FINISHED: if (mhd_stream_process_req_recv_finished (c)) continue; break; // TODO: add stage for setup and full request buffers cleanup - case MHD_CONNECTION_START_REPLY: + case mhd_HTTP_STAGE_START_REPLY: mhd_assert (NULL != c->rp.response); mhd_stream_switch_from_recv_to_send (c); if (! mhd_stream_build_header_response (c)) continue; - mhd_assert (MHD_CONNECTION_START_REPLY != c->state); + mhd_assert (mhd_HTTP_STAGE_START_REPLY != c->stage); break; - case MHD_CONNECTION_HEADERS_SENDING: + case mhd_HTTP_STAGE_HEADERS_SENDING: /* no default action, wait for sending all the headers */ break; - case MHD_CONNECTION_HEADERS_SENT: + case mhd_HTTP_STAGE_HEADERS_SENT: if (c->rp.props.send_reply_body) { if (c->rp.props.chunked) - c->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY; + c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY; else - c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY; } else - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; continue; #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADE_HEADERS_SENDING: + case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: if (! mhd_upgrade_try_start_upgrading (c)) break; continue; #endif /* MHD_UPGRADE_SUPPORT */ - case MHD_CONNECTION_UNCHUNKED_BODY_READY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: mhd_assert (c->rp.props.send_reply_body); mhd_assert (! c->rp.props.chunked); /* nothing to do here, send the data */ break; - case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: mhd_assert (c->rp.props.send_reply_body); mhd_assert (! c->rp.props.chunked); if (0 == c->rp.response->cntn_size) { /* a shortcut */ - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; continue; } if (mhd_stream_prep_unchunked_body (c)) continue; break; - case MHD_CONNECTION_CHUNKED_BODY_READY: + case mhd_HTTP_STAGE_CHUNKED_BODY_READY: mhd_assert (c->rp.props.send_reply_body); mhd_assert (c->rp.props.chunked); /* nothing to do here */ break; - case MHD_CONNECTION_CHUNKED_BODY_UNREADY: + case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: mhd_assert (c->rp.props.send_reply_body); mhd_assert (c->rp.props.chunked); if ( (0 == c->rp.response->cntn_size) || (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size) ) { - c->state = MHD_CONNECTION_CHUNKED_BODY_SENT; + c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; continue; } if (mhd_stream_prep_chunked_body (c)) continue; break; - case MHD_CONNECTION_CHUNKED_BODY_SENT: + case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: mhd_assert (c->rp.props.send_reply_body); mhd_assert (c->rp.props.chunked); mhd_assert (c->write_buffer_send_offset <= \ @@ -447,55 +455,55 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) if (mhd_stream_prep_chunked_footer (c)) continue; break; - case MHD_CONNECTION_FOOTERS_SENDING: + case mhd_HTTP_STAGE_FOOTERS_SENDING: mhd_assert (c->rp.props.send_reply_body); mhd_assert (c->rp.props.chunked); /* no default action */ break; - case MHD_CONNECTION_FULL_REPLY_SENT: + case mhd_HTTP_STAGE_FULL_REPLY_SENT: // FIXME: support MHD_HTTP_STATUS_PROCESSING ? /* Reset connection after complete reply */ mhd_stream_finish_req_serving ( \ c, mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse && ! c->discard_request - && ! c->sk_rmt_shut_wr); + && ! c->sk.state.rmt_shut_wr); continue; #ifdef MHD_UPGRADE_SUPPORT - case MHD_CONNECTION_UPGRADING: + case mhd_HTTP_STAGE_UPGRADING: if (mhd_upgrade_finish_switch_to_upgraded (c)) return true; /* Do not close connection */ - mhd_assert (MHD_CONNECTION_PRE_CLOSING == c->state); + mhd_assert (mhd_HTTP_STAGE_PRE_CLOSING == c->stage); continue; - case MHD_CONNECTION_UPGRADED: + case mhd_HTTP_STAGE_UPGRADED: mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; - case MHD_CONNECTION_UPGRADED_CLEANING: + case mhd_HTTP_STAGE_UPGRADED_CLEANING: mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; #endif /* MHD_UPGRADE_SUPPORT */ - case MHD_CONNECTION_PRE_CLOSING: + case mhd_HTTP_STAGE_PRE_CLOSING: return false; - case MHD_CONNECTION_CLOSED: + case mhd_HTTP_STAGE_CLOSED: mhd_assert (0 && "Should be unreachable"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; default: mhd_assert (0 && "Impossible value"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); break; } break; } - mhd_assert (MHD_CONNECTION_CLOSED != c->state); + mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage); - if (MHD_CONNECTION_PRE_CLOSING == c->state) + if (mhd_HTTP_STAGE_PRE_CLOSING == c->stage) { mhd_assert (0 && "Pre-closing should be already caught in the loop"); - MHD_UNREACHABLE_; + mhd_UNREACHABLE (); return false; } @@ -506,10 +514,10 @@ mhd_conn_process_data (struct MHD_Connection *restrict c) return true; } - if ((c->sk_rmt_shut_wr) && (MHD_CONNECTION_START_REPLY > c->state)) + if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage)) { mhd_conn_start_closing (c, - (MHD_CONNECTION_INIT == c->state) ? + (mhd_HTTP_STAGE_INIT == c->stage) ? mhd_CONN_CLOSE_HTTP_COMPLETED : mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY, NULL); diff --git a/src/mhd2/stream_process_states.h b/src/mhd2/stream_process_states.h @@ -35,8 +35,20 @@ struct MHD_Connection; /* forward declaration */ /** - * Process states and the data for the connection - * For HTTP/1.1 connection is equal stream + * Update event loop state for the connection. + * Event loop state indicates current stage: sending or receiving the data. + * The underlying connection layer (plain TCP or TLS) must be already connected. + * @param c the connection to process + */ +MHD_INTERNAL void +mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + +/** + * Process states and the data for the connection. + * For HTTP/1.1: connection is equal stream. + * The function updates the states of the connection. + * The underlying connection layer (plain TCP or TLS) must be already connected. * @param c the connection to process * @return true if states and data has been successfully processed, * false if connection needs to be closed diff --git a/src/mhd2/sys_ip_headers.h b/src/mhd2/sys_ip_headers.h @@ -32,7 +32,7 @@ #include "mhd_socket_type.h" #include "sys_sockets_headers.h" -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # ifdef HAVE_INETLIB_H # include <inetLib.h> # endif /* HAVE_INETLIB_H */ diff --git a/src/mhd2/sys_poll.h b/src/mhd2/sys_poll.h @@ -34,10 +34,10 @@ #ifdef MHD_USE_POLL # include "mhd_socket_type.h" -# if defined(MHD_POSIX_SOCKETS) +# if defined(MHD_SOCKETS_KIND_POSIX) # include <poll.h> # define mhd_poll poll -# elif defined(MHD_WINSOCK_SOCKETS) +# elif defined(MHD_SOCKETS_KIND_WINSOCK) # include <winsock2.h> # define mhd_poll WSAPoll # else diff --git a/src/mhd2/sys_select.h b/src/mhd2/sys_select.h @@ -31,7 +31,7 @@ #ifdef MHD_USE_SELECT # include "mhd_socket_type.h" -# if defined(MHD_POSIX_SOCKETS) +# if defined(MHD_SOCKETS_KIND_POSIX) # ifdef HAVE_SYS_SELECT_H # include <sys/select.h> # else @@ -50,7 +50,7 @@ # include <selectLib.h> # endif # endif -# elif defined(MHD_WINSOCK_SOCKETS) +# elif defined(MHD_SOCKETS_KIND_WINSOCK) # include <winsock2.h> # else #error Uknown sockets type diff --git a/src/mhd2/sys_sockets_headers.h b/src/mhd2/sys_sockets_headers.h @@ -33,7 +33,7 @@ #include "mhd_socket_type.h" -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # include "sys_base_types.h" /* required on old platforms */ # ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> @@ -41,32 +41,32 @@ # ifdef HAVE_SOCKLIB_H # include <sockLib.h> # endif /* HAVE_SOCKLIB_H */ -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # include <winsock2.h> #endif #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif -#if defined(HAVE_SOCK_NONBLOCK) && ! defined(MHD_WINSOCK_SOCKETS) +#if defined(HAVE_SOCK_NONBLOCK) && ! defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SOCK_NONBLOCK SOCK_NONBLOCK #else # define mhd_SOCK_NONBLOCK (0) #endif -#if defined(SOCK_CLOEXEC) && ! defined(MHD_WINSOCK_SOCKETS) +#if defined(SOCK_CLOEXEC) && ! defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SOCK_CLOEXEC SOCK_CLOEXEC #else # define mhd_SOCK_CLOEXEC (0) #endif -#if defined(SOCK_NOSIGPIPE) && ! defined(MHD_WINSOCK_SOCKETS) +#if defined(SOCK_NOSIGPIPE) && ! defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SOCK_NOSIGPIPE SOCK_NOSIGPIPE #else # define mhd_SOCK_NOSIGPIPE (0) #endif -#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS) +#if defined(MSG_NOSIGNAL) && ! defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_MSG_NOSIGNAL MSG_NOSIGNAL #else # define mhd_MSG_NOSIGNAL (0) @@ -95,28 +95,28 @@ * mhd_SCKT_OPT_BOOL is the type for bool parameters * for setsockopt()/getsockopt() functions */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define mhd_SCKT_OPT_BOOL int -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define mhd_SCKT_OPT_BOOL BOOL -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ /** * mhd_SCKT_SEND_SIZE is type used to specify size for send() and recv() * functions */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) typedef size_t mhd_SCKT_SEND_SIZE; -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) typedef int mhd_SCKT_SEND_SIZE; #endif /** * MHD_SCKT_SEND_MAX_SIZE_ is maximum send()/recv() size value. */ -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # define MHD_SCKT_SEND_MAX_SIZE_ SSIZE_MAX -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) # define MHD_SCKT_SEND_MAX_SIZE_ (0x7FFFFFFF) /* INT_MAX */ #endif @@ -132,7 +132,7 @@ typedef int mhd_SCKT_SEND_SIZE; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ - defined(MHD_WINSOCK_SOCKETS) || defined(__MACH__) || defined(__sun) || \ + defined(MHD_SOCKETS_KIND_WINSOCK) || defined(__MACH__) || defined(__sun) || \ defined(SOMEBSD) /* Most of the OSes inherit nonblocking setting from the listen socket */ # define MHD_ACCEPTED_INHERITS_NONBLOCK 1 @@ -150,10 +150,10 @@ typedef int mhd_SCKT_SEND_SIZE; * so sendfile() or writev() calls are avoided in application threads. */ # define mhd_SEND_SPIPE_SUPPRESS_POSSIBLE 1 -#endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */ +#endif /* MHD_SOCKETS_KIND_WINSOCK || MHD_socket_nosignal_ || MSG_NOSIGNAL */ -#if ! defined(MHD_WINSOCK_SOCKETS) +#if ! defined(MHD_SOCKETS_KIND_WINSOCK) /** * Indicate that suppression of SIGPIPE is required for some network * system calls. diff --git a/src/mhd2/sys_sockets_types.h b/src/mhd2/sys_sockets_types.h @@ -34,7 +34,7 @@ #include "mhd_socket_type.h" -#ifdef MHD_POSIX_SOCKETS +#ifdef MHD_SOCKETS_KIND_POSIX # ifdef HAVE_SYS_SOCKET_H # include <sys/socket.h> # else diff --git a/src/mhd2/sys_thread_entry_type.h b/src/mhd2/sys_thread_entry_type.h @@ -29,10 +29,10 @@ #include "mhd_sys_options.h" -#if defined(MHD_USE_POSIX_THREADS) +#if defined(mhd_THREADS_KIND_POSIX) # define mhd_THRD_RTRN_TYPE void* # define mhd_THRD_CALL_SPEC -#elif defined(MHD_USE_W32_THREADS) +#elif defined(mhd_THREADS_KIND_W32) # define mhd_THRD_RTRN_TYPE unsigned # define mhd_THRD_CALL_SPEC __stdcall #endif diff --git a/src/mhd2/sys_w32_ver.h b/src/mhd2/sys_w32_ver.h @@ -0,0 +1,44 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/sys_w32_ver.h + * @brief Define _WIN32_WINNT macro, include minimal required system header if + * necessary + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_SYS_W32_VER_H +#define MHD_SYS_W32_VER_H 1 + +#include "mhd_sys_options.h" + +#ifndef _WIN32 +#error This file must not be used on non-W32 systems +#endif + +#ifndef _WIN32_WINNT +# ifdef HAVE_SDKDDKVER_H +# include <sdkddkver.h> +# else +# include <windows.h> +#endif + +#endif /* ! MHD_SYS_W32_VER_H */ diff --git a/src/mhd2/tls_dh_params.h b/src/mhd2/tls_dh_params.h @@ -0,0 +1,49 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_dh_params.h + * @brief The Diffie-Hellman parameters for TLS backends without RFC7919 + * support + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_DH_PARAMS_H +#define MHD_TLS_DH_PARAMS_H 1 + +#include "mhd_sys_options.h" + +/** + * The PEM encoded PKCS#3 Diffie-Hellman parameters. + * + * To be used only if TLS backend does not support RFC7919 directly. + * Generated by GnuTLS "certtool --get-dh-params --sec-param=medium" command. + */ +static const char mhd_tls_dh_params_pkcs3[] = + "-----BEGIN DH PARAMETERS-----\n" \ + "MIIBDAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n" \ + "+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n" \ + "87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n" \ + "YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n" \ + "7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n" \ + "ssbzSibBsu/6iGtCOGEoXJf//////////wIBAgICAQA=\n" \ + "-----END DH PARAMETERS-----\n"; + +#endif /* ! MHD_TLS_DH_PARAMS_H */ diff --git a/src/mhd2/tls_gnu_conn_data.h b/src/mhd2/tls_gnu_conn_data.h @@ -0,0 +1,72 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_gnu_conn_data.h + * @brief The definition of GnuTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_CONN_DATA_H +#define MHD_TLS_GNU_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_gnu_tls_lib.h" + +#include "sys_bool_type.h" + +#ifndef NDEBUG +struct mhd_TlsGnuConnDebug +{ + unsigned int is_inited; + unsigned int is_tls_handshake_completed; + unsigned int is_finished; + unsigned int is_failed; +}; +#endif /* ! NDEBUG */ + +/** + * The structure with connection-specific GnuTLS data + */ +struct mhd_TlsGnuConnData +{ + /** + * GnuTLS session data + */ + gnutls_session_t sess; + + /** + * 'true' if received EOF (the remote side initiated shutting down) + */ + bool rmt_shut_tls_wr; +#ifndef NDEBUG + /** + * Debugging data + */ + struct mhd_TlsGnuConnDebug dbg; +#endif /* ! NDEBUG */ +}; + +#endif /* ! MHD_TLS_GNU_CONN_DATA_H */ diff --git a/src/mhd2/tls_gnu_daemon_data.h b/src/mhd2/tls_gnu_daemon_data.h @@ -0,0 +1,61 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_gnu_daemon_data.h + * @brief The definition of GnuTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_DAEMON_DATA_H +#define MHD_TLS_GNU_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_gnu_tls_lib.h" + +/** + * The structure with daemon-specific GnuTLS data + */ +struct mhd_TlsGnuDaemonData +{ + /** + * The credentials + */ + gnutls_certificate_credentials_t cred; + +#ifdef mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 + /** + * Diffie-Hellman parameters + */ + gnutls_dh_params_t dh_params; +#endif + + /** + * TLS priorities cache + */ + gnutls_priority_t pri_cache; +}; + +#endif /* ! MHD_TLS_GNU_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_gnu_funcs.c b/src/mhd2/tls_gnu_funcs.c @@ -0,0 +1,785 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_gnu_funcs.c + * @brief The implementation of GnuTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include <string.h> + +#include "mhd_socket_type.h" +#include "mhd_str_types.h" + +#include "mhd_str_macros.h" +#include "mhd_arr_num_elems.h" + +#include "compat_calloc.h" +#include "sys_malloc.h" +#include "mhd_assert.h" + +#include "mhd_conn_socket.h" + +#include "tls_gnu_tls_lib.h" + +#include "tls_gnu_daemon_data.h" +#include "tls_gnu_conn_data.h" +#include "tls_gnu_funcs.h" + +#include "daemon_options.h" + +#include "mhd_public_api.h" +#include "daemon_logger.h" + +#ifdef mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 +# include "tls_dh_params.h" +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# include <stdio.h> /* For TLS debug printing */ +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +static void +mhd_tls_gnu_debug_print (int level, const char *msg) +{ + (void) fprintf (stderr, "## GnuTLS %02i: %s", + level, + msg); + (void) fflush (stderr); +} + + +#endif /* mhd_USE_TLS_DEBUG_MESSAGES */ + +/* ** Global initialisation / de-initialisation ** */ + +static bool gnutls_lib_inited = false; + +MHD_INTERNAL void +mhd_tls_gnu_global_init (void) +{ +#ifdef GNUTLS_VERSION + /* Make sure that used shared GnuTLS library has least the same version as + MHD was configured for. Fail if the version is earlier. */ + gnutls_lib_inited = (NULL != gnutls_check_version (GNUTLS_VERSION)); +#endif + gnutls_lib_inited = + gnutls_lib_inited && (GNUTLS_E_SUCCESS == gnutls_global_init ()); + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + gnutls_global_set_log_function (&mhd_tls_gnu_debug_print); + gnutls_global_set_log_level (2); +#endif +} + + +MHD_INTERNAL void +mhd_tls_gnu_global_deinit (void) +{ +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + gnutls_global_set_log_level (0); +#endif + + if (gnutls_lib_inited) + gnutls_global_deinit (); + gnutls_lib_inited = false; +} + + +MHD_INTERNAL MHD_FN_PURE_ bool +mhd_tls_gnu_is_inited_fine (void) +{ + return gnutls_lib_inited; +} + + +/* ** Daemon initialisation / de-initialisation ** */ + +/** + * Check application-provided daemon TLS settings + * @param d the daemon handle + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +check_app_tls_sessings (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE != s->tls); + mhd_assert ((MHD_TLS_BACKEND_GNUTLS == s->tls) || \ + (MHD_TLS_BACKEND_ANY == s->tls)); + if (NULL == s->tls_cert_key.v_mem_cert) + { + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "No valid TLS certificate is provided"); + return MHD_SC_TLS_CONF_BAD_CERT; + } + mhd_assert (NULL != s->tls_cert_key.v_mem_key); + + return MHD_SC_OK; +} + + +/** + * Initialise daemon TLS Diffie-Hellman parameters. + * + * This function initialise Diffie-Hellman parameters for the daemon based + * on GnuTLS recommended defaults. + * With modern GnuTLS versions this function is no-op and always succeed. + * + * This function does not put any messages to the log. + * @param d_tls the daemon TLS data + * @return 'true' if succeed, + * 'false' if failed + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ bool +daemon_init_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls) +{ +#if defined(mhd_TLS_GNU_DH_PARAMS_USE_KNOWN) + /* Rely on reasonable TLS defaults set in the TLS library. + Modern GnuTLS versions relies completely on RFC 7919 and do not need + this function therefore do not bother implementing special + application-defined settings just for limited number of GnuTLS + versions (>= 3.5.6 && < 3.6.0). */ + return (GNUTLS_E_SUCCESS == + gnutls_certificate_set_known_dh_params (d_tls->cred, + GNUTLS_SEC_PARAM_MEDIUM)); +#elif defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3) + gnutls_datum_t dh_data; + if (GNUTLS_E_SUCCESS != + gnutls_dh_params_init (&(d_tls->dh_params))) + return false; + + dh_data.data = mhd_DROP_CONST (mhd_tls_dh_params_pkcs3); + dh_data.size = sizeof (mhd_tls_dh_params_pkcs3); + if (GNUTLS_E_SUCCESS == + gnutls_dh_params_import_pkcs3 (d_tls->dh_params, + &dh_data, + GNUTLS_X509_FMT_PEM)) + { + gnutls_certificate_set_dh_params (d_tls->cred, + d_tls->dh_params); + return true; /* success exit point */ + } + /* Below is a clean-up code path */ + gnutls_dh_params_deinit (d_tls->dh_params); + return false; +#else + (void) d_tls; /* Mute compiler warning */ + return true; +#endif +} + + +/** + * De-initialise daemon TLS Diffie-Hellman parameters. + * @param d_tls the daemon TLS data + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_dh_data (struct mhd_TlsGnuDaemonData *restrict d_tls) +{ +#if defined(mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3) + mhd_assert (NULL != d_tls->dh_params); + gnutls_dh_params_deinit (d_tls->dh_params); +#else + (void) d_tls; /* Mute compiler warning */ +#endif +} + + +/** + * Set daemon TLS credentials (and Diffie-Hellman parameters). + * This function puts error messages to the log if needed. + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_credentials (struct MHD_Daemon *restrict d, + struct mhd_TlsGnuDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + size_t cert_len; + size_t key_len; + + if (GNUTLS_E_SUCCESS != + gnutls_certificate_allocate_credentials (&(d_tls->cred))) + { + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS credentials for the daemon"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + // TODO: Support multiple certificates + cert_len = strlen (s->tls_cert_key.v_mem_cert); // TODO: Reuse calculated length + key_len = strlen (s->tls_cert_key.v_mem_key); // TODO: Reuse calculated length + + mhd_assert (0 != cert_len); + mhd_assert (0 != key_len); + + if ((cert_len != (unsigned int) cert_len) + || (key_len != (unsigned int) key_len)) + ret = MHD_SC_TLS_CONF_BAD_CERT; /* Very unlikely, do not waste space on special message */ + else + { + gnutls_datum_t cert_data; + gnutls_datum_t key_data; + int res; + + cert_data.data = mhd_DROP_CONST (s->tls_cert_key.v_mem_cert); + cert_data.size = (unsigned int) cert_len; + key_data.data = mhd_DROP_CONST (s->tls_cert_key.v_mem_key); + key_data.size = (unsigned int) key_len; + res = gnutls_certificate_set_x509_key_mem2 (d_tls->cred, + &cert_data, + &key_data, + GNUTLS_X509_FMT_PEM, + s->tls_cert_key.v_mem_pass, + 0); + if (0 > res) + { + mhd_LOG_PRINT (d, \ + MHD_SC_TLS_CONF_BAD_CERT, \ + mhd_LOG_FMT ("Failed to set the provided " \ + "TLS certificate: %s"), + gnutls_strerror (res)); + ret = MHD_SC_TLS_CONF_BAD_CERT; + } + else + { + if (! daemon_init_dh_data (d_tls)) + { + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise Diffie-Hellman parameters " \ + "for the daemon"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + return MHD_SC_OK; + } + } + + gnutls_certificate_free_credentials (d_tls->cred); + mhd_assert (MHD_SC_OK != ret); + return ret; /* Failure exit point */ +} + + +/** + * Free daemon fully allocated credentials (and Diffie-Hellman parameters). + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_credentials (struct mhd_TlsGnuDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->cred); + /* To avoid dangling pointer to DH data in the credentials, + free credentials first and then free DH data. */ + gnutls_certificate_free_credentials (d_tls->cred); + daemon_deinit_dh_data (d_tls); +} + + +static const struct MHD_StringNullable tlsgnulib_base_priorities[] = { + {0, NULL} /* Replaced with app-defined name */ + , + /* Do not use "multi-keyword": if the first configuration is found, but has + some error, the next configuration is not tried. */ +#if 0 /* ifdef mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY */ + mhd_MSTR_INIT ("@LIBMICROHTTPD,SYSTEM") +#else + mhd_MSTR_INIT ("@LIBMICROHTTPD") + , + mhd_MSTR_INIT ("@SYSTEM") +#endif + , + {0, NULL} + , + mhd_MSTR_INIT ("NORMAL") +}; + +/** + * Initialise GnuTLS priorities cache + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_priorities_cache (struct MHD_Daemon *restrict d, + struct mhd_TlsGnuDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + size_t i; + + (void) s; // TODO: support app-defined name for TLS backend profile + + for (i = 0; i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities); ++i) + { + int res; + + if (0 == i) + continue; // TODO: support app-defined name for TLS backend profile +#if ! defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY) + if (NULL == tlsgnulib_base_priorities[i].cstr) + { + /* GnuTLS default priorities */ +# if defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY) + d_tls->pri_cache = NULL; + break; +# else + continue; /* "default" priorities cannot be used */ +# endif + } +#endif /* ! mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY */ + res = gnutls_priority_init (&(d_tls->pri_cache), + tlsgnulib_base_priorities[i].cstr, + NULL); + if (GNUTLS_E_SUCCESS == res) + break; + if (GNUTLS_E_MEMORY_ERROR == res) + return MHD_SC_DAEMON_MALLOC_FAILURE; + } + + if (i < mhd_ARR_NUM_ELEMS (tlsgnulib_base_priorities)) + return MHD_SC_OK; /* Success exit point */ + + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS priorities cache"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; +} + + +/** + * De-initialise priorities cache + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_priorities_cache (struct mhd_TlsGnuDaemonData *restrict d_tls) +{ +#if ! defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY) + mhd_assert (NULL != d_tls->pri_cache); +#else + if (NULL != d_tls->pri_cache) +#endif + gnutls_priority_deinit (d_tls->pri_cache); +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) mhd_StatusCodeInt +mhd_tls_gnu_daemon_init3 (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s, + struct mhd_TlsGnuDaemonData **restrict p_d_tls) +{ + mhd_StatusCodeInt res; + struct mhd_TlsGnuDaemonData *restrict d_tls; + + /* Successful initialisation must be checked earlier */ + mhd_assert (gnutls_lib_inited); + + res = check_app_tls_sessings (d, s); + if (MHD_SC_OK != res) + return res; + + d_tls = (struct mhd_TlsGnuDaemonData *) + mhd_calloc (1, sizeof (struct mhd_TlsGnuDaemonData)); + *p_d_tls = d_tls; + if (NULL == d_tls) + return MHD_SC_DAEMON_MALLOC_FAILURE; + + res = daemon_init_credentials (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_priorities_cache (d, + d_tls, + s); + if (MHD_SC_OK == res) + return MHD_SC_OK; /* Success exit point */ + + /* Below is a clean-up code path */ + daemon_deinit_credentials (d_tls); + } + + free (d_tls); + *p_d_tls = NULL; + mhd_assert (MHD_SC_OK != res); + return res; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_tls_gnu_daemon_deinit (struct mhd_TlsGnuDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls); + daemon_deinit_priorities_cache (d_tls); + daemon_deinit_credentials (d_tls); + free (d_tls); +} + + +/* ** Connection initialisation / de-initialisation ** */ + +MHD_INTERNAL size_t +mhd_tls_gnu_conn_get_tls_size_v (void) +{ + return sizeof (struct mhd_TlsGnuConnData); +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) bool +mhd_tls_gnu_conn_init (const struct mhd_TlsGnuDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsGnuConnData *restrict c_tls) +{ + unsigned int c_flags; + int res; + + c_flags = GNUTLS_SERVER; +#if GNUTLS_VERSION_NUMBER >= 0x030000 + /* Note: the proper support for the blocking sockets may require use of + gnutls_handshake_set_timeout() and + gnutls_transport_set_pull_timeout_function() (the latter is not actually + required for the modern GnuTLS versions at least) */ + if (sk->props.is_nonblck) + c_flags |= GNUTLS_NONBLOCK; +#endif +#ifdef mhd_TLS_GNU_HAS_NO_SIGNAL + c_flags |= GNUTLS_NO_SIGNAL; +#endif + + if (GNUTLS_E_SUCCESS != + gnutls_init (&(c_tls->sess), + c_flags)) + return false; + +#if GNUTLS_VERSION_NUMBER >= 0x030100 + if (! sk->props.is_nonblck) + gnutls_handshake_set_timeout (c_tls->sess, + GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); +#endif +#if ! defined(mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY) + mhd_assert (NULL != d_tls->pri_cache); +#else + if (NULL == d_tls->pri_cache) + res = gnutls_set_default_priority (c_tls->sess); + else +#endif + res = gnutls_priority_set (c_tls->sess, + d_tls->pri_cache); + + if (GNUTLS_E_SUCCESS == res) + { + if (GNUTLS_E_SUCCESS == + gnutls_credentials_set (c_tls->sess, + GNUTLS_CRD_CERTIFICATE, + d_tls->cred)) + { +#if defined(mhd_TLS_GNU_HAS_TRANSP_SET_INT) && defined(MHD_SOCKETS_KIND_POSIX) + gnutls_transport_set_int (c_tls->sess, + sk->fd); +#elif defined(MHD_SOCKETS_KIND_POSIX) + gnutls_transport_set_ptr (c_tls->sess, + mhd_INT_TO_PTR (sk->fd)); +#else /* MHD_SOCKETS_KIND_WINSOCK */ + gnutls_transport_set_ptr (c_tls->sess, + mhd_UINT_TO_PTR (sk->fd)); +#endif /* MHD_SOCKETS_KIND_WINSOCK */ + + /* The basic TLS session properties has been set. + The rest is optional settings. */ +#ifdef mhd_TLS_GNU_HAS_ALPN + if (1) + { + static const char alpn_http_1_0[] = "http/1.0"; /* Registered value for HTTP/1.0 */ + static const char alpn_http_1_1[] = "http/1.1"; /* Registered value for HTTP/1.1 */ +# if 0 /* Disabled code */ + static const char alpn_http_2[] = "h2"; /* Registered value for HTTP/2 over TLS */ + static const char alpn_http_3[] = "h3"; /* Registered value for HTTP/3 */ +# endif + gnutls_datum_t prots[] = { + { mhd_DROP_CONST (alpn_http_1_1), mhd_SSTR_LEN (alpn_http_1_1) } + , + { mhd_DROP_CONST (alpn_http_1_0), mhd_SSTR_LEN (alpn_http_1_0) } + }; + unsigned int alpn_flags; + int alpn_res; + + alpn_flags = 0; +# if 0 + alpn_flags |= GNUTLS_ALPN_SERVER_PRECEDENCE; +# endif + + alpn_res = + gnutls_alpn_set_protocols (c_tls->sess, + prots, + (unsigned int) mhd_ARR_NUM_ELEMS (prots), + alpn_flags); + (void) alpn_res; /* Ignore any possible ALPN set errors */ + } +#endif /* mhd_TLS_GNU_HAS_ALPN */ +#ifndef NDEBUG + c_tls->dbg.is_inited = true; +#endif /* ! NDEBUG */ + + return true; /* Success exit point */ + } + /* Below is a clean-up code path */ + } + + gnutls_deinit (c_tls->sess); + return false; /* Failure exit point */ +} + + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls) +{ + mhd_assert (NULL != c_tls->sess); + mhd_assert (c_tls->dbg.is_inited); + gnutls_deinit (c_tls->sess); +} + + +/* ** TLS connection establishing ** */ + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (! c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_finished); + mhd_assert (! c_tls->dbg.is_failed); + + res = gnutls_handshake (c_tls->sess); + switch (res) + { + case GNUTLS_E_SUCCESS: +#ifndef NDEBUG + c_tls->dbg.is_tls_handshake_completed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_SUCCESS; + case GNUTLS_E_INTERRUPTED: + case GNUTLS_E_AGAIN: + case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */ + if (1) + { + int is_sending; + + is_sending = gnutls_record_get_direction (c_tls->sess); + if (GNUTLS_E_INTERRUPTED == res) + return is_sending ? + mhd_TLS_PROCED_SEND_INTERRUPTED : + mhd_TLS_PROCED_RECV_INTERRUPTED; + return is_sending ? + mhd_TLS_PROCED_SEND_MORE_NEEDED : + mhd_TLS_PROCED_RECV_MORE_NEEDED; + } + break; + default: + break; + } +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_FAILED; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_finished); + mhd_assert (! c_tls->dbg.is_failed); + + res = gnutls_bye (c_tls->sess, + c_tls->rmt_shut_tls_wr ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR); + switch (res) + { + case GNUTLS_E_SUCCESS: +#ifndef NDEBUG + c_tls->dbg.is_finished = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_SUCCESS; + case GNUTLS_E_INTERRUPTED: + case GNUTLS_E_AGAIN: + case GNUTLS_E_WARNING_ALERT_RECEIVED: /* Ignore any warning for now */ + if (1) + { + int is_sending; + + is_sending = gnutls_record_get_direction (c_tls->sess); + if (GNUTLS_E_INTERRUPTED == res) + return is_sending ? + mhd_TLS_PROCED_SEND_INTERRUPTED : + mhd_TLS_PROCED_RECV_INTERRUPTED; + return is_sending ? + mhd_TLS_PROCED_SEND_MORE_NEEDED : + mhd_TLS_PROCED_RECV_MORE_NEEDED; + } + break; + default: + break; + } +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_FAILED; +} + + +/* ** Data receiving and sending ** */ + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + ssize_t res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_finished); + mhd_assert (! c_tls->dbg.is_failed); + + /* Check for GnuTLS return value limitation */ + if (0 > (ssize_t) buf_size) + buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */ + + res = gnutls_record_recv (c_tls->sess, + buf, + buf_size); + if (0 >= res) + { + *received = 0; + switch (res) + { + case 0: /* Not an error */ + c_tls->rmt_shut_tls_wr = true; + return mhd_SOCKET_ERR_NO_ERROR; + case GNUTLS_E_AGAIN: + return mhd_SOCKET_ERR_AGAIN; + case GNUTLS_E_INTERRUPTED: + return mhd_SOCKET_ERR_INTR; + case GNUTLS_E_PREMATURE_TERMINATION: + return mhd_SOCKET_ERR_CONNRESET; + default: + break; + } + /* Treat all other kinds of errors as hard errors */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; + } + + *received = (size_t) res; + return mhd_SOCKET_ERR_NO_ERROR; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls) +{ + return 0 != gnutls_record_check_pending (c_tls->sess); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_gnu_conn_send (struct mhd_TlsGnuConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +{ + ssize_t res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_finished); + mhd_assert (! c_tls->dbg.is_failed); + + /* Check for GnuTLS return value limitation */ + if (0 > (ssize_t) buf_size) + buf_size = (ssize_t) ((~((size_t) 0u)) >> 1); /* SSIZE_MAX */ + + res = gnutls_record_send (c_tls->sess, + buf, + buf_size); + + mhd_assert (0 != res); + + if (0 > res) + { + *sent = 0; + switch (res) + { + case GNUTLS_E_AGAIN: + return mhd_SOCKET_ERR_AGAIN; + case GNUTLS_E_INTERRUPTED: + return mhd_SOCKET_ERR_INTR; + case GNUTLS_E_PREMATURE_TERMINATION: + return mhd_SOCKET_ERR_CONNRESET; + default: + break; + } + /* Treat all other kinds of errors as hard errors */ +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; + } + + *sent = (size_t) res; + return mhd_SOCKET_ERR_NO_ERROR; +} diff --git a/src/mhd2/tls_gnu_funcs.h b/src/mhd2/tls_gnu_funcs.h @@ -0,0 +1,252 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_gnu_funcs.h + * @brief The declarations of GnuTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_FUNCS_H +#define MHD_TLS_GNU_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_status_code_int.h" + +#include "mhd_tls_enums.h" +#include "mhd_socket_error.h" + +/** + * The structure with daemon-specific GnuTLS data + */ +struct mhd_TlsGnuDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific GnuTLS data + */ +struct mhd_TlsGnuConnData; /* Forward declaration */ + + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Globally initialise GnuTLS backend + */ +MHD_INTERNAL void +mhd_tls_gnu_global_init (void); + +/* An alias for mhd_tls_gnu_global_init() */ +#define mhd_tls_gnu_global_init_once() mhd_tls_gnu_global_init () + +/* An alias for mhd_tls_gnu_global_init() */ +#define mhd_tls_gnu_global_re_init() mhd_tls_gnu_global_init () + +/** + * Globally de-initialise GnuTLS backend + */ +MHD_INTERNAL void +mhd_tls_gnu_global_deinit (void); + +/** + * Check whether GnuTLS backend was successfully initialised globally + * @return 'true' if backend has been successfully initialised, + * 'false' if backend cannot be used + */ +MHD_INTERNAL bool +mhd_tls_gnu_is_inited_fine (void) +MHD_FN_PURE_; + + +/* ** Daemon initialisation / de-initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Check whether GnuTLS backend supports edge-triggered sockets polling + * @param s the daemon settings + * @return 'true' if the backend supports edge-triggered sockets polling, + * 'false' if edge-triggered sockets polling cannot be used + */ +#define mhd_tls_gnu_is_edge_trigg_supported(s) (! 0) + + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +MHD_INTERNAL mhd_StatusCodeInt +mhd_tls_gnu_daemon_init3 (struct MHD_Daemon *restrict d, + struct DaemonOptions *restrict s, + struct mhd_TlsGnuDaemonData **restrict p_d_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param et if 'true' then sockets polling uses edge-triggering + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +#define mhd_tls_gnu_daemon_init(d,et,s,p_d_tls) \ + mhd_tls_gnu_daemon_init3 ((d),(s),(p_d_tls)) + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +MHD_INTERNAL void +mhd_tls_gnu_daemon_deinit (struct mhd_TlsGnuDaemonData *restrict d_tls) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + + +/* ** Connection initialisation / de-initialisation ** */ + +struct mhd_ConnSocket; /* Forward declaration */ + +/** + * Get size size of the connection's TLS settings + */ +MHD_INTERNAL size_t +mhd_tls_gnu_conn_get_tls_size_v (void); + +/** + * Get size size of the connection's TLS settings + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_gnu_conn_get_tls_size(d_tls) \ + mhd_tls_gnu_conn_get_tls_size_v () + +/** + * Initialise connection TLS settings + * @param d_tls the daemon TLS settings + * @param sk data about the socket for the connection + * @param[out] c_tls the pointer to the allocated space for + * the connection TLS settings + * @return 'true' on success, + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_gnu_conn_init (const struct mhd_TlsGnuDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsGnuConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL void +mhd_tls_gnu_conn_deinit (struct mhd_TlsGnuConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + + +/* ** TLS connection establishing ** */ + +/** + * Perform TLS handshake + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_gnu_conn_handshake (struct mhd_TlsGnuConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + +/** + * Perform shutdown of TLS layer + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_gnu_conn_shutdown (struct mhd_TlsGnuConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + + +/* ** Data sending and receiving over TLS connection ** */ + +/** + * Receive the data from the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_gnu_conn_recv (struct mhd_TlsGnuConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +/** + * Check whether any incoming data is pending in the TLS buffers + * + * @param c_tls the connection TLS handle + * @return 'true' if any incoming remote data is already pending (the TLS recv() + * call can be performed), + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_gnu_conn_has_data_in (struct mhd_TlsGnuConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + +/** + * Send data to the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buffer_size the size of the @a buffer (in bytes) + * @param buffer content of the buffer to send + * @param[out] sent the pointer to get amount of actually sent bytes + * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets + * the sent size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_gnu_conn_send (struct mhd_TlsGnuConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +#endif /* ! MHD_TLS_GNU_FUNCS_H */ diff --git a/src/mhd2/tls_gnu_tls_lib.h b/src/mhd2/tls_gnu_tls_lib.h @@ -0,0 +1,125 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_gnu_tls_lib.h + * @brief The wrapper for GnuTLS header + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_GNU_TLS_LIB_H +#define MHD_TLS_GNU_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_GNUTLS +#error This header can be used only if GnuTLS is enabled +#endif + +#include <gnutls/gnutls.h> + +#ifndef GNUTLS_VERSION_NUMBER +#error GNUTLS_VERSION_NUMBER is not defined +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030600 +/** + * Indicate that RC7919 defaults are used + */ +# define mhd_TLS_GNU_HAS_RFC7919_DEFS 1 +#endif + +#ifndef mhd_TLS_GNU_HAS_RFC7919_DEFS +# if GNUTLS_VERSION_NUMBER >= 0x030506 +/** + * Use gnutls_certificate_set_known_dh_params() function to set DH parameters + */ +# define mhd_TLS_GNU_DH_PARAMS_USE_KNOWN 1 +# else +/** + * TLS backend needs encoded Diffie-Hellman parameters + */ +# define mhd_TLS_GNU_DH_PARAMS_NEEDS_PKCS3 1 +# endif +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x020104 +/** + * Defined if gnutls_set_default_priority() function is available + */ +# define mhd_TLS_GNU_HAS_SET_DEF_PRIORITY 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030300 +/** + * Defined if NULL is treated as "default priorities" when used as "priorities" + * argument for gnutls_priority_init() and gnutls_priority_init2() + */ +# define mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY 1 +#endif + +#if ! defined(mhd_TLS_GNU_TREATS_NULL_AS_DEF_PRIORITY) \ + && defined(mhd_TLS_GNU_HAS_SET_DEF_PRIORITY) +/** + * Defined if NULL in priorities cache is treated as indication that default + * priorities must used for connection / session + */ +# define mhd_TLS_GNU_NULL_PRIO_CACHE_MEANS_DEF_PRIORITY 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030603 +/** + * Defined if gnutls_priority_init2() function and the flag + * GNUTLS_PRIORITY_INIT_DEF_APPEND are available + */ +# define mhd_TLS_GNU_HAS_PRIORITY_INIT2 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030501 +/** + * Defined if gnutls_priority_init{,2}() functions support "@KEYWORD1,@KEYWORD2" + * to as fallback values. + */ +# define mhd_TLS_GNU_SUPPORTS_MULTI_KEYWORDS_PRIORITY 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030402 +/** + * Defined if GNUTLS_NO_SIGNAL flag is available for gnutls_init() function + */ +# define mhd_TLS_GNU_HAS_NO_SIGNAL 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030109 +/** + * Defined if transport_set_int() function is available + */ +# define mhd_TLS_GNU_HAS_TRANSP_SET_INT 1 +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030200 +/** + * Defined if gnutls_alpn_set_protocols() and + * gnutls_alpn_get_selected_protocol() function are available + */ +# define mhd_TLS_GNU_HAS_ALPN 1 +#endif + +#endif /* ! MHD_TLS_GNU_TLS_LIB_H */ diff --git a/src/mhd2/tls_multi_conn_data.h b/src/mhd2/tls_multi_conn_data.h @@ -0,0 +1,80 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_multi_conn_data.h + * @brief The definition of MultiTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MULTI_CONN_DATA_H +#define MHD_TLS_MULTI_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#include "tls_multi_tls_lib.h" + +#ifndef MHD_USE_MULTITLS +#error This header can be used only if MultiTLS is enabled +#endif + +#ifdef MHD_USE_GNUTLS +struct mhd_TlsGnuConnData; /* forward declaration */ +#endif +#ifdef MHD_USE_OPENSSL +struct mhd_TlsOpenConnData; /* forward declaration */ +#endif + +/** + * The pointer to the underlying TLS backend connection data + */ +struct mhd_TlsMultiConnRoutePtr +{ +#ifdef MHD_USE_GNUTLS + /** + * Pointer to GnuTLS connection-specific data + */ + struct mhd_TlsGnuConnData *gnutls; +#endif +#ifdef MHD_USE_OPENSSL + /** + * Pointer to OpenSSL connection-specific data + */ + struct mhd_TlsOpenConnData *openssl; +#endif +}; + +/** + * The structure with connection-specific MultiTLS data + */ +struct mhd_TlsMultiConnData +{ + /** + * The underlying TLS backend choice + */ + enum mhd_TlsMultiRoute choice; + + /** + * The pointer to the underlying TLS backend connection data + */ + struct mhd_TlsMultiConnRoutePtr data; +}; + +#endif /* ! MHD_TLS_MULTI_CONN_DATA_H */ diff --git a/src/mhd2/tls_multi_daemon_data.h b/src/mhd2/tls_multi_daemon_data.h @@ -0,0 +1,80 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_multi_daemon_data.h + * @brief The definition of MultiTLS daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MULTI_DAEMON_DATA_H +#define MHD_TLS_MULTI_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#include "tls_multi_tls_lib.h" + +#ifndef MHD_USE_MULTITLS +#error This header can be used only if MultiTLS is enabled +#endif + +#ifdef MHD_USE_GNUTLS +struct mhd_TlsGnuDaemonData; /* forward declaration */ +#endif +#ifdef MHD_USE_OPENSSL +struct mhd_TlsOpenDaemonData; /* forward declaration */ +#endif + +/** + * The pointer to the underlying TLS backend daemon data + */ +struct mhd_TlsMultiDaemonRoutePtr +{ +#ifdef MHD_USE_GNUTLS + /** + * Pointer to GnuTLS daemon-specific data + */ + struct mhd_TlsGnuDaemonData *gnutls; +#endif +#ifdef MHD_USE_OPENSSL + /** + * Pointer to OpenSSL daemon-specific data + */ + struct mhd_TlsOpenDaemonData *openssl; +#endif +}; + +/** + * The structure with daemon-specific MultiTLS data + */ +struct mhd_TlsMultiDaemonData +{ + /** + * The underlying TLS backend choice + */ + enum mhd_TlsMultiRoute choice; + + /** + * The pointer to the underlying TLS backend daemon data + */ + struct mhd_TlsMultiDaemonRoutePtr data; +}; + +#endif /* ! MHD_TLS_MULTI_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_multi_funcs.c b/src/mhd2/tls_multi_funcs.c @@ -0,0 +1,617 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_multi_funcs.c + * @brief The implementation of MultiTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_assert.h" +#include "mhd_unreachable.h" + +#include "compat_calloc.h" +#include "sys_malloc.h" + +#include "mhd_arr_num_elems.h" + +#include "tls_multi_tls_lib.h" + +#include "tls_multi_daemon_data.h" +#include "tls_multi_conn_data.h" +#include "tls_multi_funcs.h" + +/* Include all supported TLS backends headers */ +#if defined(MHD_USE_GNUTLS) +# include "tls_gnu_funcs.h" +#endif +#if defined(MHD_USE_OPENSSL) +# include "tls_open_funcs.h" +#endif + +#include "daemon_options.h" + +#include "mhd_public_api.h" +#include "daemon_logger.h" + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# include <stdio.h> /* For TLS debug printing */ +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# define mhd_M_DEBUG_PRINT(msg) \ + do { fprintf (stderr, "## MultiTLS: " msg "\n"); \ + fflush (stderr); } while (0) +# define mhd_M_DEBUG_PRINT1(msg,arg1) \ + do { fprintf (stderr, "## MultiTLS: " msg "\n", arg1); \ + fflush (stderr); } while (0) +#else +# define mhd_M_DEBUG_PRINT(msg) ((void) 0) +# define mhd_M_DEBUG_PRINT1(msg,arg1) ((void) 0) +#endif + + +/* ** Global initialisation / de-initialisation ** */ + +MHD_INTERNAL void +mhd_tls_multi_global_init_once (void) +{ +#if defined(MHD_USE_GNUTLS) + mhd_tls_gnu_global_init_once (); +#endif +#if defined(MHD_USE_OPENSSL) + mhd_tls_open_global_init_once (); +#endif +} + + +MHD_INTERNAL void +mhd_tls_multi_global_deinit (void) +{ + /* Note: the order is reversed to match the initialisation */ +#if defined(MHD_USE_OPENSSL) + mhd_tls_open_global_deinit (); +#endif +#if defined(MHD_USE_GNUTLS) + mhd_tls_gnu_global_deinit (); +#endif +} + + +MHD_INTERNAL void +mhd_tls_multi_global_re_init (void) +{ +#if defined(MHD_USE_GNUTLS) + mhd_tls_gnu_global_re_init (); +#endif +#if defined(MHD_USE_OPENSSL) + mhd_tls_open_global_re_init (); +#endif +} + + +/* ** Daemon initialisation / de-initialisation ** */ + +MHD_INTERNAL MHD_FN_PURE_ bool +mhd_tls_multi_is_edge_trigg_supported (struct DaemonOptions *s) +{ + switch (s->tls) + { + case MHD_TLS_BACKEND_NONE: + mhd_UNREACHABLE (); + return false; + case MHD_TLS_BACKEND_ANY: +#ifdef MHD_USE_GNUTLS + if (mhd_tls_gnu_is_edge_trigg_supported (s) + && mhd_tls_gnu_is_inited_fine ()) + return true; +#endif +#ifdef MHD_USE_OPENSSL + if (mhd_tls_open_is_edge_trigg_supported (s) + && mhd_tls_open_is_inited_fine ()) + return true; +#endif + return false; + case MHD_TLS_BACKEND_GNUTLS: +#ifdef MHD_USE_GNUTLS + /* Ignore "backend inited" status here, + it will be checked on daemon TLS init */ + return mhd_tls_gnu_is_edge_trigg_supported (s); +#endif + break; + case MHD_TLS_BACKEND_OPENSSL: +#ifdef MHD_USE_OPENSSL + /* Ignore "backend inited" status here, + it will be checked on daemon TLS init */ + return mhd_tls_open_is_edge_trigg_supported (s); +#endif + break; + default: + mhd_UNREACHABLE (); + } + return false; +} + + +/** + * Initialise selected TLS backend for the daemon + * @param route the selected TLS backend + * @param d the daemon handle + * @param s the daemon settings + * @param d_tls the daemon's TLS settings, backend-specific data is allocated + * and initialised + * @return #MHD_SC_OK on success (the backend-specific data is allocated), + * error code otherwise + */ +static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (5) mhd_StatusCodeInt +tls_daemon_init_try (enum mhd_TlsMultiRoute route, + struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsMultiDaemonData *restrict d_tls) +{ + mhd_StatusCodeInt res; + +#ifndef MHD_USE_OPENSSL + (void) sk_edge_trigg; /* Unused, mute compiler warning */ +#endif /* ! MHD_USE_OPENSSL */ + + switch (route) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + if (! mhd_tls_gnu_is_inited_fine ()) + return MHD_SC_TLS_BACKEND_UNAVAILABLE; + res = mhd_tls_gnu_daemon_init (d, + sk_edge_trigg, + s, + &(d_tls->data.gnutls)); + if (MHD_SC_OK == res) + { + mhd_M_DEBUG_PRINT ("GnuTLS backend initialised successfully " \ + "for the daemon"); + d_tls->choice = route; + return MHD_SC_OK; + } + mhd_M_DEBUG_PRINT1 ("Failed to initialise GnuTLS backend for " \ + "the daemon, error code: %u", (unsigned) res); + return res; +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + if (! mhd_tls_open_is_inited_fine ()) + return MHD_SC_TLS_BACKEND_UNAVAILABLE; + res = mhd_tls_open_daemon_init (d, + sk_edge_trigg, + s, + &(d_tls->data.openssl)); + if (MHD_SC_OK == res) + { + mhd_M_DEBUG_PRINT ("OpenSSL backend initialised successfully " \ + "for the daemon"); + d_tls->choice = route; + return MHD_SC_OK; + } + mhd_M_DEBUG_PRINT1 ("Failed to initialise OpenSSL backend for " \ + "the daemon, error code: %u", (unsigned) res); + return res; +#endif + case mhd_TLS_MULTI_ROUTE_NONE: + default: + } + mhd_assert (0 && "Impossible value"); + mhd_UNREACHABLE (); + return MHD_SC_INTERNAL_ERROR; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (4) mhd_StatusCodeInt +mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsMultiDaemonData **restrict p_d_tls) +{ + mhd_StatusCodeInt res; + struct mhd_TlsMultiDaemonData *restrict d_tls; + d_tls = (struct mhd_TlsMultiDaemonData *) + mhd_calloc (1, + sizeof (struct mhd_TlsMultiDaemonData)); + if (NULL == d_tls) + return MHD_SC_DAEMON_MALLOC_FAILURE; + + res = MHD_SC_INTERNAL_ERROR; /* Mute compiler warning, the value should not be used */ + switch (s->tls) + { + case MHD_TLS_BACKEND_ANY: + if (1) + { + size_t i; + enum mhd_TlsMultiRoute backends[] = { +#ifdef MHD_USE_GNUTLS + mhd_TLS_MULTI_ROUTE_GNU, +#endif +#ifdef MHD_USE_OPENSSL + mhd_TLS_MULTI_ROUTE_OPEN, +#endif + mhd_TLS_MULTI_ROUTE_NONE /* Not used */ + }; + /* Try backends one-by-one */ + for (i = 0; i < mhd_ARR_NUM_ELEMS (backends) - 1; ++i) + { + res = tls_daemon_init_try (backends[i], + d, + sk_edge_trigg, + s, + d_tls); + if (MHD_SC_OK == res) + break; + } + } + break; +#ifdef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: + mhd_assert (mhd_tls_gnu_is_inited_fine ()); /* Must be checked earlier */ + res = tls_daemon_init_try (mhd_TLS_MULTI_ROUTE_GNU, + d, + sk_edge_trigg, + s, + d_tls); + break; +#endif /* MHD_USE_GNUTLS */ +#ifdef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: + mhd_assert (mhd_tls_open_is_inited_fine ()); /* Must be checked earlier */ + res = tls_daemon_init_try (mhd_TLS_MULTI_ROUTE_OPEN, + d, + sk_edge_trigg, + s, + d_tls); +#endif /* MHD_USE_OPENSSL */ + break; + +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case MHD_TLS_BACKEND_NONE: + default: + break; + mhd_assert (0 && "Should not be reachable"); + mhd_UNREACHABLE (); + res = MHD_SC_TLS_BACKEND_UNSUPPORTED; + } + mhd_assert (NULL != d_tls); + if (MHD_SC_OK == res) + { + *p_d_tls = d_tls; + return MHD_SC_OK; + } + free (d_tls); + return res; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_tls_multi_daemon_deinit (struct mhd_TlsMultiDaemonData *restrict d_tls) +{ + switch (d_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + mhd_tls_gnu_daemon_deinit (d_tls->data.gnutls); + break; +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + mhd_tls_open_daemon_deinit (d_tls->data.openssl); + break; +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + free (d_tls); +} + + +/* ** Connection initialisation / de-initialisation ** */ + +MHD_INTERNAL size_t +mhd_tls_multi_conn_get_tls_size (struct mhd_TlsMultiDaemonData *restrict d_tls) +{ + size_t data_size; + + data_size = sizeof(struct mhd_TlsMultiConnData); + switch (d_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + data_size += mhd_tls_gnu_conn_get_tls_size (d_tls->data.gnutls); + break; +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + data_size += mhd_tls_open_conn_get_tls_size (d_tls->data.openssl); + break; +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + + return data_size; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) bool +mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsMultiConnData *restrict c_tls) +{ + c_tls->choice = d_tls->choice; + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + /* Assume the same alignment requirements for both structures */ + c_tls->data.gnutls = (struct mhd_TlsGnuConnData *) (c_tls + 1); + return mhd_tls_gnu_conn_init (d_tls->data.gnutls, + sk, + c_tls->data.gnutls); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + /* Assume the same alignment requirements for both structures */ + c_tls->data.openssl = (struct mhd_TlsOpenConnData *) (c_tls + 1); + return mhd_tls_open_conn_init (d_tls->data.openssl, + sk, + c_tls->data.openssl); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + + return false; +} + + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_multi_conn_deinit (struct mhd_TlsMultiConnData *restrict c_tls) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + mhd_tls_gnu_conn_deinit (c_tls->data.gnutls); + break; +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + mhd_tls_open_conn_deinit (c_tls->data.openssl); + break; +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } +} + + +/* ** TLS connection establishing ** */ + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_multi_conn_handshake (struct mhd_TlsMultiConnData *restrict c_tls) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + return mhd_tls_gnu_conn_handshake (c_tls->data.gnutls); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + return mhd_tls_open_conn_handshake (c_tls->data.openssl); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + return mhd_TLS_PROCED_FAILED; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_multi_conn_shutdown (struct mhd_TlsMultiConnData *restrict c_tls) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + return mhd_tls_gnu_conn_shutdown (c_tls->data.gnutls); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + return mhd_tls_open_conn_shutdown (c_tls->data.openssl); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + return mhd_TLS_PROCED_FAILED; +} + + +/* ** Data receiving and sending ** */ + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_multi_conn_recv (struct mhd_TlsMultiConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + return mhd_tls_gnu_conn_recv (c_tls->data.gnutls, + buf_size, + buf, + received); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + return mhd_tls_open_conn_recv (c_tls->data.openssl, + buf_size, + buf, + received); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + return mhd_SOCKET_ERR_INTERNAL; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_tls_multi_conn_has_data_in (struct mhd_TlsMultiConnData *restrict c_tls) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + return mhd_tls_gnu_conn_has_data_in (c_tls->data.gnutls); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + return mhd_tls_open_conn_has_data_in (c_tls->data.openssl); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + return false; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_multi_conn_send (struct mhd_TlsMultiConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +{ + switch (c_tls->choice) + { +#ifdef MHD_USE_GNUTLS + case mhd_TLS_MULTI_ROUTE_GNU: + return mhd_tls_gnu_conn_send (c_tls->data.gnutls, + buf_size, + buf, + sent); +#endif +#ifdef MHD_USE_OPENSSL + case mhd_TLS_MULTI_ROUTE_OPEN: + return mhd_tls_open_conn_send (c_tls->data.openssl, + buf_size, + buf, + sent); +#endif +#ifndef MHD_USE_GNUTLS + case MHD_TLS_BACKEND_GNUTLS: +#endif /* ! MHD_USE_GNUTLS */ +#ifndef MHD_USE_OPENSSL + case MHD_TLS_BACKEND_OPENSSL: +#endif /* ! MHD_USE_OPENSSL */ + case mhd_TLS_MULTI_ROUTE_NONE: + default: + mhd_UNREACHABLE (); + } + return mhd_SOCKET_ERR_INTERNAL; +} diff --git a/src/mhd2/tls_multi_funcs.h b/src/mhd2/tls_multi_funcs.h @@ -0,0 +1,231 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_multi_funcs.h + * @brief The declarations of MultiTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MULTI_FUNCS_H +#define MHD_TLS_MULTI_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_tls_choice.h" + +#ifndef MHD_USE_MULTITLS +#error This header can be used only when MultiTLS is enabled +#endif + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_status_code_int.h" + +#include "mhd_tls_enums.h" +#include "mhd_socket_error.h" + +/** + * The structure with daemon-specific MultiTLS data + */ +struct mhd_TlsMultiDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific MultiTLS data + */ +struct mhd_TlsMultiConnData; /* Forward declaration */ + + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Globally initialise MultiTLS backend + */ + +/** + * Perform one-time global initialisation of MultiTLS backend + */ +MHD_INTERNAL void +mhd_tls_multi_global_init_once (void); + +/** + * Perform de-initialisation of MultiTLS backend + */ +MHD_INTERNAL void +mhd_tls_multi_global_deinit (void); + +/** + * Perform re-initialisation of MultiTLS backend + */ +MHD_INTERNAL void +mhd_tls_multi_global_re_init (void); + + +/* ** Daemon initialisation / de-initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Check whether MultiTLS backend supports edge-triggered sockets polling + * @param s the daemon settings + * @return 'true' if the backend supports edge-triggered sockets polling, + * 'false' if edge-triggered sockets polling cannot be used + */ +MHD_INTERNAL bool +mhd_tls_multi_is_edge_trigg_supported (struct DaemonOptions *s) +MHD_FN_PURE_; + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param sk_edge_trigg if 'true' then sockets polling uses edge-triggering + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +MHD_INTERNAL mhd_StatusCodeInt +mhd_tls_multi_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsMultiDaemonData **restrict p_d_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4); + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +MHD_INTERNAL void +mhd_tls_multi_daemon_deinit (struct mhd_TlsMultiDaemonData *restrict d_tls) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + + +/* ** Connection initialisation / de-initialisation ** */ + +struct mhd_ConnSocket; /* Forward declaration */ + +/** + * Get size size of the connection's TLS settings + */ +MHD_INTERNAL size_t +mhd_tls_multi_conn_get_tls_size (struct mhd_TlsMultiDaemonData *restrict d_tls); + +/** + * Initialise connection TLS settings + * @param d_tls the daemon TLS settings + * @param sk data about the socket for the connection + * @param[out] c_tls the pointer to the allocated space for + * the connection TLS settings + * @return 'true' on success, + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_multi_conn_init (const struct mhd_TlsMultiDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsMultiConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL void +mhd_tls_multi_conn_deinit (struct mhd_TlsMultiConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + + +/* ** TLS connection establishing ** */ + +/** + * Perform TLS handshake + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_multi_conn_handshake (struct mhd_TlsMultiConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + +/** + * Perform shutdown of TLS layer + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_multi_conn_shutdown (struct mhd_TlsMultiConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + + +/* ** Data sending and receiving over TLS connection ** */ + +/** + * Receive the data from the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_multi_conn_recv (struct mhd_TlsMultiConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +/** + * Check whether any incoming data is pending in the TLS buffers + * + * @param c_tls the connection TLS handle + * @return 'true' if any incoming remote data is already pending (the TLS recv() + * call can be performed), + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_multi_conn_has_data_in (struct mhd_TlsMultiConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + +/** + * Send data to the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buffer_size the size of the @a buffer (in bytes) + * @param buffer content of the buffer to send + * @param[out] sent the pointer to get amount of actually sent bytes + * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets + * the sent size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_multi_conn_send (struct mhd_TlsMultiConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +#endif /* ! MHD_TLS_MULTI_FUNCS_H */ diff --git a/src/mhd2/tls_multi_tls_lib.h b/src/mhd2/tls_multi_tls_lib.h @@ -0,0 +1,64 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_multi_tls_lib.h + * @brief The header for virtual "MultiTLS" backend + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_MULTI_TLS_LIB_H +#define MHD_TLS_MULTI_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#include "mhd_tls_choice.h" + +#ifndef MHD_USE_MULTITLS +#error This header can be used only when MultiTLS is enabled +#endif + +/** + * The underlying TLS backend choice + */ +enum mhd_TlsMultiRoute +{ + /** + * No TLS backend. + * Invalid value if TLS is used. + */ + mhd_TLS_MULTI_ROUTE_NONE = 0 +#ifdef MHD_USE_GNUTLS + , + /** + * Use GnuTLS backend + */ + mhd_TLS_MULTI_ROUTE_GNU +#endif +#ifdef MHD_USE_OPENSSL + , + /** + * Use OpenSSL backend + */ + mhd_TLS_MULTI_ROUTE_OPEN +#endif +}; + +#endif /* ! MHD_TLS_MULTI_TLS_LIB_H */ diff --git a/src/mhd2/tls_open_conn_data.h b/src/mhd2/tls_open_conn_data.h @@ -0,0 +1,76 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_open_conn_data.h + * @brief The definition of OpenSSL daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_CONN_DATA_H +#define MHD_TLS_OPEN_CONN_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_open_tls_lib.h" + +#include "sys_bool_type.h" + +#ifndef NDEBUG +struct mhd_TlsOpenConnDebug +{ + unsigned int is_inited; + unsigned int is_tls_handshake_completed; + unsigned int is_failed; +}; +#endif /* ! NDEBUG */ + +/** + * The structure with connection-specific GnuTLS data + */ +struct mhd_TlsOpenConnData +{ + /** + * OpenSSL session data + */ + SSL *sess; + + /** + * 'true' if sent TLS shutdown "alert" + */ + bool shut_tls_wr_sent; + + /** + * 'true' if received EOF (the remote side initiated shutting down) + */ + bool shut_tls_wr_received; +#ifndef NDEBUG + /** + * Debugging data + */ + struct mhd_TlsOpenConnDebug dbg; +#endif /* ! NDEBUG */ +}; + +#endif /* ! MHD_TLS_OPEN_CONN_DATA_H */ diff --git a/src/mhd2/tls_open_daemon_data.h b/src/mhd2/tls_open_daemon_data.h @@ -0,0 +1,56 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_open_daemon_data.h + * @brief The definition of OpenSSL daemon-specific data structures + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_DAEMON_DATA_H +#define MHD_TLS_OPEN_DAEMON_DATA_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +#include "tls_open_tls_lib.h" + +/** + * The structure with daemon-specific GnuTLS data + */ +struct mhd_TlsOpenDaemonData +{ + /** + * The library context. + * Thanks to the library context, daemons are isolated from each other and + * from any application context. + */ + OSSL_LIB_CTX *libctx; + + /** + * The server context + */ + SSL_CTX *ctx; +}; + +#endif /* ! MHD_TLS_OPEN_DAEMON_DATA_H */ diff --git a/src/mhd2/tls_open_funcs.c b/src/mhd2/tls_open_funcs.c @@ -0,0 +1,1188 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_open_funcs.c + * @brief The implementation of GnuTLS wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_assert.h" +#include "mhd_unreachable.h" + +#include <string.h> + +#include "compat_calloc.h" +#include "sys_malloc.h" + +#include "mhd_conn_socket.h" + +#include "tls_open_tls_lib.h" + +#include "tls_open_daemon_data.h" +#include "tls_open_conn_data.h" +#include "tls_open_funcs.h" + +#include "daemon_options.h" + +#include "daemon_logger.h" + +#include "mhd_public_api.h" + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES +# include <stdio.h> /* For TLS debug printing */ +#endif + +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + +static MHD_FN_PAR_NONNULL_ (1) int +mhd_tls_open_dbg_print_errs (const char *msg, + size_t msg_len, + void *cls) +{ + int ret; + int print_size = (int) msg_len; + + (void) cls; /* Not used */ + + if ((print_size < 0) || + (msg_len != (unsigned int) print_size)) + print_size = (int) ((~((unsigned int) 0u)) >> 1); + + ret = fprintf (stderr, + "## OpenSSL error: %.*s\n", + print_size, msg); + (void) fflush (stderr); + return ret; +} + + +# define mhd_DBG_PRINT_TLS_ERRS() \ + ERR_print_errors_cb (&mhd_tls_open_dbg_print_errs, NULL) +#else +# define mhd_DBG_PRINT_TLS_ERRS() ERR_clear_error () +#endif + +/* ** Global initialisation / de-initialisation ** */ + +static bool openssl_lib_inited = false; + +MHD_INTERNAL void +mhd_tls_open_global_init_once (void) +{ + const unsigned long ver_num = OpenSSL_version_num (); + /* Make sure that used shared OpenSSL library has least the same version as + MHD was configured for. Fail if the version is earlier. */ + openssl_lib_inited = ((0x900000UL < ver_num) /* Versions before 3.0 */ + && (OPENSSL_VERSION_NUMBER <= ver_num)); + + /* The call of OPENSSL_init_ssl() typically not needed, but it won't hurt + if library was initialised automatically. + In some exotic situations automatic initialisation could fail, and + this call would make sure that the library is initialised before used. */ + openssl_lib_inited = openssl_lib_inited + && (0 < OPENSSL_init_ssl (0, NULL)); +} + + +MHD_INTERNAL MHD_FN_PURE_ bool +mhd_tls_open_is_inited_fine (void) +{ + return openssl_lib_inited; +} + + +/* ** Daemon initialisation / de-initialisation ** */ + +/** + * Check application-provided daemon TLS settings + * @param d the daemon handle + * @param sk_edge_trigg the sockets polling uses edge-triggering + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +check_app_tls_sessings (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s) +{ + mhd_assert (MHD_TLS_BACKEND_NONE != s->tls); + mhd_assert ((MHD_TLS_BACKEND_OPENSSL == s->tls) || \ + (MHD_TLS_BACKEND_ANY == s->tls)); + if (NULL == s->tls_cert_key.v_mem_cert) + { + mhd_LOG_MSG (d, MHD_SC_TLS_CONF_BAD_CERT, \ + "No valid TLS certificate is provided"); + return MHD_SC_TLS_CONF_BAD_CERT; + } + mhd_assert (NULL != s->tls_cert_key.v_mem_key); + + if (sk_edge_trigg) + { + mhd_LOG_MSG (d, MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS, \ + "Edge-triggered sockets polling cannot be used " + "with OpenSSL backend"); + return MHD_SC_TLS_BACKEND_DAEMON_INCOMPATIBLE_SETTINGS; + } + + return MHD_SC_OK; +} + + +/* Helper to prevent password prompts in terminal */ +static int +null_passwd_cb (char *buf, + int size, + int rwflag, + void *cls) +{ + (void) buf; (void) size; (void) rwflag; (void) cls; /* Unused */ +#ifdef mhd_USE_TLS_DEBUG_MESSAGES + fprintf (stderr, "## OpenSSL: the NULL passphrase callback is called\n"); + fflush (stderr); +#endif + return 0; +} + + +/** + * Initialise OpenSSL library context + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_lib_ctx (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + bool fallback_config; + bool prevent_fallbacks; + char *conf_filename; + d_tls->libctx = OSSL_LIB_CTX_new (); + + (void) s; // TODO: support app-defined name for TLS backend profile + + if (NULL == d_tls->libctx) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create TLS library context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + prevent_fallbacks = false; +#ifdef mhd_TLS_OPEN_HAS_CONF_DIAG + prevent_fallbacks = prevent_fallbacks || + (0 != OSSL_LIB_CTX_get_conf_diagnostics (d_tls->libctx)); +#endif + + fallback_config = false; + ERR_clear_error (); + + conf_filename = CONF_get1_default_config_file (); + if (NULL == conf_filename) + mhd_DBG_PRINT_TLS_ERRS (); + else + { + bool libctx_inited; + CONF *conf; + + libctx_inited = false; + conf = NCONF_new_ex (d_tls->libctx, + NULL); + if (NULL == conf) + mhd_DBG_PRINT_TLS_ERRS (); + else + { + if (0 >= NCONF_load (conf, + conf_filename, + NULL)) + { + unsigned long err; + + err = ERR_peek_last_error (); + mhd_DBG_PRINT_TLS_ERRS (); + libctx_inited = true; /* Nothing to initialise */ + + if ((ERR_LIB_CONF != ERR_GET_LIB (err)) || + (CONF_R_NO_SUCH_FILE != ERR_GET_REASON (err))) + { + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Error in TLS library configuration " + "file '%s'"), \ + conf_filename); + } + } + else /* NCONF_load() succeed */ + { + (void) s; // TODO: support app-defined name for TLS backend profile + + if (! libctx_inited) + { + if (NULL != NCONF_get_section (conf, + "libmicrohttpd")) + { + if (0 < + CONF_modules_load (conf, + "libmicrohttpd", + 0)) + libctx_inited = true; + else + { + mhd_DBG_PRINT_TLS_ERRS (); + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Failed to load configuration file " \ + "section [%s]"), \ + "libmicrohttpd"); + + libctx_inited = + (0 < CONF_modules_load (conf, + "libmicrohttpd", + CONF_MFLAGS_IGNORE_ERRORS)); + if (! libctx_inited) + mhd_DBG_PRINT_TLS_ERRS (); + } + } + } + if (! libctx_inited) + { + if (0 < + CONF_modules_load (conf, + NULL, + 0)) + libctx_inited = true; + else + { + mhd_DBG_PRINT_TLS_ERRS (); + fallback_config = true; + mhd_LOG_PRINT (d, MHD_SC_TLS_LIB_CONF_WARNING, \ + mhd_LOG_FMT ("Failed to load configuration file " \ + "default section")); + + libctx_inited = + (0 < CONF_modules_load (conf, + NULL, + CONF_MFLAGS_IGNORE_ERRORS)); + if (! libctx_inited) + mhd_DBG_PRINT_TLS_ERRS (); + } + } +#ifdef mhd_TLS_OPEN_HAS_CONF_DIAG + if (fallback_config && libctx_inited && ! prevent_fallbacks) + prevent_fallbacks = + (0 != OSSL_LIB_CTX_get_conf_diagnostics (d_tls->libctx)); +#endif /* mhd_TLS_OPEN_HAS_CONF_DIAG */ + } + NCONF_free (conf); + } + OPENSSL_free (conf_filename); + + if (fallback_config && prevent_fallbacks) + libctx_inited = true; + + if (libctx_inited) + { + return MHD_SC_OK; /* Success exit point */ + } + } + + OSSL_LIB_CTX_free (d_tls->libctx); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS library context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; +} + + +/** + * De-initialise OpenSSL library context + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_lib_ctx (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->libctx); + OSSL_LIB_CTX_free (d_tls->libctx); +} + + +static const unsigned char alpn_codes_list[] = { +#if 0 /* Disabled code */ + 2u, 'h', '3' /* Registered value for HTTP/3 */ + , + 2u, 'h', '2' /* Registered value for HTTP/2 over TLS */ + , +#endif /* Disabled code */ + 8u, 'h', 't', 't', 'p', '/', '1', '.', '1' /* Registered value for HTTP/1.1 */ + , + 8u, 'h', 't', 't', 'p', '/', '1', '.', '0' /* Registered value for HTTP/1.0 */ +}; + +#ifndef OPENSSL_NO_NEXTPROTONEG +/** + * Provide the list of supported protocols for NPN extension + * @param sess the TLS session (ignored) + * @param[out] out the pointer to get the location of the data + * @param[out] outlen the size of the data provided + * @param cls the closure (ignored) + * @return always SSL_TLSEXT_ERR_OK + */ +static int +get_npn_list (SSL *sess, + const unsigned char **out, + unsigned int *outlen, + void *cls) +{ + (void) sess; (void) cls; /* Unused */ + *out = alpn_codes_list; + *outlen = sizeof(alpn_codes_list); + return SSL_TLSEXT_ERR_OK; +} + + +#endif /* ! OPENSSL_NO_NEXTPROTONEG */ + +/** + * Select protocol from the provided list for ALPN extension + * @param sess the TLS session (ignored) + * @param[out] out the pointer to get the location of selected protocol value + * @param[out] outlen the size of the selected protocol value + * @param in the list of protocols values provided by the client + * @param inlen the size of the list of protocols values provided by the client + * @param cls the closure (ignored) + * @return SSL_TLSEXT_ERR_OK if matching protocol found and selected, + * SSL_TLSEXT_ERR_ALERT_FATAL otherwise + */ +static int +select_alpn_prot (SSL *sess, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *cls) +{ + (void) sess; (void) cls; /* Unused */ + if (OPENSSL_NPN_NEGOTIATED == + SSL_select_next_proto (mhd_DROP_CONST (out), + outlen, + in, + inlen, + alpn_codes_list, + sizeof(alpn_codes_list))) + return SSL_TLSEXT_ERR_OK; /* Success */ + + return SSL_TLSEXT_ERR_ALERT_FATAL; /* Failure */ +} + + +/** + * Initialise TLS server context + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_ctx (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + uint64_t ctx_opts; + +#ifndef HAVE_LOG_FUNCTIONALITY + (void) d; /* Mute compiler warning */ +#endif + (void) s; // TODO: support configuration options + + mhd_assert (NULL != d_tls->libctx); + + ERR_clear_error (); + + d_tls->ctx = SSL_CTX_new_ex (d_tls->libctx, + NULL, + TLS_server_method ()); + if (NULL == d_tls->ctx) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to initialise TLS server context"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + /* Enable some safe and useful workarounds */ + ctx_opts = SSL_OP_SAFARI_ECDHE_ECDSA_BUG | SSL_OP_TLSEXT_PADDING; + + // TODO: add configuration option + // ctx_opts |= SSL_OP_CIPHER_SERVER_PREFERENCE; + +#ifndef OPENSSL_NO_KTLS + /* Enable kernel TLS */ // TODO: add configuration option + ctx_opts |= SSL_OP_ENABLE_KTLS; +# ifdef SSL_OP_ENABLE_KTLS_TX_ZEROCOPY_SENDFILE + ctx_opts |= SSL_OP_ENABLE_KTLS_TX_ZEROCOPY_SENDFILE; +# endif +#endif + + /* HTTP defines strict framing for the client-side data, + no risk of attack on server on unexpected connection interruption */ + /* ctx_opts |= SSL_OP_IGNORE_UNEXPECTED_EOF; */ // TODO: recheck + + /* There is no reason to use re-negotiation with HTTP */ + ctx_opts |= SSL_OP_NO_RENEGOTIATION; + + /* Do not use session resumption for now */ + ctx_opts |= SSL_OP_NO_TICKET; + + (void) SSL_CTX_set_options (d_tls->ctx, + ctx_opts); + + /* Prevent interactive password prompts */ + SSL_CTX_set_default_passwd_cb (d_tls->ctx, + &null_passwd_cb); + + // TODO: make the setting configurable + /* SSL_CTX_set_security_level (d_tls->ctx, 0); */ + + /* recv()- and send()-related options */ + (void) SSL_CTX_set_mode (d_tls->ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE + | SSL_MODE_AUTO_RETRY); + (void) SSL_CTX_clear_mode (d_tls->ctx, + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER + | SSL_MODE_ASYNC); + + SSL_CTX_set_read_ahead (d_tls->ctx, + ! 0); + + /* ALPN and NPN */ + // TODO: use daemon option to disable them + SSL_CTX_set_alpn_select_cb (d_tls->ctx, + &select_alpn_prot, + NULL); +#ifndef OPENSSL_NO_NEXTPROTONEG + SSL_CTX_set_next_protos_advertised_cb (d_tls->ctx, + &get_npn_list, + NULL); +#endif /* ! OPENSSL_NO_NEXTPROTONEG */ + + return MHD_SC_OK; +} + + +/** + * De-initialise TLS server context + * @param d_tls the daemon TLS settings + */ +static MHD_FN_PAR_NONNULL_ALL_ void +daemon_deinit_ctx (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls->ctx); + SSL_CTX_free (d_tls->ctx); +} + + +/** + * Load the certificates chain from the OpenSSL BIO + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_load_certs_chain_obio (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + BIO *restrict bio) +{ + enum MHD_StatusCode ret; + X509 *cert; + + ret = MHD_SC_OK; + + /* The certificate object must be pre-allocated to associate it with + * the lib context */ + cert = X509_new_ex (d_tls->libctx, + NULL); + if (NULL == cert) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create new certificate object"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + if (NULL == PEM_read_bio_X509_AUX (bio, + &cert, + &null_passwd_cb, + NULL)) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to process the certificate"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + { + if (0 >= SSL_CTX_use_certificate (d_tls->ctx, + cert)) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to set the certificate"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + { + if (0 != ERR_peek_error ()) + mhd_DBG_PRINT_TLS_ERRS (); + } + } + /* Free certificate: if it was successfully read, it has been "copied" to CTX */ + X509_free (cert); + if (MHD_SC_OK != ret) + return ret; + + do + { + X509 *inter_ca; /* Certifying certificate */ + inter_ca = X509_new_ex (d_tls->libctx, + NULL); + if (NULL == inter_ca) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to create new chain certificate object"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + if (NULL == PEM_read_bio_X509 (bio, + &inter_ca, + &null_passwd_cb, + NULL)) + { + unsigned long err; + err = ERR_peek_last_error (); + if ((ERR_LIB_PEM == ERR_GET_LIB (err)) && + (PEM_R_NO_START_LINE == ERR_GET_REASON (err))) + { + /* End of data */ + ERR_clear_error (); + X509_free (inter_ca); /* Empty, not needed */ + + mhd_assert (MHD_SC_OK == ret); + return MHD_SC_OK; /* Success exit point */ + } + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to load next object in the certificates " \ + "chain"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + else + { + if (SSL_CTX_add0_chain_cert (d_tls->ctx, + inter_ca)) + { + /* Success, do not free the certificate as + * function '_add0_' was used to add it. */ + /* Read the next certificate in the chain. */ + continue; + } + + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to add the new certificate object " + "to the chain"); + ret = MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + X509_free (inter_ca); /* Failed, the object is not needed */ + mhd_assert (MHD_SC_OK != ret); + } while (MHD_SC_OK == ret); + mhd_assert (MHD_SC_OK != ret); + return ret; +} + + +/** + * Load the certificates chain based on application-provided settings + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_load_certs_chain (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + BIO *m_bio; + + mhd_assert (NULL != d_tls->libctx); + mhd_assert (NULL != d_tls->ctx); + + ERR_clear_error (); + + m_bio = BIO_new_mem_buf (s->tls_cert_key.v_mem_cert, + -1); + if (NULL == m_bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + return MHD_SC_DAEMON_MALLOC_FAILURE; + } + ret = daemon_load_certs_chain_obio (d, + d_tls, + m_bio); + BIO_free (m_bio); + return ret; +} + + +/** + * Initialise TLS certificate + * The function loads the certificate chain and the private key. + * @param d the daemon handle + * @param d_tls the daemon TLS settings + * @param s the application-provided settings + * @return #MHD_SC_OK on success, + * error code otherwise + */ +static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode +daemon_init_cert (struct MHD_Daemon *restrict d, + struct mhd_TlsOpenDaemonData *restrict d_tls, + struct DaemonOptions *restrict s) +{ + enum MHD_StatusCode ret; + BIO *m_bio; + EVP_PKEY *pr_key; + int res_i; + long res_l; + + mhd_assert (NULL != d_tls->libctx); + mhd_assert (NULL != d_tls->ctx); + + ERR_clear_error (); + + ret = daemon_load_certs_chain (d, + d_tls, + s); + if (MHD_SC_OK != ret) + return ret; + + /* Check and cache the certificates chain. + This also prevents automatic chain re-building for each session. */ + res_l = + SSL_CTX_build_cert_chain ( + d_tls->ctx, + SSL_BUILD_CHAIN_FLAG_CHECK /* Use only certificates in the chain */ + | SSL_BUILD_CHAIN_FLAG_UNTRUSTED /* Intermediate CA certs does not need to be trusted */ + | SSL_BUILD_CHAIN_FLAG_NO_ROOT /* The root CA should not be added to the chain */ + | SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR /* Allow the root CA to be not trusted */ + ); + if (0 >= res_l) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed rebuild certificate chain"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + if (2 == res_l) + mhd_DBG_PRINT_TLS_ERRS (); + + m_bio = BIO_new_mem_buf (s->tls_cert_key.v_mem_key, + -1); + if (NULL == m_bio) + { + mhd_DBG_PRINT_TLS_ERRS (); + return MHD_SC_DAEMON_MALLOC_FAILURE; + } + pr_key = + PEM_read_bio_PrivateKey_ex (m_bio, + NULL, + NULL == s->tls_cert_key.v_mem_pass ? + &null_passwd_cb : NULL, + mhd_DROP_CONST (s->tls_cert_key.v_mem_pass), + d_tls->libctx, + NULL); + BIO_free (m_bio); + if (NULL == pr_key) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to read the private key"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + res_i = SSL_CTX_use_PrivateKey (d_tls->ctx, + pr_key); + EVP_PKEY_free (pr_key); /* The key has been "copied" or failed */ + if (1 != res_i) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "Failed to set the private key"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + /* This actually RE-checks the key. + The key should be already checked automatically when it was set after + setting the certificate. */ + if (1 != SSL_CTX_check_private_key (d_tls->ctx)) + { + mhd_DBG_PRINT_TLS_ERRS (); + mhd_LOG_MSG (d, MHD_SC_TLS_DAEMON_INIT_FAILED, \ + "The private key does not match the certificate"); + return MHD_SC_TLS_DAEMON_INIT_FAILED; + } + + return MHD_SC_OK; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (4) mhd_StatusCodeInt +mhd_tls_open_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsOpenDaemonData **restrict p_d_tls) +{ + mhd_StatusCodeInt res; + struct mhd_TlsOpenDaemonData *restrict d_tls; + + /* Successful initialisation must be checked earlier */ + mhd_assert (openssl_lib_inited); + + res = check_app_tls_sessings (d, sk_edge_trigg, s); + if (MHD_SC_OK != res) + return res; + + d_tls = (struct mhd_TlsOpenDaemonData *) + mhd_calloc (1, sizeof (struct mhd_TlsOpenDaemonData)); + *p_d_tls = d_tls; + if (NULL == d_tls) + return MHD_SC_DAEMON_MALLOC_FAILURE; + + res = daemon_init_lib_ctx (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_ctx (d, + d_tls, + s); + if (MHD_SC_OK == res) + { + res = daemon_init_cert (d, + d_tls, + s); + if (MHD_SC_OK == res) + return MHD_SC_OK; /* Success exit point */ + + /* Below is a clean-up code path */ + daemon_deinit_ctx (d_tls); + } + daemon_deinit_lib_ctx (d_tls); + } + free (d_tls); + *p_d_tls = NULL; + mhd_assert (MHD_SC_OK != res); + return res; /* Failure exit point */ +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_INOUT_ (1) void +mhd_tls_open_daemon_deinit (struct mhd_TlsOpenDaemonData *restrict d_tls) +{ + mhd_assert (NULL != d_tls); + daemon_deinit_ctx (d_tls); + daemon_deinit_lib_ctx (d_tls); + free (d_tls); +} + + +/* ** Connection initialisation / de-initialisation ** */ + +MHD_INTERNAL size_t +mhd_tls_open_conn_get_tls_size_v (void) +{ + return sizeof (struct mhd_TlsOpenConnData); +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_ (3) bool +mhd_tls_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsOpenConnData *restrict c_tls) +{ + int fd; + + ERR_clear_error (); + + fd = (int) sk->fd; + if (sk->fd != (MHD_Socket) fd) + return false; /* OpenSSL docs clam that it should not be possible */ + + c_tls->sess = SSL_new (d_tls->ctx); + + if (NULL == c_tls->sess) + { + mhd_DBG_PRINT_TLS_ERRS (); + return false; + } + + if (0 < SSL_set_fd (c_tls->sess, fd)) + { + SSL_set_accept_state (c_tls->sess); /* Force server mode */ + +#ifndef NDEBUG + c_tls->dbg.is_inited = true; +#endif + return true; /* Success exit point */ + } + + SSL_free (c_tls->sess); + c_tls->sess = NULL; + return false; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_tls_open_conn_deinit (struct mhd_TlsOpenConnData *restrict c_tls) +{ + mhd_assert (NULL != c_tls->sess); + mhd_assert (c_tls->dbg.is_inited); + SSL_free (c_tls->sess); +} + + +/* ** TLS connection establishing ** */ + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_open_conn_handshake (struct mhd_TlsOpenConnData *restrict c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (! c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->shut_tls_wr_received); + mhd_assert (! c_tls->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_do_handshake (c_tls->sess); + + if (1 == res) + { +#ifndef NDEBUG + c_tls->dbg.is_tls_handshake_completed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + } + + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "recv-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_SEND_MORE_NEEDED; + case SSL_ERROR_WANT_WRITE: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "send-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never sent. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_RECV_MORE_NEEDED; + case SSL_ERROR_NONE: + mhd_assert (0 && "This should not be possible"); + mhd_UNREACHABLE (); + break; + default: /* Handled with all other errors below */ + break; + } + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_FAILED; +} + + +MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ +enum mhd_TlsProcedureResult +mhd_tls_open_conn_shutdown (struct mhd_TlsOpenConnData *restrict c_tls) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_shutdown (c_tls->sess); + + if (1 == res) + { + c_tls->shut_tls_wr_sent = true; + c_tls->shut_tls_wr_received = true; + return mhd_TLS_PROCED_SUCCESS; /* Success exit point */ + } + + /* The OpenSSL documentation contradicts itself: there are two mutually + exclusive statements on a single page. + * https://docs.openssl.org/master/man3/SSL_shutdown/#shutdown-lifecycle + indicates that for nonblocking socket ZERO could be returned when + "close_notify" is GOING to be sent, but NOT sent yet. + It also suggests to CALL SSL_get_error(3) when ZERO is returned. + * https://docs.openssl.org/master/man3/SSL_shutdown/#return-values + indicates ZERO is returned ONLY when "close_notify" HAS BEEN sent. + It also suggests to NOT CALL SSL_get_error(3) when ZERO is returned. + */ + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "recv-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_SEND_MORE_NEEDED; + case SSL_ERROR_WANT_WRITE: + c_tls->shut_tls_wr_sent = true; + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "send-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never sent. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_TLS_PROCED_RECV_MORE_NEEDED; + case SSL_ERROR_NONE: + mhd_assert (res != 0 && "Should not be possible"); + c_tls->shut_tls_wr_sent = true; + return mhd_TLS_PROCED_RECV_INTERRUPTED; + default: /* Handled with all other errors below */ + break; + } + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_TLS_PROCED_FAILED; +} + + +/* ** Data receiving and sending ** */ + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_open_conn_recv (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_read_ex (c_tls->sess, + buf, + buf_size, + received); + if (1 == res) + { + mhd_assert (0 != *received); + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + mhd_assert (0 == res); + *received = 0; + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_ZERO_RETURN: /* Not an error */ + c_tls->shut_tls_wr_received = true; + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + case SSL_ERROR_WANT_READ: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "recv-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never processed. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_SOCKET_ERR_AGAIN; + case SSL_ERROR_NONE: + mhd_assert (0 && "Should not be possible"); + break; + case SSL_ERROR_WANT_WRITE: + mhd_assert (0 && "Should not be possible as re-handshakes are disallowed"); + break; + case SSL_ERROR_SYSCALL: + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_CONN_BROKEN; + case SSL_ERROR_SSL: + default: + break; + } + /* Treat all other kinds of errors as hard errors */ + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_tls_open_conn_has_data_in (struct mhd_TlsOpenConnData *restrict c_tls) +{ + return 0 != SSL_pending (c_tls->sess); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum mhd_SocketError +mhd_tls_open_conn_send (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +{ + int res; + + mhd_assert (c_tls->dbg.is_inited); + mhd_assert (c_tls->dbg.is_tls_handshake_completed); + mhd_assert (! c_tls->shut_tls_wr_sent); + mhd_assert (! c_tls->dbg.is_failed); + + ERR_clear_error (); + + res = SSL_write_ex (c_tls->sess, + buf, + buf_size, + sent); + if (1 == res) + { + mhd_assert (0 != *sent); + return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */ + } + + mhd_assert (0 == res); + *sent = 0; + switch (SSL_get_error (c_tls->sess, res)) + { + case SSL_ERROR_WANT_WRITE: + /* OpenSSL does not distinguish between "interrupted" and "try again" codes. + Based on OpenSSL result it is unclear whether the "send-ready" flag + should be reset or not. + If edge-triggered sockets polling is used and the flag is cleared, but + it should not (because the process has been "interrupted") then already + pending data could be never sent. + If the flag is not cleared, but it should be cleared (because all + received data has been processed) then it would create busy-waiting loop + with edge-triggered sockets polling. + Temporal solution: disallow edge-triggered sockets polling with OpenSSL + backend and use clear of "ready" flag. */ + // TODO: replace "BIO" with custom version and track returned errors. + return mhd_SOCKET_ERR_AGAIN; + case SSL_ERROR_NONE: + mhd_assert (0 && "Should not be possible"); + break; + case SSL_ERROR_WANT_READ: + mhd_assert (0 && "Should not be possible as re-handshakes are disallowed"); + break; + case SSL_ERROR_ZERO_RETURN: + mhd_assert (0 && "Should not be possible when sending"); + break; + case SSL_ERROR_SYSCALL: + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_CONN_BROKEN; + case SSL_ERROR_SSL: + default: + break; + } + /* Treat all other kinds of errors as hard errors */ + mhd_DBG_PRINT_TLS_ERRS (); +#ifndef NDEBUG + c_tls->dbg.is_failed = true; +#endif /* ! NDEBUG */ + return mhd_SOCKET_ERR_TLS; +} diff --git a/src/mhd2/tls_open_funcs.h b/src/mhd2/tls_open_funcs.h @@ -0,0 +1,235 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_open_funcs.h + * @brief The declarations of OpenSSL interface wrapper functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_FUNCS_H +#define MHD_TLS_OPEN_FUNCS_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if OpenSSL is enabled +#endif + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "mhd_status_code_int.h" + +#include "mhd_tls_enums.h" +#include "mhd_socket_error.h" + +/** + * The structure with daemon-specific OpenSSL data + */ +struct mhd_TlsOpenDaemonData; /* Forward declaration */ + +/** + * The structure with connection-specific OpenSSL data + */ +struct mhd_TlsOpenConnData; /* Forward declaration */ + + +/* ** Global initialisation / de-initialisation ** */ + +/** + * Globally initialise OpenSSL backend. + * Once initialised, this backend cannot be de-initialised. + */ +MHD_INTERNAL void +mhd_tls_open_global_init_once (void); + +/* No-op for OpenSSL backend */ +#define mhd_tls_open_global_re_init() ((void) 0) + +/* No-op for OpenSSL backend */ +#define mhd_tls_open_global_deinit() ((void) 0) + +/** + * Check whether OpenSSL backend was successfully initialised globally + * @return 'true' if backend has been successfully initialised, + * 'false' if backend cannot be used + */ +MHD_INTERNAL bool +mhd_tls_open_is_inited_fine (void) +MHD_FN_PURE_; + + +/* ** Daemon initialisation / de-initialisation ** */ + +struct MHD_Daemon; /* Forward declaration */ +struct DaemonOptions; /* Forward declaration */ + +/** + * Check whether OpenSSL backend supports edge-triggered sockets polling + * @param s the daemon settings + * @return 'true' if the backend supports edge-triggered sockets polling, + * 'false' if edge-triggered sockets polling cannot be used + */ +#define mhd_tls_open_is_edge_trigg_supported(s) (! ! 0) + + +/** + * Allocate and initialise daemon TLS parameters + * @param d the daemon handle + * @param sk_edge_trigg if 'true' then sockets polling uses edge-triggering + * @param s the daemon settings + * @param p_d_tls the pointer to variable to set the pointer to + * the daemon's TLS settings (allocated by this function) + * @return #MHD_SC_OK on success (p_d_tls set to the allocated settings), + * error code otherwise + */ +MHD_INTERNAL mhd_StatusCodeInt +mhd_tls_open_daemon_init (struct MHD_Daemon *restrict d, + bool sk_edge_trigg, + struct DaemonOptions *restrict s, + struct mhd_TlsOpenDaemonData **restrict p_d_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4); + +/** + * De-initialise daemon TLS parameters (and free memory allocated for TLS + * settings) + * @param d_tls the pointer to the daemon's TLS settings + */ +MHD_INTERNAL void +mhd_tls_open_daemon_deinit (struct mhd_TlsOpenDaemonData *restrict d_tls) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_ (1); + + +/* ** Connection initialisation / de-initialisation ** */ + +struct mhd_ConnSocket; /* Forward declaration */ + +/** + * Get size size of the connection's TLS settings + */ +MHD_INTERNAL size_t +mhd_tls_open_conn_get_tls_size_v (void); + +/** + * Get size size of the connection's TLS settings + * @param d_tls the pointer to the daemon's TLS settings + */ +#define mhd_tls_open_conn_get_tls_size(d_tls) \ + mhd_tls_open_conn_get_tls_size_v () + +/** + * Initialise connection TLS settings + * @param d_tls the daemon TLS settings + * @param sk data about the socket for the connection + * @param[out] c_tls the pointer to the allocated space for + * the connection TLS settings + * @return 'true' on success, + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_open_conn_init (const struct mhd_TlsOpenDaemonData *restrict d_tls, + const struct mhd_ConnSocket *sk, + struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (3); + +/** + * De-initialise connection TLS settings. + * The provided pointer is not freed/deallocated. + * @param c_tls the initialised connection TLS settings + */ +MHD_INTERNAL void +mhd_tls_open_conn_deinit (struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + + +/* ** TLS connection establishing ** */ + +/** + * Perform TLS handshake + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_open_conn_handshake (struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + +/** + * Perform shutdown of TLS layer + * @param c_tls the connection TLS handle + * @return #mhd_TLS_PROCED_SUCCESS if completed successfully + * or other enum mhd_TlsProcedureResult values + */ +MHD_INTERNAL enum mhd_TlsProcedureResult +mhd_tls_open_conn_shutdown (struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_; + + +/* ** Data sending and receiving over TLS connection ** */ + +/** + * Receive the data from the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buf_size the size of the @a buf buffer + * @param[out] buf the buffer to fill with the received data + * @param[out] received the pointer to variable to get the size of the data + * actually put to the @a buffer + * @return mhd_SOCKET_ERR_NO_ERROR if receive succeed (the @a received gets + * the received size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_open_conn_recv (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict received) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +/** + * Check whether any incoming data is pending in the TLS buffers + * + * @param c_tls the connection TLS handle + * @return 'true' if any incoming remote data is already pending (the TLS recv() + * call can be performed), + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_tls_open_conn_has_data_in (struct mhd_TlsOpenConnData *restrict c_tls) +MHD_FN_PAR_NONNULL_ALL_; + +/** + * Send data to the remote side over TLS connection + * + * @param c_tls the connection TLS handle + * @param buffer_size the size of the @a buffer (in bytes) + * @param buffer content of the buffer to send + * @param[out] sent the pointer to get amount of actually sent bytes + * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets + * the sent size) or socket error + */ +MHD_INTERNAL enum mhd_SocketError +mhd_tls_open_conn_send (struct mhd_TlsOpenConnData *restrict c_tls, + size_t buf_size, + const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], + size_t *restrict sent) +MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4); + +#endif /* ! MHD_TLS_OPEN_FUNCS_H */ diff --git a/src/mhd2/tls_open_tls_lib.h b/src/mhd2/tls_open_tls_lib.h @@ -0,0 +1,66 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 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/mhd2/tls_open_tls_lib.h + * @brief The MHD wrapper for OpenSSL headers + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_TLS_OPEN_TLS_LIB_H +#define MHD_TLS_OPEN_TLS_LIB_H 1 + +#include "mhd_sys_options.h" + +#ifndef MHD_USE_OPENSSL +#error This header can be used only if GnuTLS is enabled +#endif + +/* Declare compatibility with OpenSSL versions >= 3.0 */ +#define OPENSSL_API_COMPAT 30000 +/* Request to hide all symbols deprecated before OPENSSL_API_COMPAT version */ +#define OPENSSL_NO_DEPRECATED 1 + +#include <openssl/opensslv.h> +#include <openssl/opensslconf.h> +#include <openssl/crypto.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/bio.h> +#include <openssl/conf.h> +#include <openssl/pem.h> + +#ifndef OPENSSL_VERSION_NUMBER +#error OPENSSL_VERSION_NUMBER is not defined +#endif + +#ifndef OPENSSL_VERSION_PREREQ +#error OPENSSL_VERSION_PREREQ is not defined +#endif + +#if OPENSSL_VERSION_PREREQ (3,4) +/** + * Defined if functions OSSL_LIB_CTX_get_conf_diagnostics() and + * OSSL_LIB_CTX_set_conf_diagnostics are available; + */ +# define mhd_TLS_OPEN_HAS_CONF_DIAG 1 +#endif + +#endif /* ! MHD_TLS_OPEN_TLS_LIB_H */ diff --git a/src/mhd2/upgrade_prep.c b/src/mhd2/upgrade_prep.c @@ -152,7 +152,7 @@ build_reply_header (struct MHD_Connection *restrict c, mhd_assert (MHD_HTTP_VERSION_1_1 == c->rq.http_ver); mhd_assert ((0 == c->rq.cntn.cntn_size) || \ - (MHD_CONNECTION_FULL_REQ_RECEIVED == c->state)); + (mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage)); buf_used = 0; @@ -422,21 +422,21 @@ mhd_upgrade_prep_for_action (struct MHD_Request *restrict req, struct MHD_Connection *const c = mhd_cntnr_ptr (req, struct MHD_Connection, rq); - mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state); - mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED >= c->state); + mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage); + mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED >= c->stage); if (req->have_chunked_upload && - (MHD_CONNECTION_FOOTERS_RECEIVED >= c->state)) + (mhd_HTTP_STAGE_FOOTERS_RECEIVED >= c->stage)) return false; /* The request has not been fully received */ if (! is_upload_act) { - if (MHD_CONNECTION_HEADERS_PROCESSED != c->state) + if (mhd_HTTP_STAGE_HEADERS_PROCESSED != c->stage) return false; } else { - if (MHD_CONNECTION_BODY_RECEIVING > c->state) + if (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage) return false; } diff --git a/src/mhd2/upgrade_proc.c b/src/mhd2/upgrade_proc.c @@ -49,7 +49,7 @@ MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) bool mhd_upgrade_try_start_upgrading (struct MHD_Connection *restrict c) { - mhd_assert (MHD_CONNECTION_UPGRADE_HEADERS_SENDING == c->state); + mhd_assert (mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING == c->stage); mhd_assert ((mhd_ACTION_UPGRADE == c->rq.app_act.head_act.act) || (mhd_UPLOAD_ACTION_UPGRADE == c->rq.app_act.upl_act.act)); mhd_assert (NULL != c->write_buffer); @@ -59,7 +59,7 @@ mhd_upgrade_try_start_upgrading (struct MHD_Connection *restrict c) if (c->write_buffer_append_offset != c->write_buffer_send_offset) return false; - c->state = MHD_CONNECTION_UPGRADING; + c->stage = mhd_HTTP_STAGE_UPGRADING; return true; } @@ -70,7 +70,7 @@ MHD_FN_PAR_NONNULL_ (1) bool mhd_upgrade_finish_switch_to_upgraded (struct MHD_Connection *restrict c) { struct mhd_UpgradeActionData *pupgr_data; - mhd_assert (MHD_CONNECTION_UPGRADING == c->state); + mhd_assert (mhd_HTTP_STAGE_UPGRADING == c->stage); mhd_assert (NULL != c->write_buffer); mhd_assert ((0 != c->read_buffer_offset) || (NULL == c->read_buffer)); mhd_assert (NULL == c->upgr.c); @@ -97,7 +97,7 @@ mhd_upgrade_finish_switch_to_upgraded (struct MHD_Connection *restrict c) mhd_conn_pre_clean_part1 (c); - c->state = MHD_CONNECTION_UPGRADED; + c->stage = mhd_HTTP_STAGE_UPGRADED; mhd_assert (! c->in_proc_ready); mhd_assert (NULL == mhd_DLINKEDL_GET_PREV (c, by_timeout)); @@ -121,10 +121,10 @@ MHD_upgraded_close (struct MHD_UpgradedHandle *urh) struct MHD_Connection *const restrict c = urh->c; struct MHD_Daemon *const restrict d = c->daemon; - if (MHD_CONNECTION_UPGRADED != c->state) /* Probably, assert would be better here */ + if (mhd_HTTP_STAGE_UPGRADED != c->stage) /* Probably, assert would be better here */ return MHD_SC_TOO_LATE; - c->state = MHD_CONNECTION_UPGRADED_CLEANING; + c->stage = mhd_HTTP_STAGE_UPGRADED_CLEANING; mhd_mutex_lock_chk (&(d->conns.upgr.ucu_lock)); mhd_DLINKEDL_INS_LAST (&(d->conns.upgr), c, upgr_cleanup); mhd_mutex_unlock_chk (&(d->conns.upgr.ucu_lock)); @@ -138,8 +138,8 @@ MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) void mhd_upgraded_deinit (struct MHD_Connection *restrict c) { - mhd_assert ((MHD_CONNECTION_UPGRADED_CLEANING == c->state) || \ - (MHD_CONNECTION_UPGRADED == c->state)); + mhd_assert ((mhd_HTTP_STAGE_UPGRADED_CLEANING == c->stage) || \ + (mhd_HTTP_STAGE_UPGRADED == c->stage)); mhd_assert (c == c->upgr.c); mhd_mutex_destroy_chk (&(c->upgr.lock)); diff --git a/src/mhd2/upgraded_net.c b/src/mhd2/upgraded_net.c @@ -51,7 +51,7 @@ #if ! defined (MHD_USE_POLL) && \ - (defined(MHD_POSIX_SOCKETS) || ! defined(MHD_USE_SELECT)) + (defined(MHD_SOCKETS_KIND_POSIX) || ! defined(MHD_USE_SELECT)) # if defined(_WIN32) || defined(HAVE_NANOSLEEP) || defined(HAVE_USLEEP) # define mhd_HAVE_MHD_SLEEP 1 @@ -95,7 +95,7 @@ mhd_sleep (uint_fast32_t millisec) #endif /* _WIN32 || HAVE_NANOSLEEP || HAVE_USLEEP */ -#endif /* ! MHD_USE_POLL) && (MHD_POSIX_SOCKETS || ! MHD_USE_SELECT) */ +#endif /* ! MHD_USE_POLL) && (MHD_SOCKETS_KIND_POSIX || ! MHD_USE_SELECT) */ MHD_EXTERN_ @@ -110,7 +110,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, { struct MHD_Connection *restrict c = urh->c; #if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) - const MHD_Socket socket_fd = c->socket_fd; + const MHD_Socket socket_fd = c->sk.fd; #endif /* MHD_USE_POLL || MHD_USE_SELECT */ char *restrict buf_char = (char *) recv_buf; size_t last_block_size; @@ -120,7 +120,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, if (&(c->upgr) != urh) return MHD_SC_UPGRADED_HANDLE_INVALID; - if (MHD_CONNECTION_UPGRADED != c->state) + if (mhd_HTTP_STAGE_UPGRADED != c->stage) return MHD_SC_UPGRADED_HANDLE_INVALID; if (0 == recv_buf_size) @@ -167,7 +167,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, if (mhd_SOCKET_ERR_NO_ERROR == res) { if (0 == last_block_size) - c->sk_rmt_shut_wr = true; + c->sk.state.rmt_shut_wr = true; *received_size += last_block_size; return MHD_SC_OK; } @@ -219,11 +219,11 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, #else /* ! MHD_USE_POLL */ # if defined(MHD_USE_SELECT) bool use_select; -# ifdef MHD_POSIX_SOCKETS - use_select = (socket_fd < FD_SETSIZE); -# else /* MHD_WINSOCK_SOCKETS */ +# ifdef MHD_SOCKETS_KIND_POSIX + use_select = (sk.fd < FD_SETSIZE); +# else /* MHD_SOCKETS_KIND_WINSOCK */ use_select = true; -# endif /* MHD_WINSOCK_SOCKETS */ +# endif /* MHD_SOCKETS_KIND_WINSOCK */ if (use_select) { fd_set rfds; @@ -231,11 +231,11 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, int wait_err; struct timeval tmvl; -# ifdef MHD_POSIX_SOCKETS +# ifdef MHD_SOCKETS_KIND_POSIX tmvl.tv_sec = (time_t) (max_wait_millisec / 1000); -# else /* MHD_WINSOCK_SOCKETS */ +# else /* MHD_SOCKETS_KIND_WINSOCK */ tmvl.tv_sec = (long) (max_wait_millisec / 1000); -# endif /* MHD_WINSOCK_SOCKETS */ +# endif /* MHD_SOCKETS_KIND_WINSOCK */ if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || (0 > tmvl.tv_sec)) { @@ -245,9 +245,9 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, else tmvl.tv_usec = (int) (max_wait_millisec % 1000); FD_ZERO (&rfds); - FD_SET (socket_fd, &rfds); + FD_SET (sk.fd, &rfds); - sel_res = select (c->socket_fd + 1, + sel_res = select (c->sk.fd + 1, &rfds, NULL, NULL, @@ -295,7 +295,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, if (mhd_SOCKET_ERR_NO_ERROR == res) { if (0 == last_block_size) - c->sk_rmt_shut_wr = true; + c->sk.state.rmt_shut_wr = true; *received_size += last_block_size; return MHD_SC_OK; } @@ -327,7 +327,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, { struct MHD_Connection *restrict c = urh->c; #if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) - const MHD_Socket socket_fd = c->socket_fd; + const MHD_Socket socket_fd = c->sk.fd; #endif /* MHD_USE_POLL || MHD_USE_SELECT */ const char *restrict buf_char = (const char *) send_buf; const bool push_data = (MHD_NO == more_data_to_come); @@ -339,7 +339,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, if (&(c->upgr) != urh) return MHD_SC_UPGRADED_HANDLE_INVALID; - if (MHD_CONNECTION_UPGRADED != c->state) + if (mhd_HTTP_STAGE_UPGRADED != c->stage) return MHD_SC_UPGRADED_HANDLE_INVALID; finish_time_set = false; @@ -391,7 +391,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, if (! wait_indefinitely) { uint_fast64_t cur_time; - cur_time = MHD_monotonic_msec_counter (); + cur_time = mhd_monotonic_msec_counter (); if (! finish_time_set) { @@ -451,11 +451,11 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, } #else /* ! MHD_USE_POLL */ # if defined(MHD_USE_SELECT) -# ifdef MHD_POSIX_SOCKETS - use_select = (socket_fd < FD_SETSIZE); -# else /* MHD_WINSOCK_SOCKETS */ +# ifdef MHD_SOCKETS_KIND_POSIX + use_select = (sk.fd < FD_SETSIZE); +# else /* MHD_SOCKETS_KIND_WINSOCK */ use_select = true; -# endif /* MHD_WINSOCK_SOCKETS */ +# endif /* MHD_SOCKETS_KIND_WINSOCK */ if (use_select) { fd_set wfds; @@ -472,11 +472,11 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, } else { -# ifdef MHD_POSIX_SOCKETS +# ifdef MHD_SOCKETS_KIND_POSIX tmvl.tv_sec = (time_t) (wait_left / 1000); -# else /* MHD_WINSOCK_SOCKETS */ +# else /* MHD_SOCKETS_KIND_WINSOCK */ tmvl.tv_sec = (long) (wait_left / 1000); -# endif /* MHD_WINSOCK_SOCKETS */ +# endif /* MHD_SOCKETS_KIND_WINSOCK */ if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || (0 > tmvl.tv_sec)) { @@ -488,9 +488,9 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, tmvl.tv_usec = (int) (max_wait_millisec % 1000); } FD_ZERO (&wfds); - FD_SET (socket_fd, &wfds); + FD_SET (sk.fd, &wfds); - sel_res = select (c->socket_fd + 1, + sel_res = select (c->sk.fd + 1, NULL, &wfds, NULL, diff --git a/src/tests/upgrade/test_upgrade.c b/src/tests/upgrade/test_upgrade.c @@ -61,13 +61,13 @@ # 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 */ +#if defined(MHD_SOCKETS_KIND_POSIX) +# ifdef MHD_SOCKETS_KIND_WINSOCK +# error Both MHD_SOCKETS_KIND_POSIX and MHD_SOCKETS_KIND_WINSOCK are defined +# endif /* MHD_SOCKETS_KIND_WINSOCK */ +#elif ! defined(MHD_SOCKETS_KIND_WINSOCK) +# error Neither MHD_SOCKETS_KIND_POSIX nor MHD_SOCKETS_KIND_WINSOCK are defined +#endif /* MHD_SOCKETS_KIND_WINSOCK */ #ifndef mhd_SSTR_LEN @@ -89,7 +89,7 @@ # define SHUT_RDWR SD_BOTH #endif -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) # if defined(ENETUNREACH) # define mhdt_SCKT_HARD_ERR ENETUNREACH # elif defined(ENOTCONN) @@ -103,7 +103,7 @@ # else # define mhdt_SCKT_HARD_ERR 99 /* Fallback, never used in practice */ # endif -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ # define mhdt_SCKT_HARD_ERR WSAENETRESET #endif @@ -123,9 +123,9 @@ _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, strerror (errno)); -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ fflush (stderr); exit (99); } @@ -167,9 +167,9 @@ _testErrorLog_func (const char *errDesc, const char *funcName, int lineNum) fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, strerror (errno)); -#ifdef MHD_WINSOCK_SOCKETS +#ifdef MHD_SOCKETS_KIND_WINSOCK fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ fflush (stderr); } @@ -328,7 +328,7 @@ gnutlscli_connect (int *sock, static void make_blocking (MHD_Socket fd) { -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) int flags; flags = fcntl (fd, F_GETFL); @@ -337,12 +337,12 @@ make_blocking (MHD_Socket fd) if ((flags & ~O_NONBLOCK) != flags) if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) externalErrorExitDesc ("fcntl() failed"); -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) unsigned long flags = 0; if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) externalErrorExitDesc ("ioctlsocket() failed"); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ } @@ -357,7 +357,7 @@ make_blocking (MHD_Socket fd) static void make_nonblocking (MHD_Socket fd) { -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) int flags; flags = fcntl (fd, F_GETFL); @@ -366,12 +366,12 @@ make_nonblocking (MHD_Socket fd) if (O_NONBLOCK != (flags & O_NONBLOCK)) if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK)) externalErrorExitDesc ("fcntl() failed"); -#elif defined(MHD_WINSOCK_SOCKETS) +#elif defined(MHD_SOCKETS_KIND_WINSOCK) unsigned long flags = 1; if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) externalErrorExitDesc ("ioctlsocket() failed"); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ } @@ -393,13 +393,13 @@ make_nodelay (MHD_Socket fd) sizeof (on_val))) return; /* Success exit point */ -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). " "errno: %d (%s)\n", (int) errno, strerror (errno)); -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). " "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ fflush (stderr); #endif /* TCP_NODELAY */ } @@ -632,10 +632,10 @@ wr_wait_socket_ready_noabort_ (struct wr_socket *s, struct timeval tmo; struct timeval *tmo_ptr; -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK if (FD_SETSIZE <= s->fd) externalErrorExitDesc ("Too large FD value"); -#endif /* ! MHD_WINSOCK_SOCKETS */ +#endif /* ! MHD_SOCKETS_KIND_WINSOCK */ FD_ZERO (&fds); FD_SET (s->fd, &fds); if (0 <= timeout_ms) @@ -666,12 +666,12 @@ wr_wait_socket_ready_noabort_ (struct wr_socket *s, fprintf (stderr, "Timeout"); else { -#ifndef MHD_WINSOCK_SOCKETS +#ifndef MHD_SOCKETS_KIND_WINSOCK fprintf (stderr, "Error %d (%s)", (int) errno, strerror (errno)); -#else /* MHD_WINSOCK_SOCKETS */ +#else /* MHD_SOCKETS_KIND_WINSOCK */ fprintf (stderr, "Error (WSAGetLastError code: %d)", (int) WSAGetLastError ()); -#endif /* MHD_WINSOCK_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_WINSOCK */ } fprintf (stderr, " waiting for socket to be available for %s.\n", (WR_WAIT_FOR_RECV == wait_for) ? "receiving" : "sending"); @@ -715,7 +715,7 @@ wr_connect_tmo (struct wr_socket *s, bool connect_completed = false; err = mhd_SCKT_GET_LERR (); -#if defined(MHD_POSIX_SOCKETS) +#if defined(MHD_SOCKETS_KIND_POSIX) while (! connect_completed && (EINTR == err)) { connect_completed = (0 == connect (s->fd, addr, (socklen_t) length)); @@ -728,7 +728,7 @@ wr_connect_tmo (struct wr_socket *s, connect_completed = true; } } -#endif /* MHD_POSIX_SOCKETS */ +#endif /* MHD_SOCKETS_KIND_POSIX */ if (! connect_completed && (mhd_SCKT_ERR_IS_INPROGRESS (err) || mhd_SCKT_ERR_IS_EAGAIN (err))) /* No modern system uses EAGAIN, except W32 */