From de4a8f6f3c7b4179c9aad6f647d1279f3dfc131c Mon Sep 17 00:00:00 2001 From: Nick Korotysh Date: Sun, 2 May 2021 09:43:16 +0300 Subject: [PATCH] Refactor blacklist/whitelist filter implementation (#253) * refactored peer black/whitelist implementation * improved filter loggins - report line number containing invalid expression - print warning and do not enable filter when no rules loaded - report loaded rules count --- src/base/CMakeLists.txt | 2 +- src/base/bittorrent/peer_blacklist.hpp | 34 -------- src/base/bittorrent/peer_filter.hpp | 8 +- .../bittorrent/peer_filter_session_plugin.hpp | 85 +++++++++++++++++++ src/base/bittorrent/peer_whitelist.hpp | 46 ---------- src/base/bittorrent/session.cpp | 5 +- 6 files changed, 93 insertions(+), 87 deletions(-) create mode 100644 src/base/bittorrent/peer_filter_session_plugin.hpp delete mode 100644 src/base/bittorrent/peer_whitelist.hpp diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index cc986e50402..f32363aaa57 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -106,8 +106,8 @@ add_library(qbt_base STATIC bittorrent/peer_blacklist.hpp bittorrent/peer_filter.hpp bittorrent/peer_filter_plugin.hpp + bittorrent/peer_filter_session_plugin.hpp bittorrent/peer_logger.hpp - bittorrent/peer_whitelist.hpp bittorrent/portforwarderimpl.cpp bittorrent/resumedatasavingmanager.cpp bittorrent/session.cpp diff --git a/src/base/bittorrent/peer_blacklist.hpp b/src/base/bittorrent/peer_blacklist.hpp index 0aa5899edbd..fc6fb1ddc70 100644 --- a/src/base/bittorrent/peer_blacklist.hpp +++ b/src/base/bittorrent/peer_blacklist.hpp @@ -4,15 +4,11 @@ #include -#include #include -#include "base/logger.h" #include "base/net/geoipmanager.h" -#include "base/profile.h" #include "peer_filter_plugin.hpp" -#include "peer_filter.hpp" #include "peer_logger.hpp" // bad peer filter @@ -100,33 +96,3 @@ std::shared_ptr create_drop_bittorrent_media_player_plugin(l { return create_peer_action_plugin(th, wrap_filter(is_bittorrent_media_player, "bittorrent media player"), drop_connection); } - -std::shared_ptr create_peer_blacklist_plugin(lt::torrent_handle const&, client_data) -{ - QDir qbt_data_dir(specialFolderLocation(SpecialFolder::Data)); - - QString filter_file = qbt_data_dir.absoluteFilePath(QStringLiteral("peer_blacklist.txt")); - // do not create plugin if filter file doesn't exists - if (!QFile::exists(filter_file)) { - LogMsg("'peer_blacklist.txt' doesn't exist, do not enabling blacklist plugin", Log::INFO); - return nullptr; - } - - // create filter object only once - static peer_filter filter(filter_file); - // do not create plugin if no rules were loaded - if (filter.is_empty()) { - LogMsg("'peer_blacklist.txt' has no valid rules, do not enabling blacklist plugin", Log::WARNING); - return nullptr; - } - - auto peer_in_list = [&](const lt::peer_info& info, bool handshake, bool* stop_filtering) { - bool matched = filter.match_peer(info, false); - *stop_filtering = !handshake && !matched; - if (matched) - peer_logger_singleton::instance().log_peer(info, "blacklist"); - return matched; - }; - - return std::make_shared(std::move(peer_in_list), drop_connection); -} diff --git a/src/base/bittorrent/peer_filter.hpp b/src/base/bittorrent/peer_filter.hpp index e09b81c1868..a830ddd77c4 100644 --- a/src/base/bittorrent/peer_filter.hpp +++ b/src/base/bittorrent/peer_filter.hpp @@ -36,13 +36,14 @@ class peer_filter QRegularExpression peer_id_re(QString::fromStdString(peer_id)); QRegularExpression client_re(QString::fromStdString(client)); - QString msg_tmpl("'%1': invalid %2 matching expression '%3' detected, ignoring rule"); + QString msg_tmpl("'%1': invalid %2 matching expression '%3' detected at line %4, ignoring rule"); + int line = m_filters.size() + 1; if (!peer_id_re.isValid()) - LogMsg(msg_tmpl.arg(log_tag).arg("peer id").arg(peer_id_re.pattern()), Log::WARNING); + LogMsg(msg_tmpl.arg(log_tag).arg("peer id").arg(peer_id_re.pattern()).arg(line), Log::WARNING); if (!client_re.isValid()) - LogMsg(msg_tmpl.arg(log_tag).arg("client name").arg(client_re.pattern()), Log::WARNING); + LogMsg(msg_tmpl.arg(log_tag).arg("client name").arg(client_re.pattern()).arg(line), Log::WARNING); if (peer_id_re.isValid() && client_re.isValid()) m_filters.append({peer_id_re, client_re}); @@ -61,6 +62,7 @@ class peer_filter } bool is_empty() const { return m_filters.isEmpty(); } + int rules_count() const { return m_filters.size(); } private: QVector> m_filters; diff --git a/src/base/bittorrent/peer_filter_session_plugin.hpp b/src/base/bittorrent/peer_filter_session_plugin.hpp new file mode 100644 index 00000000000..36de2a90d58 --- /dev/null +++ b/src/base/bittorrent/peer_filter_session_plugin.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include "base/logger.h" +#include "base/profile.h" + +#include "peer_filter_plugin.hpp" +#include "peer_filter.hpp" +#include "peer_logger.hpp" + +// filter factory function +std::unique_ptr create_peer_filter(const QString& filename) +{ + QDir qbt_data_dir(specialFolderLocation(SpecialFolder::Data)); + + QString filter_file = qbt_data_dir.absoluteFilePath(filename); + // do not create plugin if filter file doesn't exists + if (!QFile::exists(filter_file)) { + LogMsg(QString("'%1' doesn't exist, do not enabling filter").arg(filename), Log::NORMAL); + return nullptr; + } + + auto filter = std::make_unique(filter_file); + if (filter->is_empty()) { + LogMsg(QString("'%1' has no valid rules, do not enabling filter").arg(filename), Log::WARNING); + filter.reset(); + } else { + LogMsg(QString("'%1' contains %2 valid rules").arg(filename).arg(filter->rules_count()), Log::INFO); + } + + return filter; +} + + +// drop connection action +void drop_peer_connection(lt::peer_connection_handle ph) +{ + ph.disconnect(boost::asio::error::connection_refused, lt::operation_t::bittorrent, lt::disconnect_severity_t{0}); +} + + +class peer_filter_session_plugin final : public lt::plugin +{ +public: + peer_filter_session_plugin() + : m_blacklist(create_peer_filter(QStringLiteral("peer_blacklist.txt"))) + , m_whitelist(create_peer_filter(QStringLiteral("peer_whitelist.txt"))) + { + } + + std::shared_ptr new_torrent(const lt::torrent_handle&, client_data) override + { + // do not waste CPU and memory for useless objects when no filters are enabled + if (!m_blacklist && !m_whitelist) + return nullptr; + return std::make_shared([this](auto&&... args) { return filter(args...); }, drop_peer_connection); + } + +protected: + bool filter(const lt::peer_info& info, bool handshake, bool* stop_filtering) const + { + if (m_blacklist) { + bool matched = m_blacklist->match_peer(info, false); + *stop_filtering = !handshake && !matched; + if (matched) + peer_logger_singleton::instance().log_peer(info, "blacklist"); + return matched; + } + + if (m_whitelist) { + bool matched = m_whitelist->match_peer(info, handshake); + *stop_filtering = !handshake && matched; + if (!matched) + peer_logger_singleton::instance().log_peer(info, "whitelist"); + return !matched; + } + + return false; + } + +private: + std::unique_ptr m_blacklist; + std::unique_ptr m_whitelist; +}; diff --git a/src/base/bittorrent/peer_whitelist.hpp b/src/base/bittorrent/peer_whitelist.hpp deleted file mode 100644 index 09069d19049..00000000000 --- a/src/base/bittorrent/peer_whitelist.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -#include "base/logger.h" -#include "base/profile.h" - -#include "peer_filter.hpp" -#include "peer_filter_plugin.hpp" -#include "peer_logger.hpp" - -std::shared_ptr create_peer_whitelist_plugin(lt::torrent_handle const&, client_data) -{ - QDir qbt_data_dir(specialFolderLocation(SpecialFolder::Data)); - - QString filter_file = qbt_data_dir.absoluteFilePath(QStringLiteral("peer_whitelist.txt")); - // do not create plugin if filter file doesn't exists - if (!QFile::exists(filter_file)) { - LogMsg("'peer_whitelist.txt' doesn't exist, do not enabling whitelist plugin", Log::INFO); - return nullptr; - } - - // create filter object only once - static peer_filter filter(filter_file); - // do not create plugin if no rules were loaded - if (filter.is_empty()) { - LogMsg("'peer_whitelist.txt' has no valid rules, do not enabling whitelist plugin", Log::WARNING); - return nullptr; - } - - auto peer_not_in_list = [&](const lt::peer_info& info, bool handshake, bool* stop_filtering) { - bool matched = filter.match_peer(info, handshake); - *stop_filtering = !handshake && matched; - if (!matched) - peer_logger_singleton::instance().log_peer(info, "whitelist"); - return !matched; - }; - - auto drop_connection = [](lt::peer_connection_handle p) { - p.disconnect(boost::asio::error::connection_refused, - lt::operation_t::bittorrent, - lt::disconnect_severity_t{0}); - }; - - return std::make_shared(std::move(peer_not_in_list), std::move(drop_connection)); -} diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index f24a5de5712..bf58fcc7f26 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -97,7 +97,7 @@ #include "magneturi.h" #include "nativesessionextension.h" #include "peer_blacklist.hpp" -#include "peer_whitelist.hpp" +#include "peer_filter_session_plugin.hpp" #include "portforwarderimpl.h" #include "resumedatasavingmanager.h" #include "statistics.h" @@ -1207,8 +1207,7 @@ void Session::initializeNativeSession() } if (isAutoBanBTPlayerPeerEnabled()) m_nativeSession->add_extension(&create_drop_bittorrent_media_player_plugin); - m_nativeSession->add_extension(&create_peer_blacklist_plugin); - m_nativeSession->add_extension(&create_peer_whitelist_plugin); + m_nativeSession->add_extension(std::make_shared()); m_nativeSession->add_extension(std::make_shared()); }