/* This file is part of libhttpserver Copyright (C) 2011-2019 Sebastiano Merlino 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 */ #if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) #error "Only or can be included directly." #endif #ifndef SRC_HTTPSERVER_HTTP_UTILS_HPP_ #define SRC_HTTPSERVER_HTTP_UTILS_HPP_ // Backend headers (libmicrohttpd, GnuTLS, BSD-socket) // are deliberately NOT included from this public header. // The enums declared below previously took their integer values from // upstream macros (MHD_USE_*, MHD_DIGEST_AUTH_ALGO3_*, MHD_DAUTH_*, // GNUTLS_CRD_*), dragging the backend headers through the umbrella to // every downstream consumer. Those values are now hard-coded to match // the stable upstream ABI; static_assert blocks in src/webserver.cpp // and src/http_request.cpp (where the upstream headers are reachable) // pin the enum values to the upstream macros so any renumber breaks // the build at the right place. `struct sockaddr` is forward-declared // at file scope; the implementations live in src/http_utils.cpp where // the BSD-socket header is included directly. // needed to force Vista as a bare minimum to have inet_ntop (libmicro defines // this to include XP support as a lower version). #if defined(__MINGW32__) || defined(__CYGWIN32__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 #endif #include #include #include #include #include #include #include #include #include "httpserver/constants.hpp" #include "httpserver/http_arg_value.hpp" // Forward-declare the BSD-socket address family. Only pointer-to-incomplete // uses appear in this header; the .cpp side includes the BSD-socket header // directly. MinGW/Windows uses winsock2.h; the same forward declaration is // valid there since the canonical name `struct sockaddr` is the same across // POSIX and Win32. struct sockaddr; namespace httpserver { enum file_upload_target_T { FILE_UPLOAD_MEMORY_ONLY, FILE_UPLOAD_DISK_ONLY, FILE_UPLOAD_MEMORY_AND_DISK, }; typedef void(*unescaper_ptr)(std::string&); namespace http { struct generateFilenameException : public std::exception { public: explicit generateFilenameException(const std::string& message) noexcept : error_message(message) { } const char* what() const noexcept { return error_message.c_str(); } private: std::string error_message; }; class http_utils { public: // Hard-coded values matching gnutls_credentials_type_t // (a stable public ABI). Values pinned to the GnuTLS macros via // static_assert in src/webserver.cpp so an upstream renumber would // break the build. The enum is unconditional (public-header // declarations are never gated on HAVE_GNUTLS); when // GnuTLS is disabled at build time, selecting one of these via // create_webserver().cred_type(...) hits the existing // feature-unavailable path on use_ssl(true). enum cred_type_T { NONE = -1, CERTIFICATE = 1, // GNUTLS_CRD_CERTIFICATE ANON = 2, // GNUTLS_CRD_ANON SRP = 3, // GNUTLS_CRD_SRP PSK = 4, // GNUTLS_CRD_PSK IA = 5 // GNUTLS_CRD_IA }; // Hard-coded values mirroring libmicrohttpd's MHD_FLAG enum // (MHD_USE_AUTO=65536, MHD_USE_SELECT_INTERNALLY=8, // MHD_USE_THREAD_PER_CONNECTION=4). Pinned to the upstream macros // via static_assert in src/webserver.cpp so an upstream renumber // breaks the build at the right place rather than silently mis-routing // start-mode selection. enum start_method_T { INTERNAL_SELECT = 65544, // MHD_USE_SELECT_INTERNALLY | MHD_USE_AUTO THREAD_PER_CONNECTION = 65540, // MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO EXTERNAL_SELECT = 65536 // MHD_USE_AUTO }; enum policy_T { ACCEPT, REJECT }; enum IP_version_T { IPV4 = 4, IPV6 = 16 }; // Hard-coded values mirroring libmicrohttpd's // MHD_DigestAuthAlgo3 / MHD_DigestAuthResult enums. The integer // values follow upstream's bitfield encoding // (BASE_ALGO_X | NON_SESSION = X | 64 for the algorithms; the // result codes are stable signed ints). Pinned via static_assert // in src/http_request.cpp where is reachable, so // an upstream renumber breaks the build at the right place. // The enums are unconditional (public-header declarations are // never gated on HAVE_DAUTH); when digest auth is // disabled at build time, the corresponding methods on // http_request hit the existing feature-unavailable path. enum class digest_algorithm { MD5 = 65, // MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION SHA256 = 66, // MHD_DIGEST_BASE_ALGO_SHA256 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION SHA512_256 = 68 // MHD_DIGEST_BASE_ALGO_SHA512_256 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION }; enum class digest_auth_result { OK = 1, // MHD_DAUTH_OK // GENERIC_ERROR (not "ERROR") because on Windows // unconditionally `#define`s ERROR to 0, which the preprocessor // expands inside scoped-enum bodies just like anywhere else. GENERIC_ERROR = 0, // MHD_DAUTH_ERROR ("general error") WRONG_HEADER = -1, // MHD_DAUTH_WRONG_HEADER WRONG_USERNAME = -2, // MHD_DAUTH_WRONG_USERNAME WRONG_REALM = -3, // MHD_DAUTH_WRONG_REALM WRONG_URI = -4, // MHD_DAUTH_WRONG_URI WRONG_QOP = -5, // MHD_DAUTH_WRONG_QOP WRONG_ALGO = -6, // MHD_DAUTH_WRONG_ALGO TOO_LARGE = -15, // MHD_DAUTH_TOO_LARGE NONCE_STALE = -17, // MHD_DAUTH_NONCE_STALE NONCE_OTHER_COND = -18, // MHD_DAUTH_NONCE_OTHER_COND NONCE_WRONG = -33, // MHD_DAUTH_NONCE_WRONG RESPONSE_WRONG = -34 // MHD_DAUTH_RESPONSE_WRONG }; static constexpr size_t md5_digest_size = 16; static constexpr size_t sha256_digest_size = 32; static constexpr size_t sha512_256_digest_size = 32; static const uint16_t http_method_connect_code; static const uint16_t http_method_delete_code; static const uint16_t http_method_get_code; static const uint16_t http_method_head_code; static const uint16_t http_method_options_code; static const uint16_t http_method_post_code; static const uint16_t http_method_put_code; static const uint16_t http_method_trace_code; static const uint16_t http_method_patch_code; static const uint16_t http_method_unknown_code; static const int http_continue; static const int http_switching_protocol; static const int http_processing; static const int http_ok; static const int http_created; static const int http_accepted; static const int http_non_authoritative_information; static const int http_no_content; static const int http_reset_content; static const int http_partial_content; static const int http_multi_status; static const int http_multiple_choices; static const int http_moved_permanently; static const int http_found; static const int http_see_other; static const int http_not_modified; static const int http_use_proxy; static const int http_switch_proxy; static const int http_temporary_redirect; static const int http_bad_request; static const int http_unauthorized; static const int http_payment_required; static const int http_forbidden; static const int http_not_found; static const int http_method_not_allowed; static const int http_method_not_acceptable; static const int http_proxy_authentication_required; static const int http_request_timeout; static const int http_conflict; static const int http_gone; static const int http_length_required; static const int http_precondition_failed; static const int http_request_entity_too_large; static const int http_request_uri_too_long; static const int http_unsupported_media_type; static const int http_requested_range_not_satisfiable; static const int http_expectation_failed; static const int http_unprocessable_entity; static const int http_locked; static const int http_failed_dependency; static const int http_upgrade_required; static const int http_retry_with; static const int http_internal_server_error; static const int http_not_implemented; static const int http_bad_gateway; static const int http_service_unavailable; static const int http_gateway_timeout; static const int http_version_not_supported; static const int http_variant_also_negotiated; static const int http_insufficient_storage; static const int http_bandwidth_limit_exceeded; static const int http_not_extended; static const int shoutcast_response; // See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html static const char* http_header_accept; static const char* http_header_accept_charset; static const char* http_header_accept_encoding; static const char* http_header_accept_language; static const char* http_header_accept_ranges; static const char* http_header_age; static const char* http_header_allow; static const char* http_header_authorization; static const char* http_header_cache_control; static const char* http_header_connection; static const char* http_header_content_encoding; static const char* http_header_content_language; static const char* http_header_content_length; static const char* http_header_content_location; static const char* http_header_content_md5; static const char* http_header_content_range; static const char* http_header_content_type; static const char* http_header_date; static const char* http_header_etag; static const char* http_header_expect; static const char* http_header_expires; static const char* http_header_from; static const char* http_header_host; static const char* http_header_if_match; static const char* http_header_if_modified_since; static const char* http_header_if_none_match; static const char* http_header_if_range; static const char* http_header_if_unmodified_since; static const char* http_header_last_modified; static const char* http_header_location; static const char* http_header_max_forwards; static const char* http_header_pragma; static const char* http_header_proxy_authenticate; static const char* http_header_proxy_authentication; static const char* http_header_range; static const char* http_header_referer; static const char* http_header_retry_after; static const char* http_header_server; static const char* http_header_te; static const char* http_header_trailer; static const char* http_header_transfer_encoding; static const char* http_header_upgrade; static const char* http_header_user_agent; static const char* http_header_vary; static const char* http_header_via; static const char* http_header_warning; static const char* http_header_www_authenticate; static const char* http_version_1_0; static const char* http_version_1_1; static const char* http_method_connect; static const char* http_method_delete; static const char* http_method_head; static const char* http_method_get; static const char* http_method_options; static const char* http_method_post; static const char* http_method_put; static const char* http_method_trace; static const char* http_method_patch; static const char* http_post_encoding_form_urlencoded; static const char* http_post_encoding_multipart_formdata; static const char* application_octet_stream; static const char* text_plain; static const char* upload_filename_template; static const char path_separator; static std::vector tokenize_url(const std::string&, const char separator = '/'); static std::string standardize_url(const std::string&); static const std::string generate_random_upload_filename(const std::string& directory); static std::string sanitize_upload_filename(const std::string& filename); static const char* reason_phrase(unsigned int status_code); // The parameter is the integer value of an `MHD_FEATURE` // enumerator. The signature is `int` rather than `enum MHD_FEATURE` // so this header doesn't have to drag in ; callers // can still pass an `MHD_FEATURE_*` enumerator directly because the // enum's underlying type implicitly converts to int. static bool is_feature_supported(int feature); static const char* get_mhd_version(); }; // Body of a strict-weak-ordering "less than": compares LENGTH FIRST // (shorter sorts before longer), then per-character under `op`. This is // NOT plain lexicographic order ("b" sorts before "aa"). WARNING: the // macro expands to multiple `return` statements at the call site, so it // must be the entire body of a bool-returning function. #define COMPARATOR(x, y, op) { \ size_t l1 = (x).size(); \ size_t l2 = (y).size(); \ if (l1 < l2) return true; \ if (l1 > l2) return false; \ \ for (size_t n = 0; n < l1; n++) { \ int xc = op((x)[n]); \ int yc = op((y)[n]); \ if (xc < yc) return true; \ if (xc > yc) return false; \ } \ return false; \ } // ASCII-only uppercase helper for header name comparison. // HTTP header names are defined to be US-ASCII (RFC 7230 ยง3.2), so using // the locale-aware std::toupper on every character is unnecessary overhead. // This branchless helper folds a-z to A-Z and leaves all other bytes unchanged. // It is intentionally NOT locale-aware: non-ASCII bytes pass through as-is. inline int http_header_toupper(char c) { return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : static_cast(c); } class header_comparator { public: // is_transparent enables heterogeneous lookup against header_map // (std::map): callers // can pass std::string_view directly to find()/count() without // constructing a std::string. Required by the // string_view-returning const accessors on http_response. using is_transparent = std::true_type; /** * Case-insensitive less-than comparison. * @param x first string to compare. * @param y second string to compare. * @return `true` iff @p x sorts before @p y under case-insensitive * length-first-then-per-character order (shorter names * sort first; equal lengths compare character by * character after ASCII upcasing). **/ bool operator()(std::string_view x, std::string_view y) const { COMPARATOR(x, y, http_header_toupper); } /// @copydoc operator()(std::string_view, std::string_view) const bool operator()(const std::string& x, const std::string& y) const { COMPARATOR(x, y, http_header_toupper); } }; /** * Operator Class that is used to compare two strings. The comparison can be sensitive or insensitive. * The default comparison is case sensitive. To obtain insensitive comparison you have to pass in * compilation phase the flag CASE_INSENSITIVE to the preprocessor. **/ class arg_comparator { public: using is_transparent = std::true_type; /** * Less-than comparison between two argument keys. * * Length-first-then-per-character order (shorter keys sort * first; equal lengths compare byte by byte). Defaults to * case-sensitive comparison; if the library was built with * `-DCASE_INSENSITIVE` it folds upper-case before comparing, * matching arg_map's documented contract. * * @param x first string to compare. * @param y second string to compare. * @return `true` iff @p x sorts before @p y under the active mode. **/ bool operator()(std::string_view x, std::string_view y) const { #ifdef CASE_INSENSITIVE COMPARATOR(x, y, std::toupper); #else COMPARATOR(x, y,); // NOLINT(whitespace/comma) #endif } /// @copydoc operator()(std::string_view, std::string_view) const bool operator()(const std::string& x, const std::string& y) const { return operator()(std::string_view(x), std::string_view(y)); } /// @copydoc operator()(std::string_view, std::string_view) const bool operator()(std::string_view x, const std::string& y) const { return operator()(x, std::string_view(y)); } /// @copydoc operator()(std::string_view, std::string_view) const bool operator()(const std::string& x, std::string_view y) const { return operator()(std::string_view(x), y); } }; using header_map = std::map; // WARNING: header_view_map keys and values are non-owning views (std::string_view). // Callers MUST NOT store a header_view_map beyond the lifetime of the header_map // whose strings it views, and MUST NOT mutate that source map while any view is // in use. Storing a header_view_map across response mutations is a use-after-free // bug (CWE-416). This type is used internally for diagnostic formatting only. using header_view_map = std::map; using arg_map = std::map; using arg_view_map = std::map; } // namespace http } // namespace httpserver // ip_representation lives in its own header; included here so existing // consumers of still see the type. #include "httpserver/ip_representation.hpp" #include "httpserver/http_utils_helpers.hpp" #endif // SRC_HTTPSERVER_HTTP_UTILS_HPP_