forked from qbittorrent/qBittorrent
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
enhanced
committed
Oct 24, 2024
1 parent
841a604
commit ec95179
Showing
18 changed files
with
949 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#pragma once | ||
|
||
#include <regex> | ||
|
||
#include <libtorrent/torrent_info.hpp> | ||
|
||
#include <QHostAddress> | ||
|
||
#include "base/net/geoipmanager.h" | ||
|
||
#include "peer_filter_plugin.hpp" | ||
#include "peer_logger.hpp" | ||
|
||
// bad peer filter | ||
bool is_bad_peer(const lt::peer_info& info) | ||
{ | ||
static const std::regex id_filter("-(XL|SD|XF|QD|BN|DL|TS|DT|HP)(\\d+)-"); | ||
static const std::regex ua_filter(R"((\d+.\d+.\d+.\d+|cacao_torrent))"); | ||
static const std::regex consume_filter(R"(((dt|hp|xm)/torrent|Gopeed dev|Rain 0.0.0|(Taipei-torrent( dev)?)))", std::regex_constants::icase); | ||
|
||
// TODO: trafficConsume by thank243(senis) but it's hard to determine GT0003 is legitimate client or not... | ||
// Anyway, block dt/torrent and Taipei-torrent with specific case first. | ||
QString country = Net::GeoIPManager::instance()->lookup(QHostAddress(info.ip.data())); | ||
if (country == QLatin1String("CN") && std::regex_match(info.client, consume_filter)) { | ||
return true; | ||
} | ||
|
||
return std::regex_match(info.pid.data(), info.pid.data() + 8, id_filter) || std::regex_match(info.client, ua_filter); | ||
} | ||
|
||
// Unknown Peer filter | ||
bool is_unknown_peer(const lt::peer_info& info) | ||
{ | ||
QString country = Net::GeoIPManager::instance()->lookup(QHostAddress(info.ip.data())); | ||
return info.client.find("Unknown") != std::string::npos && country == QLatin1String("CN"); | ||
} | ||
|
||
// Offline Downloader filter | ||
bool is_offline_downloader(const lt::peer_info& info) | ||
{ | ||
static const std::regex id_filter("-LT(1220|2070)-"); | ||
unsigned short port = info.ip.port(); | ||
QString country = Net::GeoIPManager::instance()->lookup(QHostAddress(info.ip.data())); | ||
// 115: Old data, may out of date. | ||
bool fake_transmission = port >= 65000 && country == QLatin1String("CN") && info.client.find("Transmission") != std::string::npos; | ||
// PikPak: PikPak is renting Worldstream server and announce as LT1220/LT2070, the best way is block the ip range via ip filter(?) | ||
// Xunlei: it seems Xunlei is using LT2070 too | ||
bool fake_libtorrent = (country == QLatin1String("NL") || country == QLatin1String("CN")) && std::regex_match(info.pid.data(), info.pid.data() + 8, id_filter); | ||
return fake_transmission || fake_libtorrent; | ||
} | ||
|
||
// BitTorrent Media Player Peer filter | ||
bool is_bittorrent_media_player(const lt::peer_info& info) | ||
{ | ||
if (info.client.find("StellarPlayer") != std::string::npos || info.client.find("Elementum") != std::string::npos) { | ||
return true; | ||
} | ||
static const std::regex player_filter("-(UW\\w{4}|SP(([0-2]\\d{3})|(3[0-5]\\d{2})))-"); | ||
return !!std::regex_match(info.pid.data(), info.pid.data() + 8, player_filter); | ||
} | ||
|
||
|
||
// drop connection action | ||
void drop_connection(lt::peer_connection_handle ph) | ||
{ | ||
ph.disconnect(boost::asio::error::connection_refused, lt::operation_t::bittorrent, lt::disconnect_severity_t{0}); | ||
} | ||
|
||
|
||
template<typename F> | ||
auto wrap_filter(F filter, const std::string& tag) | ||
{ | ||
return [=](const lt::peer_info& info, bool handshake, bool* stop_filtering) { | ||
bool matched = filter(info); | ||
*stop_filtering = !handshake && !matched; | ||
if (matched) | ||
peer_logger_singleton::instance().log_peer(info, tag); | ||
return matched; | ||
}; | ||
} | ||
|
||
|
||
std::shared_ptr<lt::torrent_plugin> create_peer_action_plugin( | ||
const lt::torrent_handle& th, | ||
filter_function filter, | ||
action_function action) | ||
{ | ||
// ignore private torrents | ||
if (th.torrent_file() && th.torrent_file()->priv()) | ||
return nullptr; | ||
|
||
return std::make_shared<peer_action_plugin>(std::move(filter), std::move(action)); | ||
} | ||
|
||
|
||
// plugins factory functions | ||
|
||
std::shared_ptr<lt::torrent_plugin> create_drop_bad_peers_plugin(lt::torrent_handle const& th, client_data) | ||
{ | ||
return create_peer_action_plugin(th, wrap_filter(is_bad_peer, "bad peer"), drop_connection); | ||
} | ||
|
||
std::shared_ptr<lt::torrent_plugin> create_drop_unknown_peers_plugin(lt::torrent_handle const& th, client_data) | ||
{ | ||
return create_peer_action_plugin(th, wrap_filter(is_unknown_peer, "unknown peer"), drop_connection); | ||
} | ||
|
||
std::shared_ptr<lt::torrent_plugin> create_drop_offline_downloader_plugin(lt::torrent_handle const& th, client_data) | ||
{ | ||
return create_peer_action_plugin(th, wrap_filter(is_offline_downloader, "offline downloader"), drop_connection); | ||
} | ||
|
||
std::shared_ptr<lt::torrent_plugin> create_drop_bittorrent_media_player_plugin(lt::torrent_handle const& th, client_data) | ||
{ | ||
return create_peer_action_plugin(th, wrap_filter(is_bittorrent_media_player, "bittorrent media player"), drop_connection); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <fstream> | ||
#include <string> | ||
|
||
#include <QFileInfo> | ||
#include <QRegularExpression> | ||
#include <QString> | ||
#include <QVector> | ||
|
||
#include <libtorrent/peer_info.hpp> | ||
|
||
#include "base/logger.h" | ||
|
||
namespace { | ||
|
||
bool qregex_has_match(const QRegularExpression& re, const QString& str) | ||
{ | ||
auto m = re.match(str); | ||
return m.hasMatch(); | ||
} | ||
|
||
} | ||
|
||
class peer_filter | ||
{ | ||
public: | ||
explicit peer_filter(const QString& filter_file) | ||
{ | ||
QString log_tag = QFileInfo(filter_file).fileName(); | ||
|
||
std::ifstream ifs(filter_file.toStdString()); | ||
std::string peer_id, client; | ||
while (ifs >> peer_id >> client) { | ||
QRegularExpression peer_id_re(QString::fromStdString(peer_id)); | ||
QRegularExpression client_re(QString::fromStdString(client)); | ||
|
||
auto msg_tmpl = u"'%1': invalid %2 matching expression '%3' detected at line %4, ignoring rule"_s; | ||
int line = m_filters.size() + 1; | ||
|
||
if (!peer_id_re.isValid()) | ||
LogMsg(msg_tmpl.arg(log_tag).arg(u"peer id"_s).arg(peer_id_re.pattern()).arg(line), Log::WARNING); | ||
|
||
if (!client_re.isValid()) | ||
LogMsg(msg_tmpl.arg(log_tag).arg(u"client name"_s).arg(client_re.pattern()).arg(line), Log::WARNING); | ||
|
||
if (peer_id_re.isValid() && client_re.isValid()) | ||
m_filters.append({peer_id_re, client_re}); | ||
} | ||
} | ||
|
||
bool match_peer(const lt::peer_info& info, bool skip_name) const | ||
{ | ||
QString peer_id = QString::fromLatin1(info.pid.data(), 8); | ||
QString client = QString::fromStdString(info.client); | ||
return std::any_of(m_filters.begin(), m_filters.end(), | ||
[&](const auto& filter) { | ||
return qregex_has_match(filter[0], peer_id) && | ||
(skip_name || qregex_has_match(filter[1], client)); | ||
}); | ||
} | ||
|
||
bool is_empty() const { return m_filters.isEmpty(); } | ||
int rules_count() const { return m_filters.size(); } | ||
|
||
private: | ||
QVector<QVector<QRegularExpression>> m_filters; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
#pragma once | ||
|
||
#include <libtorrent/extensions.hpp> | ||
#include <libtorrent/peer_connection_handle.hpp> | ||
|
||
#if (LIBTORRENT_VERSION_NUM >= 20000) | ||
using client_data = lt::client_data_t; | ||
#else | ||
using client_data = void*; | ||
#endif | ||
|
||
using filter_function = std::function<bool(const lt::peer_info&, bool, bool*)>; | ||
using action_function = std::function<void(lt::peer_connection_handle)>; | ||
|
||
class peer_filter_plugin final : public lt::peer_plugin | ||
{ | ||
public: | ||
peer_filter_plugin(lt::peer_connection_handle p, filter_function filter, action_function action) | ||
: m_peer_connection(p) | ||
, m_filter(std::move(filter)) | ||
, m_action(std::move(action)) | ||
{} | ||
|
||
bool on_handshake(lt::span<char const> d) override | ||
{ | ||
handle_peer(true); | ||
return peer_plugin::on_handshake(d); | ||
} | ||
|
||
bool on_extension_handshake(lt::bdecode_node const& d) override | ||
{ | ||
handle_peer(true); | ||
return peer_plugin::on_extension_handshake(d); | ||
} | ||
|
||
bool on_interested() override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_interested(); | ||
} | ||
|
||
bool on_not_interested() override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_not_interested(); | ||
} | ||
|
||
bool on_have(lt::piece_index_t p) override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_have(p); | ||
} | ||
|
||
bool on_dont_have(lt::piece_index_t p) override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_dont_have(p); | ||
} | ||
|
||
bool on_bitfield(lt::bitfield const& bitfield) override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_bitfield(bitfield); | ||
} | ||
|
||
bool on_have_all() override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_have_all(); | ||
} | ||
|
||
bool on_have_none() override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_have_none(); | ||
} | ||
|
||
bool on_request(lt::peer_request const& r) override | ||
{ | ||
handle_peer(); | ||
return peer_plugin::on_request(r); | ||
} | ||
|
||
protected: | ||
void handle_peer(bool handshake = false) | ||
{ | ||
if (m_stop_filtering) | ||
return; | ||
|
||
lt::peer_info info; | ||
m_peer_connection.get_peer_info(info); | ||
|
||
if (m_filter(info, handshake, &m_stop_filtering)) | ||
m_action(m_peer_connection); | ||
} | ||
|
||
private: | ||
lt::peer_connection_handle m_peer_connection; | ||
|
||
filter_function m_filter; | ||
action_function m_action; | ||
|
||
bool m_stop_filtering = false; | ||
}; | ||
|
||
|
||
class peer_action_plugin : public lt::torrent_plugin | ||
{ | ||
public: | ||
peer_action_plugin(filter_function filter, action_function action) | ||
: m_filter(std::move(filter)) | ||
, m_action(std::move(action)) | ||
{} | ||
|
||
std::shared_ptr<lt::peer_plugin> new_connection(lt::peer_connection_handle const& p) override | ||
{ | ||
return std::make_shared<peer_filter_plugin>(p, m_filter, m_action); | ||
} | ||
|
||
private: | ||
filter_function m_filter; | ||
action_function m_action; | ||
}; |
Oops, something went wrong.