From b7c8ed7e3ab11f29b59dea65893df3b63f8c3755 Mon Sep 17 00:00:00 2001 From: Sergey Kuznetsov Date: Tue, 13 Aug 2024 17:26:47 +0100 Subject: [PATCH] refactor: Create interface for DOSGuard (#1602) For #919. --- src/app/ClioApplication.cpp | 12 +- src/rpc/RPCEngine.hpp | 8 +- src/web/CMakeLists.txt | 11 +- src/web/HttpSession.hpp | 4 +- src/web/PlainWsSession.hpp | 8 +- src/web/Server.hpp | 12 +- src/web/SslHttpSession.hpp | 5 +- src/web/SslWsSession.hpp | 8 +- src/web/dosguard/DOSGuard.cpp | 148 ++++++++++++++++++ src/web/{ => dosguard}/DOSGuard.hpp | 140 ++--------------- src/web/dosguard/DOSGuardInterface.hpp | 110 +++++++++++++ .../{ => dosguard}/IntervalSweepHandler.cpp | 10 +- .../{ => dosguard}/IntervalSweepHandler.hpp | 10 +- src/web/dosguard/WhitelistHandler.cpp | 118 ++++++++++++++ src/web/{ => dosguard}/WhitelistHandler.hpp | 81 ++-------- .../dosguard/WhitelistHandlerInterface.hpp | 44 ++++++ src/web/impl/HttpBase.hpp | 6 +- src/web/impl/WsBase.hpp | 6 +- src/web/ng/Server.cpp | 0 src/web/ng/Server.hpp | 0 tests/common/rpc/FakesAndMocks.hpp | 1 - tests/unit/CMakeLists.txt | 6 +- tests/unit/web/ServerTests.cpp | 21 +-- .../unit/{ => web/dosguard}/DOSGuardTests.cpp | 26 ++- .../IntervalSweepHandlerTests.cpp | 6 +- .../{ => dosguard}/WhitelistHandlerTests.cpp | 4 +- 26 files changed, 535 insertions(+), 270 deletions(-) create mode 100644 src/web/dosguard/DOSGuard.cpp rename src/web/{ => dosguard}/DOSGuard.hpp (50%) create mode 100644 src/web/dosguard/DOSGuardInterface.hpp rename src/web/{ => dosguard}/IntervalSweepHandler.cpp (89%) rename src/web/{ => dosguard}/IntervalSweepHandler.hpp (88%) create mode 100644 src/web/dosguard/WhitelistHandler.cpp rename src/web/{ => dosguard}/WhitelistHandler.hpp (66%) create mode 100644 src/web/dosguard/WhitelistHandlerInterface.hpp create mode 100644 src/web/ng/Server.cpp create mode 100644 src/web/ng/Server.hpp rename tests/unit/{ => web/dosguard}/DOSGuardTests.cpp (88%) rename tests/unit/web/{ => dosguard}/IntervalSweepHandlerTests.cpp (93%) rename tests/unit/web/{ => dosguard}/WhitelistHandlerTests.cpp (98%) diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 13db52aaf..fdb9dbe68 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -33,11 +33,11 @@ #include "util/config/Config.hpp" #include "util/log/Logger.hpp" #include "util/prometheus/Prometheus.hpp" -#include "web/DOSGuard.hpp" -#include "web/IntervalSweepHandler.hpp" #include "web/RPCServerHandler.hpp" #include "web/Server.hpp" -#include "web/WhitelistHandler.hpp" +#include "web/dosguard/DOSGuard.hpp" +#include "web/dosguard/IntervalSweepHandler.hpp" +#include "web/dosguard/WhitelistHandler.hpp" #include @@ -93,9 +93,9 @@ ClioApplication::run() boost::asio::io_context ioc{threads}; // Rate limiter, to prevent abuse - auto whitelistHandler = web::WhitelistHandler{config_}; - auto dosGuard = web::DOSGuard{config_, whitelistHandler}; - auto sweepHandler = web::IntervalSweepHandler{config_, ioc, dosGuard}; + auto whitelistHandler = web::dosguard::WhitelistHandler{config_}; + auto dosGuard = web::dosguard::DOSGuard{config_, whitelistHandler}; + auto sweepHandler = web::dosguard::IntervalSweepHandler{config_, ioc, dosGuard}; // Interface to the database auto backend = data::make_Backend(config_); diff --git a/src/rpc/RPCEngine.hpp b/src/rpc/RPCEngine.hpp index 2aa42ad3c..77ef4fc8d 100644 --- a/src/rpc/RPCEngine.hpp +++ b/src/rpc/RPCEngine.hpp @@ -28,7 +28,7 @@ #include "rpc/common/impl/ForwardingProxy.hpp" #include "util/log/Logger.hpp" #include "web/Context.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include #include @@ -65,7 +65,7 @@ class RPCEngine { util::Logger log_{"RPC"}; std::shared_ptr backend_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; std::reference_wrapper workQueue_; std::reference_wrapper counters_; @@ -87,7 +87,7 @@ class RPCEngine { RPCEngine( std::shared_ptr const& backend, std::shared_ptr const& balancer, - web::DOSGuard const& dosGuard, + web::dosguard::DOSGuardInterface const& dosGuard, WorkQueue& workQueue, Counters& counters, std::shared_ptr const& handlerProvider @@ -116,7 +116,7 @@ class RPCEngine { make_RPCEngine( std::shared_ptr const& backend, std::shared_ptr const& balancer, - web::DOSGuard const& dosGuard, + web::dosguard::DOSGuardInterface const& dosGuard, WorkQueue& workQueue, Counters& counters, std::shared_ptr const& handlerProvider diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index fa33c2bcf..cdf882ece 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -1,8 +1,15 @@ add_library(clio_web) target_sources( - clio_web PRIVATE impl/AdminVerificationStrategy.cpp impl/ServerSslContext.cpp IntervalSweepHandler.cpp Resolver.cpp - Server.cpp + clio_web + PRIVATE Resolver.cpp + Server.cpp + dosguard/DOSGuard.cpp + dosguard/IntervalSweepHandler.cpp + dosguard/WhitelistHandler.cpp + impl/AdminVerificationStrategy.cpp + impl/ServerSslContext.cpp + ng/Server.cpp ) target_link_libraries(clio_web PUBLIC clio_util) diff --git a/src/web/HttpSession.hpp b/src/web/HttpSession.hpp index 861052fba..0f01da376 100644 --- a/src/web/HttpSession.hpp +++ b/src/web/HttpSession.hpp @@ -20,8 +20,8 @@ #pragma once #include "util/Taggable.hpp" -#include "web/DOSGuard.hpp" #include "web/PlainWsSession.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/HttpBase.hpp" #include "web/interface/ConnectionBase.hpp" @@ -70,7 +70,7 @@ class HttpSession : public impl::HttpBase, std::string const& ip, std::shared_ptr const& adminVerification, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer buffer ) diff --git a/src/web/PlainWsSession.hpp b/src/web/PlainWsSession.hpp index f0a04d86b..1d2da951e 100644 --- a/src/web/PlainWsSession.hpp +++ b/src/web/PlainWsSession.hpp @@ -20,7 +20,7 @@ #pragma once #include "util/Taggable.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/WsBase.hpp" #include "web/interface/ConnectionBase.hpp" @@ -68,7 +68,7 @@ class PlainWsSession : public impl::WsBase { boost::asio::ip::tcp::socket&& socket, std::string ip, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer&& buffer, bool isAdmin @@ -102,7 +102,7 @@ class WsUpgrader : public std::enable_shared_from_this> boost::optional> parser_; boost::beast::flat_buffer buffer_; std::reference_wrapper tagFactory_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; http::request req_; std::string ip_; std::shared_ptr const handler_; @@ -125,7 +125,7 @@ class WsUpgrader : public std::enable_shared_from_this> boost::beast::tcp_stream&& stream, std::string ip, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer&& buffer, http::request request, diff --git a/src/web/Server.hpp b/src/web/Server.hpp index 76a0c8120..4c57c88ed 100644 --- a/src/web/Server.hpp +++ b/src/web/Server.hpp @@ -21,9 +21,9 @@ #include "util/Taggable.hpp" #include "util/log/Logger.hpp" -#include "web/DOSGuard.hpp" #include "web/HttpSession.hpp" #include "web/SslHttpSession.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/ServerSslContext.hpp" #include "web/interface/Concepts.hpp" @@ -91,7 +91,7 @@ class Detector : public std::enable_shared_from_this> ctx_; std::reference_wrapper tagFactory_; - std::reference_wrapper const dosGuard_; + std::reference_wrapper const dosGuard_; std::shared_ptr const handler_; boost::beast::flat_buffer buffer_; std::shared_ptr const adminVerification_; @@ -111,7 +111,7 @@ class Detector : public std::enable_shared_from_this> ctx, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr handler, std::shared_ptr adminVerification ) @@ -213,7 +213,7 @@ class Server : public std::enable_shared_from_this ioc_; std::optional ctx_; util::TagDecoratorFactory tagFactory_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; std::shared_ptr handler_; tcp::acceptor acceptor_; std::shared_ptr adminVerification_; @@ -235,7 +235,7 @@ class Server : public std::enable_shared_from_this ctx, tcp::endpoint endpoint, util::TagDecoratorFactory tagFactory, - web::DOSGuard& dosGuard, + dosguard::DOSGuardInterface& dosGuard, std::shared_ptr handler, std::optional adminPassword ) @@ -327,7 +327,7 @@ static std::shared_ptr> make_HttpServer( util::Config const& config, boost::asio::io_context& ioc, - web::DOSGuard& dosGuard, + dosguard::DOSGuardInterface& dosGuard, std::shared_ptr const& handler ) { diff --git a/src/web/SslHttpSession.hpp b/src/web/SslHttpSession.hpp index f979b0960..d5590aba8 100644 --- a/src/web/SslHttpSession.hpp +++ b/src/web/SslHttpSession.hpp @@ -20,9 +20,10 @@ #pragma once #include "util/Taggable.hpp" -#include "web/DOSGuard.hpp" #include "web/SslWsSession.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/HttpBase.hpp" +#include "web/interface/Concepts.hpp" #include "web/interface/ConnectionBase.hpp" #include @@ -78,7 +79,7 @@ class SslHttpSession : public impl::HttpBase, std::shared_ptr const& adminVerification, boost::asio::ssl::context& ctx, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer buffer ) diff --git a/src/web/SslWsSession.hpp b/src/web/SslWsSession.hpp index 968347ae2..1e33fc590 100644 --- a/src/web/SslWsSession.hpp +++ b/src/web/SslWsSession.hpp @@ -20,7 +20,7 @@ #pragma once #include "util/Taggable.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/WsBase.hpp" #include "web/interface/ConnectionBase.hpp" @@ -69,7 +69,7 @@ class SslWsSession : public impl::WsBase { boost::beast::ssl_stream&& stream, std::string ip, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer&& buffer, bool isAdmin @@ -102,7 +102,7 @@ class SslWsUpgrader : public std::enable_shared_from_this tagFactory_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; std::shared_ptr const handler_; http::request req_; bool isAdmin_; @@ -124,7 +124,7 @@ class SslWsUpgrader : public std::enable_shared_from_this stream, std::string ip, std::reference_wrapper tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr handler, boost::beast::flat_buffer&& buffer, http::request request, diff --git a/src/web/dosguard/DOSGuard.cpp b/src/web/dosguard/DOSGuard.cpp new file mode 100644 index 000000000..f3b529611 --- /dev/null +++ b/src/web/dosguard/DOSGuard.cpp @@ -0,0 +1,148 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "web/dosguard/DOSGuard.hpp" + +#include "util/Assert.hpp" +#include "util/config/Config.hpp" +#include "util/log/Logger.hpp" +#include "web/dosguard/WhitelistHandlerInterface.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace web::dosguard { + +DOSGuard::DOSGuard(util::Config const& config, WhitelistHandlerInterface const& whitelistHandler) + : whitelistHandler_{std::cref(whitelistHandler)} + , maxFetches_{config.valueOr("dos_guard.max_fetches", DEFAULT_MAX_FETCHES)} + , maxConnCount_{config.valueOr("dos_guard.max_connections", DEFAULT_MAX_CONNECTIONS)} + , maxRequestCount_{config.valueOr("dos_guard.max_requests", DEFAULT_MAX_REQUESTS)} +{ +} + +[[nodiscard]] bool +DOSGuard::isWhiteListed(std::string_view const ip) const noexcept +{ + return whitelistHandler_.get().isWhiteListed(ip); +} + +[[nodiscard]] bool +DOSGuard::isOk(std::string const& ip) const noexcept +{ + if (whitelistHandler_.get().isWhiteListed(ip)) + return true; + + { + std::scoped_lock const lck(mtx_); + if (ipState_.find(ip) != ipState_.end()) { + auto [transferedByte, requests] = ipState_.at(ip); + if (transferedByte > maxFetches_ || requests > maxRequestCount_) { + LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip + << " Transfered Byte: " << transferedByte << "; Requests: " << requests; + return false; + } + } + auto it = ipConnCount_.find(ip); + if (it != ipConnCount_.end()) { + if (it->second > maxConnCount_) { + LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip + << " Concurrent connection: " << it->second; + return false; + } + } + } + return true; +} + +void +DOSGuard::increment(std::string const& ip) noexcept +{ + if (whitelistHandler_.get().isWhiteListed(ip)) + return; + std::scoped_lock const lck{mtx_}; + ipConnCount_[ip]++; +} + +void +DOSGuard::decrement(std::string const& ip) noexcept +{ + if (whitelistHandler_.get().isWhiteListed(ip)) + return; + std::scoped_lock const lck{mtx_}; + ASSERT(ipConnCount_[ip] > 0, "Connection count for ip {} can't be 0", ip); + ipConnCount_[ip]--; + if (ipConnCount_[ip] == 0) + ipConnCount_.erase(ip); +} + +[[maybe_unused]] bool +DOSGuard::add(std::string const& ip, uint32_t numObjects) noexcept +{ + if (whitelistHandler_.get().isWhiteListed(ip)) + return true; + + { + std::scoped_lock const lck(mtx_); + ipState_[ip].transferedByte += numObjects; + } + + return isOk(ip); +} + +[[maybe_unused]] bool +DOSGuard::request(std::string const& ip) noexcept +{ + if (whitelistHandler_.get().isWhiteListed(ip)) + return true; + + { + std::scoped_lock const lck(mtx_); + ipState_[ip].requestsCount++; + } + + return isOk(ip); +} + +void +DOSGuard::clear() noexcept +{ + std::scoped_lock const lck(mtx_); + ipState_.clear(); +} + +[[nodiscard]] std::unordered_set +DOSGuard::getWhitelist(util::Config const& config) +{ + using T = std::unordered_set const; + auto whitelist = config.arrayOr("dos_guard.whitelist", {}); + auto const transform = [](auto const& elem) { return elem.template value(); }; + return T{ + boost::transform_iterator(std::begin(whitelist), transform), + boost::transform_iterator(std::end(whitelist), transform) + }; +} + +} // namespace web::dosguard diff --git a/src/web/DOSGuard.hpp b/src/web/dosguard/DOSGuard.hpp similarity index 50% rename from src/web/DOSGuard.hpp rename to src/web/dosguard/DOSGuard.hpp index 2118bb821..79798f052 100644 --- a/src/web/DOSGuard.hpp +++ b/src/web/dosguard/DOSGuard.hpp @@ -19,11 +19,10 @@ #pragma once -#include "util/Assert.hpp" #include "util/config/Config.hpp" #include "util/log/Logger.hpp" -#include "web/IntervalSweepHandler.hpp" -#include "web/WhitelistHandler.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" +#include "web/dosguard/WhitelistHandlerInterface.hpp" #include #include @@ -31,48 +30,32 @@ #include #include -#include #include #include #include #include #include -namespace web { - -/** - * @brief The interface of a denial of service guard. - */ -class BaseDOSGuard { -public: - virtual ~BaseDOSGuard() = default; - - /** - * @brief Clears implementation-defined counters. - */ - virtual void - clear() noexcept = 0; -}; +namespace web::dosguard { /** * @brief A simple denial of service guard used for rate limiting. * * @tparam WhitelistHandlerType The type of the whitelist handler */ -template -class BasicDOSGuard : public BaseDOSGuard { +class DOSGuard : public DOSGuardInterface { /** * @brief Accumulated state per IP, state will be reset accordingly */ struct ClientState { - std::uint32_t transferedByte = 0; /**< Accumulated transfered byte */ + std::uint32_t transferedByte = 0; /**< Accumulated transferred byte */ std::uint32_t requestsCount = 0; /**< Accumulated served requests count */ }; mutable std::mutex mtx_; std::unordered_map ipState_; std::unordered_map ipConnCount_; - std::reference_wrapper whitelistHandler_; + std::reference_wrapper whitelistHandler_; std::uint32_t const maxFetches_; std::uint32_t const maxConnCount_; @@ -90,13 +73,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @param config Clio config * @param whitelistHandler Whitelist handler that checks whitelist for IP addresses */ - BasicDOSGuard(util::Config const& config, WhitelistHandlerType const& whitelistHandler) - : whitelistHandler_{std::cref(whitelistHandler)} - , maxFetches_{config.valueOr("dos_guard.max_fetches", DEFAULT_MAX_FETCHES)} - , maxConnCount_{config.valueOr("dos_guard.max_connections", DEFAULT_MAX_CONNECTIONS)} - , maxRequestCount_{config.valueOr("dos_guard.max_requests", DEFAULT_MAX_REQUESTS)} - { - } + DOSGuard(util::Config const& config, WhitelistHandlerInterface const& whitelistHandler); /** * @brief Check whether an ip address is in the whitelist or not. @@ -106,10 +83,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @return false */ [[nodiscard]] bool - isWhiteListed(std::string_view const ip) const noexcept - { - return whitelistHandler_.get().isWhiteListed(ip); - } + isWhiteListed(std::string_view const ip) const noexcept override; /** * @brief Check whether an ip address is currently rate limited or not. @@ -119,32 +93,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @return false If rate limited and the request should not be processed */ [[nodiscard]] bool - isOk(std::string const& ip) const noexcept - { - if (whitelistHandler_.get().isWhiteListed(ip)) - return true; - - { - std::scoped_lock const lck(mtx_); - if (ipState_.find(ip) != ipState_.end()) { - auto [transferedByte, requests] = ipState_.at(ip); - if (transferedByte > maxFetches_ || requests > maxRequestCount_) { - LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip - << " Transfered Byte: " << transferedByte << "; Requests: " << requests; - return false; - } - } - auto it = ipConnCount_.find(ip); - if (it != ipConnCount_.end()) { - if (it->second > maxConnCount_) { - LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip - << " Concurrent connection: " << it->second; - return false; - } - } - } - return true; - } + isOk(std::string const& ip) const noexcept override; /** * @brief Increment connection count for the given ip address. @@ -152,13 +101,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @param ip */ void - increment(std::string const& ip) noexcept - { - if (whitelistHandler_.get().isWhiteListed(ip)) - return; - std::scoped_lock const lck{mtx_}; - ipConnCount_[ip]++; - } + increment(std::string const& ip) noexcept override; /** * @brief Decrement connection count for the given ip address. @@ -166,16 +109,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @param ip */ void - decrement(std::string const& ip) noexcept - { - if (whitelistHandler_.get().isWhiteListed(ip)) - return; - std::scoped_lock const lck{mtx_}; - ASSERT(ipConnCount_[ip] > 0, "Connection count for ip {} can't be 0", ip); - ipConnCount_[ip]--; - if (ipConnCount_[ip] == 0) - ipConnCount_.erase(ip); - } + decrement(std::string const& ip) noexcept override; /** * @brief Adds numObjects of usage for the given ip address. @@ -190,18 +124,7 @@ class BasicDOSGuard : public BaseDOSGuard { * @return false */ [[maybe_unused]] bool - add(std::string const& ip, uint32_t numObjects) noexcept - { - if (whitelistHandler_.get().isWhiteListed(ip)) - return true; - - { - std::scoped_lock const lck(mtx_); - ipState_[ip].transferedByte += numObjects; - } - - return isOk(ip); - } + add(std::string const& ip, uint32_t numObjects) noexcept override; /** * @brief Adds one request for the given ip address. @@ -215,46 +138,17 @@ class BasicDOSGuard : public BaseDOSGuard { * @return false */ [[maybe_unused]] bool - request(std::string const& ip) noexcept - { - if (whitelistHandler_.get().isWhiteListed(ip)) - return true; - - { - std::scoped_lock const lck(mtx_); - ipState_[ip].requestsCount++; - } - - return isOk(ip); - } + request(std::string const& ip) noexcept override; /** * @brief Instantly clears all fetch counters added by @see add(std::string const&, uint32_t). */ void - clear() noexcept override - { - std::scoped_lock const lck(mtx_); - ipState_.clear(); - } + clear() noexcept override; private: - [[nodiscard]] std::unordered_set - getWhitelist(util::Config const& config) const - { - using T = std::unordered_set const; - auto whitelist = config.arrayOr("dos_guard.whitelist", {}); - auto const transform = [](auto const& elem) { return elem.template value(); }; - return T{ - boost::transform_iterator(std::begin(whitelist), transform), - boost::transform_iterator(std::end(whitelist), transform) - }; - } + [[nodiscard]] static std::unordered_set + getWhitelist(util::Config const& config); }; -/** - * @brief A simple denial of service guard used for rate limiting. - */ -using DOSGuard = BasicDOSGuard; - -} // namespace web +} // namespace web::dosguard diff --git a/src/web/dosguard/DOSGuardInterface.hpp b/src/web/dosguard/DOSGuardInterface.hpp new file mode 100644 index 000000000..1eee27b65 --- /dev/null +++ b/src/web/dosguard/DOSGuardInterface.hpp @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include +#include +#include +namespace web::dosguard { + +/** + * @brief The interface of a denial of service guard. + */ +class BaseDOSGuard { +public: + virtual ~BaseDOSGuard() = default; + + /** + * @brief Clears implementation-defined counters. + */ + virtual void + clear() noexcept = 0; +}; + +/** + * @brief The interface of a denial of service guard. + */ +class DOSGuardInterface : public BaseDOSGuard { +public: + /** + * @brief Check whether an ip address is in the whitelist or not. + * + * @param ip The ip address to check + * @return true + * @return false + */ + [[nodiscard]] virtual bool + isWhiteListed(std::string_view const ip) const noexcept = 0; + + /** + * @brief Check whether an ip address is currently rate limited or not. + * + * @param ip The ip address to check + * @return true If not rate limited + * @return false If rate limited and the request should not be processed + */ + [[nodiscard]] virtual bool + isOk(std::string const& ip) const noexcept = 0; + + /** + * @brief Increment connection count for the given ip address. + * + * @param ip + */ + virtual void + increment(std::string const& ip) noexcept = 0; + + /** + * @brief Decrement connection count for the given ip address. + * + * @param ip + */ + virtual void + decrement(std::string const& ip) noexcept = 0; + + /** + * @brief Adds numObjects of usage for the given ip address. + * + * If the total sums up to a value equal or larger than maxFetches_ + * the operation is no longer allowed and false is returned; true is + * returned otherwise. + * + * @param ip + * @param numObjects + * @return true + * @return false + */ + [[maybe_unused]] virtual bool + add(std::string const& ip, uint32_t numObjects) noexcept = 0; + + /** + * @brief Adds one request for the given ip address. + * + * + * @param ip + * @return If the total sums up to a value equal or larger than maxRequestCount_ + * the operation is no longer allowed and false is returned; true is + * returned otherwise. + */ + [[maybe_unused]] virtual bool + request(std::string const& ip) noexcept = 0; +}; + +} // namespace web::dosguard diff --git a/src/web/IntervalSweepHandler.cpp b/src/web/dosguard/IntervalSweepHandler.cpp similarity index 89% rename from src/web/IntervalSweepHandler.cpp rename to src/web/dosguard/IntervalSweepHandler.cpp index 65e43b3c2..55edf7d4e 100644 --- a/src/web/IntervalSweepHandler.cpp +++ b/src/web/dosguard/IntervalSweepHandler.cpp @@ -17,10 +17,10 @@ */ //============================================================================== -#include "web/IntervalSweepHandler.hpp" +#include "web/dosguard/IntervalSweepHandler.hpp" #include "util/config/Config.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include #include @@ -29,12 +29,12 @@ #include #include -namespace web { +namespace web::dosguard { IntervalSweepHandler::IntervalSweepHandler( util::Config const& config, boost::asio::io_context& ctx, - web::BaseDOSGuard& dosGuard + BaseDOSGuard& dosGuard ) : repeat_{std::ref(ctx)} { @@ -44,4 +44,4 @@ IntervalSweepHandler::IntervalSweepHandler( repeat_.start(sweepInterval, [&dosGuard] { dosGuard.clear(); }); } -} // namespace web +} // namespace web::dosguard diff --git a/src/web/IntervalSweepHandler.hpp b/src/web/dosguard/IntervalSweepHandler.hpp similarity index 88% rename from src/web/IntervalSweepHandler.hpp rename to src/web/dosguard/IntervalSweepHandler.hpp index af7307241..5162eca4f 100644 --- a/src/web/IntervalSweepHandler.hpp +++ b/src/web/dosguard/IntervalSweepHandler.hpp @@ -24,7 +24,7 @@ #include -namespace web { +namespace web::dosguard { class BaseDOSGuard; @@ -42,7 +42,11 @@ class IntervalSweepHandler { * @param ctx The boost::asio::io_context to use * @param dosGuard The DOS guard to use */ - IntervalSweepHandler(util::Config const& config, boost::asio::io_context& ctx, web::BaseDOSGuard& dosGuard); + IntervalSweepHandler( + util::Config const& config, + boost::asio::io_context& ctx, + web::dosguard::BaseDOSGuard& dosGuard + ); }; -} // namespace web +} // namespace web::dosguard diff --git a/src/web/dosguard/WhitelistHandler.cpp b/src/web/dosguard/WhitelistHandler.cpp new file mode 100644 index 000000000..8ad38798a --- /dev/null +++ b/src/web/dosguard/WhitelistHandler.cpp @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "web/dosguard/WhitelistHandler.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace web::dosguard { + +void +Whitelist::add(std::string_view net) +{ + using namespace boost::asio; + + if (not isMask(net)) { + ips_.push_back(ip::make_address(net)); + return; + } + + if (isV4(net)) { + subnetsV4_.push_back(ip::make_network_v4(net)); + } else if (isV6(net)) { + subnetsV6_.push_back(ip::make_network_v6(net)); + } else { + throw std::runtime_error(fmt::format("malformed network: {}", net.data())); + } +} + +bool +Whitelist::isWhiteListed(std::string_view ip) const +{ + using namespace boost::asio; + + auto const addr = ip::make_address(ip); + if (std::find(std::begin(ips_), std::end(ips_), addr) != std::end(ips_)) + return true; + + if (addr.is_v4()) { + return std::find_if( + std::begin(subnetsV4_), std::end(subnetsV4_), std::bind_front(&isInV4Subnet, std::cref(addr)) + ) != std::end(subnetsV4_); + } + + if (addr.is_v6()) { + return std::find_if( + std::begin(subnetsV6_), std::end(subnetsV6_), std::bind_front(&isInV6Subnet, std::cref(addr)) + ) != std::end(subnetsV6_); + } + + return false; +} + +bool +Whitelist::isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet) +{ + auto const range = subnet.hosts(); + return range.find(addr.to_v4()) != range.end(); +} + +bool +Whitelist::isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet) +{ + auto const range = subnet.hosts(); + return range.find(addr.to_v6()) != range.end(); +} + +bool +Whitelist::isV4(std::string_view net) +{ + static std::regex const ipv4CidrRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}$)"); + return std::regex_match(std::string(net), ipv4CidrRegex); +} + +bool +Whitelist::isV6(std::string_view net) +{ + static std::regex const ipv6CidrRegex(R"(^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/\d{1,3}$)"); + return std::regex_match(std::string(net), ipv6CidrRegex); +} + +bool +Whitelist::isMask(std::string_view net) +{ + return net.find('/') != std::string_view::npos; +} + +} // namespace web::dosguard diff --git a/src/web/WhitelistHandler.hpp b/src/web/dosguard/WhitelistHandler.hpp similarity index 66% rename from src/web/WhitelistHandler.hpp rename to src/web/dosguard/WhitelistHandler.hpp index bb33cd224..d6f4f845f 100644 --- a/src/web/WhitelistHandler.hpp +++ b/src/web/dosguard/WhitelistHandler.hpp @@ -21,6 +21,7 @@ #include "util/config/Config.hpp" #include "web/Resolver.hpp" +#include "web/dosguard/WhitelistHandlerInterface.hpp" #include #include @@ -30,16 +31,14 @@ #include #include -#include #include -#include #include #include #include #include #include -namespace web { +namespace web::dosguard { /** * @brief A whitelist to remove rate limits of certain IP addresses. @@ -57,23 +56,7 @@ class Whitelist { * @throws std::runtime::error when the network address is not valid */ void - add(std::string_view net) - { - using namespace boost::asio; - - if (not isMask(net)) { - ips_.push_back(ip::make_address(net)); - return; - } - - if (isV4(net)) { - subnetsV4_.push_back(ip::make_network_v4(net)); - } else if (isV6(net)) { - subnetsV6_.push_back(ip::make_network_v6(net)); - } else { - throw std::runtime_error(fmt::format("malformed network: {}", net.data())); - } - } + add(std::string_view net); /** * @brief Checks to see if ip address is whitelisted. @@ -83,69 +66,29 @@ class Whitelist { * @return true if the given IP is whitelisted; false otherwise */ bool - isWhiteListed(std::string_view ip) const - { - using namespace boost::asio; - - auto const addr = ip::make_address(ip); - if (std::find(std::begin(ips_), std::end(ips_), addr) != std::end(ips_)) - return true; - - if (addr.is_v4()) { - return std::find_if( - std::begin(subnetsV4_), std::end(subnetsV4_), std::bind_front(&isInV4Subnet, std::cref(addr)) - ) != std::end(subnetsV4_); - } - - if (addr.is_v6()) { - return std::find_if( - std::begin(subnetsV6_), std::end(subnetsV6_), std::bind_front(&isInV6Subnet, std::cref(addr)) - ) != std::end(subnetsV6_); - } - - return false; - } + isWhiteListed(std::string_view ip) const; private: static bool - isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet) - { - auto const range = subnet.hosts(); - return range.find(addr.to_v4()) != range.end(); - } + isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet); static bool - isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet) - { - auto const range = subnet.hosts(); - return range.find(addr.to_v6()) != range.end(); - } + isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet); static bool - isV4(std::string_view net) - { - static std::regex const ipv4CidrRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}$)"); - return std::regex_match(std::string(net), ipv4CidrRegex); - } + isV4(std::string_view net); static bool - isV6(std::string_view net) - { - static std::regex const ipv6CidrRegex(R"(^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/\d{1,3}$)"); - return std::regex_match(std::string(net), ipv6CidrRegex); - } + isV6(std::string_view net); static bool - isMask(std::string_view net) - { - return net.find('/') != std::string_view::npos; - } + isMask(std::string_view net); }; /** * @brief A simple handler to add/check elements in a whitelist. */ -class WhitelistHandler { +class WhitelistHandler : public WhitelistHandlerInterface { Whitelist whitelist_; public: @@ -170,7 +113,7 @@ class WhitelistHandler { * @return true if the given IP is whitelisted; false otherwise */ bool - isWhiteListed(std::string_view ip) const + isWhiteListed(std::string_view ip) const override { return whitelist_.isWhiteListed(ip); } @@ -200,4 +143,4 @@ class WhitelistHandler { } }; -} // namespace web +} // namespace web::dosguard diff --git a/src/web/dosguard/WhitelistHandlerInterface.hpp b/src/web/dosguard/WhitelistHandlerInterface.hpp new file mode 100644 index 000000000..7c0d5f0d9 --- /dev/null +++ b/src/web/dosguard/WhitelistHandlerInterface.hpp @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include + +namespace web::dosguard { + +/** + * @brief Interface for a whitelist handler + */ +class WhitelistHandlerInterface { +public: + /** @brief Virtual destructor */ + virtual ~WhitelistHandlerInterface() = default; + + /** + * @brief Checks to see if the given IP is whitelisted + * + * @param ip The IP to check + * @return true if the given IP is whitelisted; false otherwise + */ + [[nodiscard]] virtual bool + isWhiteListed(std::string_view ip) const = 0; +}; + +} // namespace web::dosguard diff --git a/src/web/impl/HttpBase.hpp b/src/web/impl/HttpBase.hpp index 45b958a70..f986800a3 100644 --- a/src/web/impl/HttpBase.hpp +++ b/src/web/impl/HttpBase.hpp @@ -24,7 +24,7 @@ #include "util/build/Build.hpp" #include "util/log/Logger.hpp" #include "util/prometheus/Http.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/impl/AdminVerificationStrategy.hpp" #include "web/interface/Concepts.hpp" #include "web/interface/ConnectionBase.hpp" @@ -114,7 +114,7 @@ class HttpBase : public ConnectionBase { protected: boost::beast::flat_buffer buffer_; http::request req_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; std::shared_ptr const handler_; util::Logger log_{"WebServer"}; util::Logger perfLog_{"Performance"}; @@ -154,7 +154,7 @@ class HttpBase : public ConnectionBase { std::string const& ip, std::reference_wrapper tagFactory, std::shared_ptr adminVerification, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr handler, boost::beast::flat_buffer buffer ) diff --git a/src/web/impl/WsBase.hpp b/src/web/impl/WsBase.hpp index 0d2cf14a9..0ea0fae66 100644 --- a/src/web/impl/WsBase.hpp +++ b/src/web/impl/WsBase.hpp @@ -23,7 +23,7 @@ #include "rpc/common/Types.hpp" #include "util/Taggable.hpp" #include "util/log/Logger.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" #include "web/interface/Concepts.hpp" #include "web/interface/ConnectionBase.hpp" @@ -72,7 +72,7 @@ class WsBase : public ConnectionBase, public std::enable_shared_from_this>::shared_from_this; boost::beast::flat_buffer buffer_; - std::reference_wrapper dosGuard_; + std::reference_wrapper dosGuard_; bool sending_ = false; std::queue> messages_; std::shared_ptr const handler_; @@ -99,7 +99,7 @@ class WsBase : public ConnectionBase, public std::enable_shared_from_this tagFactory, - std::reference_wrapper dosGuard, + std::reference_wrapper dosGuard, std::shared_ptr const& handler, boost::beast::flat_buffer&& buffer ) diff --git a/src/web/ng/Server.cpp b/src/web/ng/Server.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/web/ng/Server.hpp b/src/web/ng/Server.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/common/rpc/FakesAndMocks.hpp b/tests/common/rpc/FakesAndMocks.hpp index 8ddaff329..38febee8e 100644 --- a/tests/common/rpc/FakesAndMocks.hpp +++ b/tests/common/rpc/FakesAndMocks.hpp @@ -23,7 +23,6 @@ #include "rpc/common/Specs.hpp" #include "rpc/common/Types.hpp" #include "rpc/common/Validators.hpp" -#include "web/DOSGuard.hpp" #include #include diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 74cd91b8e..cd591ea9b 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -12,7 +12,6 @@ target_sources( data/cassandra/ExecutionStrategyTests.cpp data/cassandra/RetryPolicyTests.cpp data/cassandra/SettingsProviderTests.cpp - DOSGuardTests.cpp # ETL etl/AmendmentBlockHandlerTests.cpp etl/CacheLoaderSettingsTests.cpp @@ -127,11 +126,12 @@ target_sources( util/TxUtilTests.cpp # Webserver web/AdminVerificationTests.cpp + web/dosguard/DOSGuardTests.cpp + web/dosguard/IntervalSweepHandlerTests.cpp + web/dosguard/WhitelistHandlerTests.cpp web/impl/ServerSslContextTests.cpp web/RPCServerHandlerTests.cpp web/ServerTests.cpp - web/IntervalSweepHandlerTests.cpp - web/WhitelistHandlerTests.cpp # New Config util/newconfig/ArrayViewTests.cpp util/newconfig/ObjectViewTests.cpp diff --git a/tests/unit/web/ServerTests.cpp b/tests/unit/web/ServerTests.cpp index 07704d95e..a61b30746 100644 --- a/tests/unit/web/ServerTests.cpp +++ b/tests/unit/web/ServerTests.cpp @@ -24,10 +24,11 @@ #include "util/config/Config.hpp" #include "util/prometheus/Label.hpp" #include "util/prometheus/Prometheus.hpp" -#include "web/DOSGuard.hpp" -#include "web/IntervalSweepHandler.hpp" #include "web/Server.hpp" -#include "web/WhitelistHandler.hpp" +#include "web/dosguard/DOSGuard.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" +#include "web/dosguard/IntervalSweepHandler.hpp" +#include "web/dosguard/WhitelistHandler.hpp" #include "web/impl/AdminVerificationStrategy.hpp" #include "web/interface/ConnectionBase.hpp" @@ -127,14 +128,14 @@ struct WebServerTest : NoLoggerFixture { boost::asio::io_context ctxSync; std::string const port = std::to_string(tests::util::generateFreePort()); Config cfg{generateJSONWithDynamicPort(port)}; - WhitelistHandler whitelistHandler{cfg}; - DOSGuard dosGuard{cfg, whitelistHandler}; - IntervalSweepHandler sweepHandler{cfg, ctxSync, dosGuard}; + dosguard::WhitelistHandler whitelistHandler{cfg}; + dosguard::DOSGuard dosGuard{cfg, whitelistHandler}; + dosguard::IntervalSweepHandler sweepHandler{cfg, ctxSync, dosGuard}; Config cfgOverload{generateJSONDataOverload(port)}; - WhitelistHandler whitelistHandlerOverload{cfgOverload}; - DOSGuard dosGuardOverload{cfgOverload, whitelistHandlerOverload}; - IntervalSweepHandler sweepHandlerOverload{cfgOverload, ctxSync, dosGuardOverload}; + dosguard::WhitelistHandler whitelistHandlerOverload{cfgOverload}; + dosguard::DOSGuard dosGuardOverload{cfgOverload, whitelistHandlerOverload}; + dosguard::IntervalSweepHandler sweepHandlerOverload{cfgOverload, ctxSync, dosGuardOverload}; // this ctx is for http server boost::asio::io_context ctx; @@ -178,7 +179,7 @@ std::shared_ptr> makeServerSync( util::Config const& config, boost::asio::io_context& ioc, - web::DOSGuard& dosGuard, + web::dosguard::DOSGuardInterface& dosGuard, std::shared_ptr const& handler ) { diff --git a/tests/unit/DOSGuardTests.cpp b/tests/unit/web/dosguard/DOSGuardTests.cpp similarity index 88% rename from tests/unit/DOSGuardTests.cpp rename to tests/unit/web/dosguard/DOSGuardTests.cpp index ac33520aa..c7f350693 100644 --- a/tests/unit/DOSGuardTests.cpp +++ b/tests/unit/web/dosguard/DOSGuardTests.cpp @@ -19,7 +19,8 @@ #include "util/LoggerFixtures.hpp" #include "util/config/Config.hpp" -#include "web/DOSGuard.hpp" +#include "web/dosguard/DOSGuard.hpp" +#include "web/dosguard/WhitelistHandlerInterface.hpp" #include #include @@ -30,11 +31,11 @@ using namespace testing; using namespace util; using namespace std; -using namespace web; +using namespace web::dosguard; namespace json = boost::json; -namespace { -constexpr auto JSONData = R"JSON( +struct DOSGuardTest : NoLoggerFixture { + static constexpr auto JSONData = R"JSON( { "dos_guard": { "max_fetches": 100, @@ -47,20 +48,15 @@ constexpr auto JSONData = R"JSON( } )JSON"; -constexpr auto IP = "127.0.0.2"; + static constexpr auto IP = "127.0.0.2"; -struct MockWhitelistHandler { - MOCK_METHOD(bool, isWhiteListed, (std::string_view ip), (const)); -}; - -using MockWhitelistHandlerType = NiceMock; -}; // namespace + struct MockWhitelistHandler : WhitelistHandlerInterface { + MOCK_METHOD(bool, isWhiteListed, (std::string_view ip), (const)); + }; -class DOSGuardTest : public NoLoggerFixture { -protected: Config cfg{json::parse(JSONData)}; - MockWhitelistHandlerType whitelistHandler; - BasicDOSGuard guard{cfg, whitelistHandler}; + NiceMock whitelistHandler; + DOSGuard guard{cfg, whitelistHandler}; }; TEST_F(DOSGuardTest, Whitelisting) diff --git a/tests/unit/web/IntervalSweepHandlerTests.cpp b/tests/unit/web/dosguard/IntervalSweepHandlerTests.cpp similarity index 93% rename from tests/unit/web/IntervalSweepHandlerTests.cpp rename to tests/unit/web/dosguard/IntervalSweepHandlerTests.cpp index 0e51e053d..9c9127047 100644 --- a/tests/unit/web/IntervalSweepHandlerTests.cpp +++ b/tests/unit/web/dosguard/IntervalSweepHandlerTests.cpp @@ -19,8 +19,8 @@ #include "util/AsioContextTestFixture.hpp" #include "util/config/Config.hpp" -#include "web/DOSGuard.hpp" -#include "web/IntervalSweepHandler.hpp" +#include "web/dosguard/DOSGuardInterface.hpp" +#include "web/dosguard/IntervalSweepHandler.hpp" #include #include @@ -28,7 +28,7 @@ #include -using namespace web; +using namespace web::dosguard; struct IntervalSweepHandlerTest : SyncAsioContextTest { protected: diff --git a/tests/unit/web/WhitelistHandlerTests.cpp b/tests/unit/web/dosguard/WhitelistHandlerTests.cpp similarity index 98% rename from tests/unit/web/WhitelistHandlerTests.cpp rename to tests/unit/web/dosguard/WhitelistHandlerTests.cpp index 2ef1facc2..b16a95a94 100644 --- a/tests/unit/web/WhitelistHandlerTests.cpp +++ b/tests/unit/web/dosguard/WhitelistHandlerTests.cpp @@ -18,7 +18,7 @@ //============================================================================== #include "util/LoggerFixtures.hpp" #include "util/config/Config.hpp" -#include "web/WhitelistHandler.hpp" +#include "web/dosguard/WhitelistHandler.hpp" #include #include @@ -29,7 +29,7 @@ #include using namespace util; -using namespace web; +using namespace web::dosguard; struct WhitelistHandlerTest : NoLoggerFixture {};