/
githubmirror
/
libmicrohttpd
Обзор
Документация
Войти
/
githubmirror
/
libmicrohttpd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/testzzuf/mhd_zzuf_common.h
572 строки
16 KB
Christian Grothoff
expand test suite, add CI/CD logic as done in other GNUnet projects
28 июл 2026, 13:36
Не верифицирован
28 июл 2026, 13:36
e34baa3
Код
Авторство
О чём код?
/* This file is part of GNU libmicrohttpd Copyright (C) 2026 Christian Grothoff 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. 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 GNU libmicrohttpd. If not, see <http://www.gnu.org/licenses/>. */ /** * @file testzzuf/mhd_zzuf_common.h * @brief Shared support code for the fuzzing tests in this directory * @author Christian Grothoff * * This header must be included *after* "platform.h" and <microhttpd.h>. * * The helpers here provide: * - the common command line handling ("--dry-run", "--with-socat"), * - the matrix of "hostile" daemon option profiles (small memory pools, * small connection limits, short timeouts, strictness levels), * - a generic driver that runs a client callback against every polling * mode supported by the current MHD build, * - a tiny raw socket HTTP client, needed for all requests that libcurl * is unable to generate (chunk extensions, pipelining, header-less * requests, hand-crafted "Authorization:" headers). */ #ifndef MHD_ZZUF_COMMON_H #define MHD_ZZUF_COMMON_H 1 #include <stddef.h> #include <stdint.h> #include <curl/curl.h> #include <microhttpd.h> #ifndef MHD_STATICSTR_LEN_ /** * Determine length of static string / macro strings at compile time. */ #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) #endif /* ! MHD_STATICSTR_LEN_ */ /** * The IP address MHD is listening on. * Must match 'mhd_listen_ip' in zzuf_test_runner.sh, as this is the only * address for which zzuf is instructed to fuzz the network traffic. */ #define ZZUF_MHD_LISTEN_IP "127.0.0.1" /** * The IP address used by the test clients as the source address. * Must be different from #ZZUF_MHD_LISTEN_IP so that the traffic * generated by the clients themselves is not fuzzed. */ #define ZZUF_CLIENT_BIND_IP "127.0.0.101" /** * The IP address socat is listening on. * Must match 'socat_listen_ip' in zzuf_socat_test_runner.sh. */ #define ZZUF_SOCAT_IP "127.0.0.121" /** * The port socat is listening on. * Must match 'socat_listen_port' in zzuf_socat_test_runner.sh. */ #define ZZUF_SOCAT_PORT 10121 /** * The port MHD is listening on when the port cannot be auto-detected. * Must match 'mhd_listen_port' in zzuf_socat_test_runner.sh. */ #define ZZUF_BASE_PORT 4010 /** * The default MHD connection timeout (in seconds) used by the tests. */ #define ZZUF_MHD_TIMEOUT 2 /** * The client-side timeout (in seconds). */ #define ZZUF_CLIENT_TIMEOUT 5 /** * A larger loop count runs more random tests, but takes longer. * Can be redefined by CPPFLAGS=-DZZUF_LOOP_COUNT=123 */ #ifndef ZZUF_LOOP_COUNT #ifndef _MHD_VHEAVY_TESTS #define ZZUF_LOOP_COUNT 10 #else /* _MHD_VHEAVY_TESTS */ #define ZZUF_LOOP_COUNT 200 #endif /* _MHD_VHEAVY_TESTS */ #endif /* ! ZZUF_LOOP_COUNT */ /* *** Global test parameters, initialised by zzuf_parse_common_args() *** */ /** * Non-zero if the traffic is relayed (and fuzzed) by socat. */ extern int zzuf_run_with_socat; /** * Non-zero if this is a "dry run", i.e. no real client requests are made. */ extern int zzuf_dry_run; /** * Non-zero if HTTP/1.1 should be used by the clients, zero for HTTP/1.0. */ extern int zzuf_oneone; /** * Non-zero if MHD should close the connection after each reply. */ extern int zzuf_use_close; /** * Parse the common command line parameters and derive the test flavour * from the program name. Also makes SIGPIPE non-fatal (if applicable). * * @param argc as given to main() * @param argv as given to main() */ void zzuf_parse_common_args (int argc, char *const *argv); /** * Check whether the name of the running program contains @a marker. * zzuf_parse_common_args() must have been called before. * * @param marker the marker to look for * @return non-zero if the marker is present, zero otherwise */ int zzuf_name_has (const char *marker); /** * The number of client iterations to perform, zero for "dry runs". */ unsigned int zzuf_loop_count (void); /* *** The matrix of "hostile" daemon option profiles *** */ /** * One set of daemon options used to widen the tested option matrix. */ struct zzuf_opt_profile { /** * Human readable name of the profile, used for logging. */ const char *name; /** * The value for #MHD_OPTION_CONNECTION_MEMORY_LIMIT. * Zero means "keep the MHD default". * Values below MHD_BUF_INC_SIZE (1500) enable the read buffer * "shift back" code path in MHD. */ size_t mem_limit; /** * The value for #MHD_OPTION_CONNECTION_LIMIT. * Zero means "keep the MHD default". */ unsigned int conn_limit; /** * The value for #MHD_OPTION_CONNECTION_TIMEOUT (seconds). * Never zero, as the tests rely on MHD closing idle connections. */ unsigned int timeout; /** * The value for #MHD_OPTION_CLIENT_DISCIPLINE_LVL (or, when * @a use_legacy_strict is set, for #MHD_OPTION_STRICT_FOR_CLIENT). */ int discipline_lvl; /** * If non-zero, the (older) #MHD_OPTION_STRICT_FOR_CLIENT option is used * instead of #MHD_OPTION_CLIENT_DISCIPLINE_LVL, so that the legacy * option translation code is exercised as well. */ int use_legacy_strict; }; /** * The number of available option profiles. */ unsigned int zzuf_num_opt_profiles (void); /** * Get the option profile number @a idx (wrapping around). * * Profile zero is always the "plain" profile, i.e. the set of options * that was used by the fuzzing tests before the option matrix was added. */ const struct zzuf_opt_profile * zzuf_opt_profile (unsigned int idx); /** * The profile that is in use by the currently running daemon. * Never NULL after the first daemon has been started. */ extern const struct zzuf_opt_profile *zzuf_active_profile; /* *** Daemon handling *** */ /** * Select the port to be used by the daemon. * * @param offset the per-test offset added to #ZZUF_BASE_PORT if the port * cannot be auto-detected * @return the port to be given to MHD_start_daemon(), may be zero */ uint16_t zzuf_pick_port (uint16_t offset); /** * Start a daemon for the test. * * @param daemon_flags the flags for MHD_start_daemon() * @param[in,out] pport the port to use, updated with the real port * @param prof the option profile to apply, never NULL * @param mem_limit_override if non-zero, this value is used for * #MHD_OPTION_CONNECTION_MEMORY_LIMIT instead of * the value from @a prof * @param ahc the access handler callback * @param ahc_cls the closure for @a ahc * @param rcc the request completed callback, may be NULL * @param rcc_cls the closure for @a rcc * @param extra_opts additional options, terminated by #MHD_OPTION_END; * may be NULL * @return the daemon, or NULL on error */ struct MHD_Daemon * zzuf_start_daemon (unsigned int daemon_flags, uint16_t *pport, const struct zzuf_opt_profile *prof, size_t mem_limit_override, MHD_AccessHandlerCallback ahc, void *ahc_cls, MHD_RequestCompletedCallback rcc, void *rcc_cls, const struct MHD_OptionItem *extra_opts); /** * The client callback invoked by zzuf_run_polling_modes() once per daemon. * * @param d_extern the daemon that must be driven by MHD_run() while the * client is waiting; NULL if the daemon uses an internal * polling thread * @param port the port the daemon is listening on * @param cls the closure * @return zero on success, 77 to skip the test, 99 for an external * (non-MHD) error, any other non-zero value for a test failure */ typedef unsigned int (*zzuf_client_func)(struct MHD_Daemon *d_extern, uint16_t port, void *cls); /** * Parameters for zzuf_run_polling_modes(). */ struct zzuf_run_params { /** * The port to use, updated in place. */ uint16_t port; /** * The access handler callback. */ MHD_AccessHandlerCallback ahc; /** * The closure for @e ahc. */ void *ahc_cls; /** * The request completed callback, may be NULL. */ MHD_RequestCompletedCallback rcc; /** * The closure for @e rcc. */ void *rcc_cls; /** * If non-zero, this value is always used for the connection memory * limit, overriding the value from the option profile. */ size_t mem_limit_override; /** * If non-zero, a different option profile is used for every started * daemon, sweeping the whole option matrix during a single test run. */ int sweep_profiles; /** * The number of the first option profile to use when @e sweep_profiles * is set. Use 1 to skip the "plain" profile, i.e. to make sure that * every daemon of the test uses a small connection memory pool. */ unsigned int profile_start; /** * The client callback. */ zzuf_client_func client; /** * The closure for @e client. */ void *client_cls; /** * Additional daemon options, terminated by #MHD_OPTION_END. * May be NULL. */ const struct MHD_OptionItem *extra_opts; }; /** * Run the client callback against a daemon in every polling mode * supported by the current MHD build. * * @param[in,out] p the parameters * @return zero if all checks succeeded, 77 to skip, 99 for an external * error, any other non-zero value on failure */ unsigned int zzuf_run_polling_modes (struct zzuf_run_params *p); /** * Check whether the test can be run at all with the current build and * the current invocation mode. * * @return zero if the test may run, 77 if the test must be skipped */ unsigned int zzuf_check_runnable (void); /* *** The raw socket client *** */ /** * One piece of a raw request. Every piece is written with a separate * send() call, so the pieces typically end up in separate TCP segments. * This is used deliberately to feed MHD with partial header, chunk-size * and chunk-extension lines. */ struct zzuf_raw_part { /** * The data to send, not zero-terminated. */ const char *data; /** * The number of bytes to send. */ size_t size; }; /** * The raw exchange has been performed (whatever the result was). */ #define ZZUF_RAW_OK 0 /** * The raw client could not be set up. This is an external error and * must not be reported as a test failure. */ #define ZZUF_RAW_SETUP_FAILED 1 /** * Connect to the daemon (via socat, if used), send @a parts and read the * reply until the peer closes the connection or the timeout expires. * * The reply is discarded: under fuzzing, any reply (including no reply * at all) is a valid outcome. Only crashes and hangs are failures. * * @param d_extern if not NULL, MHD_run() is called while waiting * @param port the port MHD is listening on (ignored if socat is used) * @param parts the pieces of the request * @param num_parts the number of @a parts * @param timeout_sec the maximum number of seconds to spend * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED */ int zzuf_raw_exchange (struct MHD_Daemon *d_extern, uint16_t port, const struct zzuf_raw_part *parts, size_t num_parts, unsigned int timeout_sec); /** * The same as zzuf_raw_exchange(), but with two extra abilities. * * With @a bypass_relay set, the client connects to MHD directly instead of * going through socat. As zzuf only fuzzes the socat process, such an * exchange is *not* fuzzed at all. This makes it possible to run a small * deterministic self-check of the parser inside the very same test binary. * Note that this only works in the socat mode: when zzuf runs the test * binary itself, all traffic accepted by MHD is fuzzed and there is no * clean channel to MHD. * * @param d_extern if not NULL, MHD_run() is called while waiting * @param port the port MHD is listening on * @param bypass_relay if non-zero, connect to MHD directly * @param parts the pieces of the request * @param num_parts the number of @a parts * @param timeout_sec the maximum number of seconds to spend * @param[out] resp_buf the buffer for the reply, may be NULL * @param resp_buf_size the size of @a resp_buf * @param[out] resp_len set to the number of bytes stored in @a resp_buf, * may be NULL * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED */ int zzuf_raw_exchange2 (struct MHD_Daemon *d_extern, uint16_t port, int bypass_relay, const struct zzuf_raw_part *parts, size_t num_parts, unsigned int timeout_sec, char *resp_buf, size_t resp_buf_size, size_t *resp_len); /** * Convenience wrapper around zzuf_raw_exchange() for a request that is * sent as a single zero-terminated string. * * @param d_extern if not NULL, MHD_run() is called while waiting * @param port the port MHD is listening on (ignored if socat is used) * @param request the zero-terminated request * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED */ int zzuf_raw_request (struct MHD_Daemon *d_extern, uint16_t port, const char *request); /* *** libcurl helpers *** */ /** * The sink for the data downloaded by libcurl. */ struct zzuf_curl_sink { /** * The number of bytes received so far. */ size_t dn_pos; /** * The scratch buffer. */ char buf[2048]; }; /** * Create an "easy" handle with the settings that are common to all * fuzzing tests in this directory. * * @param port the port MHD is listening on (ignored if socat is used) * @param uri_tail the path (and query string) of the request-target, * must start with a slash * @param sink the download sink, must stay valid while the handle is used * @return the handle, or NULL on failure */ CURL * zzuf_curl_setup (uint16_t port, const char *uri_tail, struct zzuf_curl_sink *sink); /** * The state needed to run "easy" handles against a daemon that may or * may not have an internal polling thread. */ struct zzuf_curl_driver { /** * The daemon to drive with MHD_run(), NULL for daemons with an internal * polling thread. */ struct MHD_Daemon *d_extern; /** * The "multi" handle, only used when @e d_extern is not NULL. */ CURLM *multi; }; /** * Initialise the driver. * * @param[out] drv the driver to initialise * @param d_extern the daemon to drive, or NULL * @return non-zero on success, zero on failure */ int zzuf_curl_driver_init (struct zzuf_curl_driver *drv, struct MHD_Daemon *d_extern); /** * Release the resources of the driver. * * @param drv the driver to clean up */ void zzuf_curl_driver_deinit (struct zzuf_curl_driver *drv); /** * Run a single transfer to completion (or to the timeout). * * The result of the transfer is deliberately ignored: with the traffic * being fuzzed, libcurl may fail in arbitrary ways. * * @param drv the driver * @param c the "easy" handle to run */ void zzuf_curl_driver_perform (struct zzuf_curl_driver *drv, CURL *c); #endif /* MHD_ZZUF_COMMON_H */