Skip to content

Commit

Permalink
Apply enhanced patch
Browse files Browse the repository at this point in the history
  • Loading branch information
enhanced committed Oct 24, 2024
1 parent 841a604 commit ec95179
Show file tree
Hide file tree
Showing 18 changed files with 949 additions and 8 deletions.
116 changes: 116 additions & 0 deletions src/base/bittorrent/peer_blacklist.hpp
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);
}
69 changes: 69 additions & 0 deletions src/base/bittorrent/peer_filter.hpp
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;
};
123 changes: 123 additions & 0 deletions src/base/bittorrent/peer_filter_plugin.hpp
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;
};
Loading

0 comments on commit ec95179

Please sign in to comment.