libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit c0f7cbb280cff285b5ca7991fb865b761f230f8a
parent 40b675518b14fef4d327323f63c9c70f1b2bc44e
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Fri, 28 Oct 2022 12:06:46 +0300

Added back testing with socat as a fallback option

Diffstat:
Mconfigure.ac | 27++++++++++++++++++++++-----
Msrc/testzzuf/Makefile.am | 29++++++++++++++++++++++++++---
Msrc/testzzuf/test_get.c | 107++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Asrc/testzzuf/zzuf_socat_test_runner.sh | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testzzuf/zzuf_test_runner.sh | 45++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 273 insertions(+), 53 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -1789,6 +1789,7 @@ AS_VAR_IF([use_heavy_tests], ["yes"], ] ) AM_CONDITIONAL([HEAVY_TESTS],[test "x$use_heavy_tests" = "xyes"]) +AM_CONDITIONAL([VHEAVY_TESTS],[test "x$use_vheavy_tests" = "xyes"]) AM_CONDITIONAL([TESTS_STRESS_OS],[false]) AC_ARG_ENABLE([[poll]], @@ -4844,6 +4845,7 @@ int main(void) # fuzzing tests run_zzuf_tests="no" +zzuf_socat_mandatory="no" AS_VAR_IF([use_heavy_tests],["yes"], [ AS_VAR_IF([enable_curl],["yes"], @@ -4855,19 +4857,32 @@ AS_VAR_IF([use_heavy_tests],["yes"], run_zzuf_tests_MSG="no, zzuf tool not found" ], [ + AC_PATH_PROG([SOCAT],[socat],[no]) AS_IF([test "x${ac_cv_func_accept4}" = "xyes" && test "x${enable_asserts}" = "xno"], [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, fuzzing tests require asserts enabled on this platform" + zzuf_socat_mandatory="yes" + AS_VAR_IF([SOCAT],["no"], + [run_zzuf_tests_MSG="no, socat not found, but required for fuzzing tests on this platform without asserts enabled"], + [ + run_zzuf_tests="yes" + run_zzuf_tests_MSG="yes, with zzuf and socat tools" + ] + ) ], [test -n "${enabled_sanitizers}"], [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, fuzzing tests cannot be used with sanitizers" + zzuf_socat_mandatory="yes" + AS_VAR_IF([SOCAT],["no"], + [run_zzuf_tests_MSG="no, socat not found, but required for fuzzing tests when sanitizers enabled"], + [ + run_zzuf_tests="yes" + run_zzuf_tests_MSG="yes, with zzuf and socat tools" + ] + ) ], [ run_zzuf_tests="yes" - run_zzuf_tests_MSG="yes" + run_zzuf_tests_MSG="yes, with zzuf tool" ] ) ] @@ -4885,6 +4900,7 @@ AS_VAR_IF([use_heavy_tests],["yes"], ] ) AM_CONDITIONAL([RUN_ZZUF_TESTS],[test "x$run_zzuf_tests" = "xyes"]) +AM_CONDITIONAL([FORCE_USE_ZZUF_SOCAT],[test "x$zzuf_socat_mandatory" = "xyes"]) # Final flags that may interfere with autoconf detections AS_CASE([${enable_build_type}],[debug|debugger], @@ -4959,6 +4975,7 @@ AC_CONFIG_COMMANDS([po-directories], AC_DEFINE_DIR([MHD_PLUGIN_INSTALL_PREFIX], [libdir/libmicrohttpd], [tls plugins]) AC_SUBST([ZZUF]) +AC_SUBST([SOCAT]) # should experimental code be compiled (code that may not yet compile)? AC_MSG_CHECKING(whether to compile experimental code) diff --git a/src/testzzuf/Makefile.am b/src/testzzuf/Makefile.am @@ -10,6 +10,15 @@ ZZUF_SEED = 0 # Additional flags for zzuf ZZUF_FLAGS = +# Additional flags for socat (if socat is used) +SOCAT_FLAGS = + +if FORCE_USE_ZZUF_SOCAT +TEST_RUNNER_SCRIPT = zzuf_socat_test_runner.sh +else +TEST_RUNNER_SCRIPT = zzuf_test_runner.sh +endif + AM_CPPFLAGS = \ -I$(top_srcdir)/src/include \ -I$(top_srcdir)/src/microhttpd \ @@ -23,7 +32,9 @@ AM_LDFLAGS = $(LDFLAGS_ac) AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) \ ZZUF="$(ZZUF)" ; export ZZUF ; \ ZZUF_SEED="$(ZZUF_SEED)" ; export ZZUF_SEED ; \ - ZZUF_FLAGS="$(ZZUF_FLAGS)" ; export ZZUF_FLAGS ; + ZZUF_FLAGS="$(ZZUF_FLAGS)" ; export ZZUF_FLAGS ; \ + SOCAT="$(SOCAT)" ; export SOCAT ; \ + SOCAT_FLAGS="$(SOCAT_FLAGS)" ; export SOCAT_FLAGS ; if USE_COVERAGE AM_CFLAGS += -fprofile-arcs -ftest-coverage @@ -70,9 +81,21 @@ check_PROGRAMS = \ TESTS = $(check_PROGRAMS) -dist_check_SCRIPTS = zzuf_test_runner.sh +dist_check_SCRIPTS = zzuf_test_runner.sh zzuf_socat_test_runner.sh + +LOG_COMPILER = @SHELL@ "$(srcdir)/$(TEST_RUNNER_SCRIPT)" + +if VHEAVY_TESTS +check_SCRIPTS = warn_vheavy_use + +.PHONY: warn_vheavy_use +endif -LOG_COMPILER = @SHELL@ $(srcdir)/zzuf_test_runner.sh +warn_vheavy_use: + @echo "NOTICE" ; \ + echo "NOTICE: Full heavy tests are enabled. Each test may take up to several minutes to complete." ; \ + echo "NOTICE" + tests_common_sources = mhd_debug_funcs.h mhd_debug_funcs.c diff --git a/src/testzzuf/test_get.c b/src/testzzuf/test_get.c @@ -96,8 +96,14 @@ static int use_post_form; static int use_long_header; static int use_long_uri; static int use_close; +static int run_with_socat; #define TEST_BASE_URI "http:/" "/127.0.0.1/test_uri" +#define TEST_BASE_URI_SOCAT "http:/" "/127.0.0.121/test_uri" + +#define SOCAT_PORT 10121 + +#define TEST_BASE_PORT 4010 #define EMPTY_PAGE "Empty page." #define EMPTY_PAGE_ALT "Alternative empty page." @@ -959,19 +965,24 @@ setupCURL (struct CBC *cbc, uint16_t port CURLcode e; char *buf; const char *uri_to_use; + const char *base_uri; #ifndef TEST_USE_STATIC_POST_DATA *mime = NULL; #endif /* ! TEST_USE_STATIC_POST_DATA */ + base_uri = run_with_socat ? TEST_BASE_URI_SOCAT : TEST_BASE_URI; if (! use_long_uri) { - uri_to_use = TEST_BASE_URI; + uri_to_use = base_uri; buf = NULL; } else { size_t pos; + size_t base_uri_len; + + base_uri_len = strlen (base_uri); buf = malloc (TEST_STRING_VLONG_LEN + 1); if (NULL == buf) { @@ -979,8 +990,8 @@ setupCURL (struct CBC *cbc, uint16_t port "at line %d.\n", (int) __LINE__); return NULL; } - memcpy (buf, TEST_BASE_URI, MHD_STATICSTR_LEN_ (TEST_BASE_URI)); - for (pos = MHD_STATICSTR_LEN_ (TEST_BASE_URI); + memcpy (buf, base_uri, base_uri_len); + for (pos = base_uri_len; pos < TEST_STRING_VLONG_LEN; ++pos) { @@ -993,6 +1004,8 @@ setupCURL (struct CBC *cbc, uint16_t port buf[TEST_STRING_VLONG_LEN] = 0; uri_to_use = buf; } + if (run_with_socat) + port = SOCAT_PORT; c = curl_easy_init (); if (NULL == c) @@ -1189,8 +1202,10 @@ start_daemon_for_test (unsigned int daemon_flags, uint16_t *pport, "at line %d.\n", (int) __LINE__); return NULL; } + /* Do not use accept4() as only accept() is intercepted by zzuf */ - MHD_avoid_accept4_ (d); + if (! run_with_socat) + MHD_avoid_accept4_ (d); if (0 == *pport) { @@ -1516,44 +1531,49 @@ run_all_checks (void) unsigned int testRes; unsigned int ret = 0; - if (MHD_are_sanitizers_enabled_ ()) + if (! run_with_socat) { - fprintf (stderr, "The test does not work with sanitizers. " - "At line %d.\n", (int) __LINE__); - return 77; - } - if (! MHD_is_avoid_accept4_possible_ ()) - { - fprintf (stderr, - "Non-debug build of MHD on this platform use accept4() function. " - "Test with zzuf is not possible. " - "At line %d.\n", (int) __LINE__); - return 77; + if (MHD_are_sanitizers_enabled_ ()) + { + fprintf (stderr, "Direct run with zzuf does not work with sanitizers. " + "At line %d.\n", (int) __LINE__); + return 77; + } + if (! MHD_is_avoid_accept4_possible_ ()) + { + fprintf (stderr, + "Non-debug build of MHD on this platform use accept4() function. " + "Direct run with zzuf is not possible. " + "At line %d.\n", (int) __LINE__); + return 77; + } + if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) + port = 0; /* Use system automatic assignment */ + else + { + port = TEST_BASE_PORT; /* Use predefined port, may break parallel testing of another MHD build */ + if (oneone) + port += 100; + if (use_long_uri) + port += 30; + else if (use_long_header) + port += 35; + else if (use_get_chunked) + port += 0; + else if (use_get) + port += 5; + else if (use_post_form) + port += 10; + else if (use_post) + port += 15; + else if (use_put_large) + port += 20; + else if (use_put_chunked) + port += 25; + } } - if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) - port = 0; /* Use system automatic assignment */ else - { - port = 4010; /* Use predefined port, may break parallel testing of another MHD build */ - if (oneone) - port += 100; - if (use_long_uri) - port += 30; - else if (use_long_header) - port += 35; - else if (use_get_chunked) - port += 0; - else if (use_get) - port += 5; - else if (use_post_form) - port += 10; - else if (use_post) - port += 15; - else if (use_put_large) - port += 20; - else if (use_put_chunked) - port += 25; - } + port = TEST_BASE_PORT; /* Use predefined port, may break parallel testing of another MHD build */ if (! dry_run && (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))) { @@ -1600,6 +1620,7 @@ int main (int argc, char *const *argv) { unsigned int res; + int use_magic_exit_codes; oneone = ! has_in_name (argv[0], "10"); use_get = has_in_name (argv[0], "_get"); @@ -1613,6 +1634,7 @@ main (int argc, char *const *argv) use_long_uri = has_in_name (argv[0], "_long_uri"); use_close = has_in_name (argv[0], "_close"); + run_with_socat = has_param (argc, argv, "--with-socat"); dry_run = has_param (argc, argv, "--dry-run") || has_param (argc, argv, "-n"); @@ -1623,17 +1645,18 @@ main (int argc, char *const *argv) "for the test type.\n", argv[0] ? argv[0] : "(NULL)"); return 99; } + use_magic_exit_codes = run_with_socat || dry_run; /* zzuf cannot bypass exit values. Unless 'dry run' is used, do not return errors for external error conditions (like out-of-memory) as they will be reported as test failures. */ if (! test_global_init ()) - return dry_run ? 99 : 0; + return use_magic_exit_codes ? 99 : 0; res = run_all_checks (); test_global_deinit (); if (99 == res) - return dry_run ? 99 : 0; + return use_magic_exit_codes ? 99 : 0; if (77 == res) - return dry_run ? 77 : 0; + return use_magic_exit_codes ? 77 : 0; return (0 == res) ? 0 : 1; /* 0 == pass */ } diff --git a/src/testzzuf/zzuf_socat_test_runner.sh b/src/testzzuf/zzuf_socat_test_runner.sh @@ -0,0 +1,118 @@ +#!/bin/sh + +if set -m ; then : ; else + echo "The shell $SHELL does not support background jobs, the test cannot run." 1>&2 + exit 77 +fi + +socat_listen_ip='127.0.0.121' +socat_listen_port='10121' +mhd_listen_port='4010' +max_runtime_sec='300' + +if test "x${ZZUF}" = "xno" ; then + echo "zzuf command missing" 1>&2 + exit 77 +fi + +if command -v "${ZZUF}" > /dev/null 2>&1 ; then : ; else + echo "zzuf command missing" 1>&2 + exit 77 +fi + +if test "x${SOCAT}" = "xno" ; then + echo "socat command missing" 1>&2 + exit 77 +fi + +if command -v "${SOCAT}" > /dev/null 2>&1 ; then : ; else + echo "socat command missing" 1>&2 + exit 77 +fi + +socat_test_params="-ls -lu \ + -T0.1 -4 \ + TCP-LISTEN:${socat_listen_port},bind=${socat_listen_ip},reuseaddr,linger=2,linger2=1,accept-timeout=0.1 \ + TCP:127.0.0.1:${mhd_listen_port},reuseaddr" + +echo "## Trying to run socat to test ports availability..." +if "${SOCAT}" ${socat_test_params} ; then + echo "Success." +else + echo "socat test run failed" 1>&2 + exit 77 +fi + +# fuzz the input only for IP ${socat_listen_ip}. libcurl uses another IP +# in this test therefore libcurl input is not fuzzed. +zzuf_all_params="--ratio=0.001:0.4 --autoinc --verbose --signal \ + --max-usertime=${max_runtime_sec} --check-exit --network --allow=${socat_listen_ip} --exclude=." + +if test -n "${ZZUF_SEED}" ; then + zzuf_all_params="${zzuf_all_params} --seed=${ZZUF_SEED}" +fi + +if test -n "${ZZUF_FLAGS}" ; then + zzuf_all_params="${zzuf_all_params} ${ZZUF_FLAGS}" +fi + +# Uncomment the next line to see more zzuf data in logs +#zzuf_all_params="${zzuf_all_params} -dd" + +socat_options="-ls -lu \ + -T3 -4" +socat_addr1="TCP-LISTEN:${socat_listen_port},bind=${socat_listen_ip},reuseaddr,nodelay,linger=2,linger2=1,accept-timeout=${max_runtime_sec},fork" +socat_addr2="TCP:127.0.0.1:${mhd_listen_port},reuseaddr,connect-timeout=3,nodelay,linger=2,linger2=1" + +if test -n "${SOCAT_FLAGS}" ; then + socat_options="${socat_options} ${SOCAT_FLAGS}" +fi + +# Uncomment the next line to see more socat data in logs +#socat_options="${socat_options} -dd -D" + +# Uncomment the next line to see all traffic in logs +#socat_options="${socat_options} -v" + +stop_zzuf_socat () +{ + trap - EXIT + if test -n "$zzuf_pid" ; then + echo "The test has been interrupted." 1>&2 + test "x$zzuf_pid" = "xstarting" && zzuf_pid=$! + # Finish zzuf + socat + kill -TERM ${zzuf_pid} -${zzuf_pid} + # Finish the test + kill -INT %2 2> /dev/null || kill -INT %1 2> /dev/null + exit 99 + fi +} + +echo "## Starting zzuf with socat to reflect fuzzed traffic..." +trap 'stop_zzuf_socat' EXIT +zzuf_pid="starting" +"${ZZUF}" ${zzuf_all_params} "${SOCAT}" ${socat_options} ${socat_addr1} ${socat_addr2} & +if test $? -eq 0 ; then + zzuf_pid=$! + echo "zzuf with socat has been started." +else + zzuf_pid='' + echo "Failed to start zzuf with socat" 1>&2 + exit 99 +fi + +echo "## Starting real test of $@ with traffic fuzzed by zzuf with socat..." +"$@" --with-socat +test_result=$? +trap - EXIT +echo "$@ has exited with the return code $test_result" +if kill -s 0 -- $$ 2> /dev/null ; then + if kill -s 0 -- ${zzuf_pid} -${zzuf_pid} ; then : ; else + echo "No running zzuf with socat is detected after the test." 1>&2 + echo "Looks like zzuf ended prematurely, at least part of the testing has not been performed." 1>&2 + test_result=99 + fi +fi +kill -TERM ${zzuf_pid} -${zzuf_pid} +zzuf_pid='' +exit $test_result diff --git a/src/testzzuf/zzuf_test_runner.sh b/src/testzzuf/zzuf_test_runner.sh @@ -13,6 +13,40 @@ if command -v "${ZZUF}" > /dev/null 2>&1 ; then : ; else exit 77 fi +run_with_socat () +{ + echo "Trying to run the test with socat..." + script_dir="" + if command -v dirname > /dev/null 2>&1 ; then + test_dir=`dirname /` + if test "x${test_dir}" = "x/" ; then + if dirname "$1" > /dev/null 2>&1 ; then + script_dir=`dirname "$1"` + if test -n "${script_dir}" ; then + # Assume script is not in the root dir + script_dir="${script_dir}/" + else + script_dir="./" + fi + fi + fi + fi + if test -z "${script_dir}" ; then + if echo "$1" | sed 's|[^/]*$||' > /dev/null 2>&1 ; then + script_dir=`echo "$1" | sed 's|[^/]*$||'` + if test -z "${script_dir}" ; then + script_dir="./" + fi + fi + fi + if test -z "${script_dir}" ; then + echo "Cannot determine script location, will try current directory." 1>&2 + script_dir="./" + fi + $SHELL "${script_dir}zzuf_socat_test_runner.sh" "$@" + exit $? +} + # zzuf cannot pass-through the return value of checked program # so try the direct dry-run first to get possibe 77 or 99 codes echo "## Dry-run of the $@..." @@ -20,7 +54,11 @@ if "$@" --dry-run ; then echo "# Dry-run succeded." else res_code=$? - echo "Dry-run failed with exit code $res_code. $@ will not be run with zzuf." 1>&2 + echo "Dry-run failed with exit code $res_code." 1>&2 + if test $res_code -ne 99; then + run_with_socat "$@" + fi + echo "$@ will not be run with zzuf." 1>&2 exit $res_code fi @@ -46,8 +84,9 @@ if "$ZZUF" ${zzuf_all_params} "$@" --dry-run ; then echo "# Dry-run with zzuf succeded." else res_code=$? - echo "$@ cannot be run with zzuf. The test is skipped." 1>&2 - exit 77 + echo "$@ cannot be run with zzuf directly." 1>&2 + run_with_socat "$@" + exit $res_code fi echo "## Real test of $@ with zzuf..."