Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

PCB 作弊快速测试支持 #459

Merged
merged 5 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ public class PeerBanHelperServer implements Reloadable {
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
@Getter
private final List<BanListInvoker> banListInvoker = new ArrayList<>();
private String pbhServerAddress;
private final ScheduledExecutorService GENERAL_SCHEDULER = Executors.newScheduledThreadPool(8, Thread.ofVirtual().factory());
private final Lock banWaveLock = new ReentrantLock();
private String pbhServerAddress;
@Getter
private YamlConfiguration profileConfig;
@Getter
Expand All @@ -101,7 +102,6 @@ public class PeerBanHelperServer implements Reloadable {
@Autowired
private ModuleMatchCache moduleMatchCache;
private ScheduledExecutorService BAN_WAVE_SERVICE;
private final Lock banWaveLock = new ReentrantLock();
@Getter
private Map<PeerAddress, List<PeerMetadata>> LIVE_PEERS = new HashMap<>();
@Autowired
Expand Down Expand Up @@ -467,18 +467,20 @@ public void banWave() {
List<Torrent> relaunch = Collections.synchronizedList(new ArrayList<>());
details.forEach(detail -> {
protect.getService().submit(() -> {
if (detail.result().action() == PeerAction.BAN) {
if (detail.result().action() == PeerAction.BAN || detail.result().action() == PeerAction.BAN_FOR_DISCONNECT) {
long actualBanDuration = banDuration;
if (detail.banDuration() > 0) {
actualBanDuration = detail.banDuration();
}
BanMetadata banMetadata = new BanMetadata(detail.result().moduleContext().getName(), downloader.getName(),
System.currentTimeMillis(), System.currentTimeMillis() + actualBanDuration,
System.currentTimeMillis(), System.currentTimeMillis() + actualBanDuration, detail.result().action() == PeerAction.BAN_FOR_DISCONNECT,
detail.torrent(), detail.peer(), detail.result().rule(), detail.result().reason());
bannedPeers.add(banMetadata);
relaunch.add(detail.torrent());
banPeer(banMetadata, detail.torrent(), detail.peer());
log.warn(tlUI(Lang.BAN_PEER, detail.peer().getPeerAddress(), detail.peer().getPeerId(), detail.peer().getClientName(), detail.peer().getProgress(), detail.peer().getUploaded(), detail.peer().getDownloaded(), detail.torrent().getName(), tl(DEF_LOCALE, detail.result().reason())));
if (detail.result().action() != PeerAction.BAN_FOR_DISCONNECT) {
log.warn(tlUI(Lang.BAN_PEER, detail.peer().getPeerAddress(), detail.peer().getPeerId(), detail.peer().getClientName(), detail.peer().getProgress(), detail.peer().getUploaded(), detail.peer().getDownloaded(), detail.torrent().getName(), tl(DEF_LOCALE, detail.result().reason())));
}
}
});
});
Expand Down Expand Up @@ -761,7 +763,7 @@ public CheckResult checkBan(@NotNull Torrent torrent, @NotNull Peer peer, @NotNu
result = r;
break; // 立刻离开循环,处理跳过
}
if (r.action() == PeerAction.BAN) {
if (r.action() == PeerAction.BAN || r.action() == PeerAction.BAN_FOR_DISCONNECT) {
result = r;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ private List<BtnBan> generateBans() {
if (e.getValue().getBanAt() <= lastReport) {
continue;
}
if(e.getValue().isBanForDisconnect()){
continue;
}
BtnBan btnBan = new BtnBan();
btnBan.setBtnBan(e.getValue().getContext().equals(BtnNetworkOnline.class.getName()));
btnBan.setPeer(e.getKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public ProfileUpdateScript(YamlConfiguration conf) {
this.conf = conf;
}

@UpdateScript(version = 19)
public void fastPcbTesting() {
conf.set("module.progress-cheat-blocker.fast-pcb-test-percentage", 0.1d);
conf.set("module.progress-cheat-blocker.fast-pcb-test-block-duration", 15000);
}

@UpdateScript(version = 18)
public void banDelayWait() {
conf.set("module.progress-cheat-blocker.max-wait-duration", 30000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private void createTables() throws SQLException {

private void performUpgrade() throws SQLException {
Dao<MetadataEntity, String> metadata = DaoManager.createDao(getDataSource(), MetadataEntity.class);
MetadataEntity version = metadata.createIfNotExists(new MetadataEntity("version", "5"));
MetadataEntity version = metadata.createIfNotExists(new MetadataEntity("version", "6"));
int v = Integer.parseInt(version.getValue());
if (v < 3) {
try {
Expand All @@ -66,6 +66,11 @@ private void performUpgrade() throws SQLException {
TableUtils.createTableIfNotExists(database.getDataSource(), ProgressCheatBlockerPersistEntity.class);
v = 5;
}
if (v == 5) {
TableUtils.dropTable(getDataSource(), ProgressCheatBlockerPersistEntity.class, true);
TableUtils.createTableIfNotExists(database.getDataSource(), ProgressCheatBlockerPersistEntity.class);
v = 6;
}
version.setValue(String.valueOf(v));
metadata.update(version);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public List<ProgressCheatBlocker.ClientTask> fetchFromDatabase(ProgressCheatBloc
entity.getFirstTimeSeen().getTime(),
entity.getLastTimeSeen().getTime(),
entity.getDownloader(),
entity.getBanDelayWindowEndAt().getTime()
entity.getBanDelayWindowEndAt().getTime(),
entity.getFastPcbTestExecuteAt()
)
).collect(Collectors.toCollection(CopyOnWriteArrayList::new)); // 可变 List,需要并发安全
}
Expand All @@ -70,7 +71,8 @@ public void flushDatabase(List<ProgressCheatBlocker.ClientTaskRecord> records) t
new Timestamp(System.currentTimeMillis()),
new Timestamp(System.currentTimeMillis()),
task.getDownloader(),
new Timestamp(task.getBanDelayWindowEndAt())
new Timestamp(task.getBanDelayWindowEndAt()),
task.getFastPcbTestExecuteAt()
);
create(entity);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ public final class ProgressCheatBlockerPersistEntity {
private String downloader;
@DatabaseField(canBeNull = false)
private Timestamp banDelayWindowEndAt;
@DatabaseField(canBeNull = false)
private long fastPcbTestExecuteAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,17 @@ public void recordCheck() {

@Override
public void recordPeerBan(PeerAddress address, BanMetadata metadata) {
if(metadata.isBanForDisconnect()){
return;
}
bans++;
}

@Override
public void recordPeerUnban(PeerAddress address, BanMetadata metadata) {
if(metadata.isBanForDisconnect()){
return;
}
unbans++;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public void recordCheck() {

@Override
public void recordPeerBan(PeerAddress address, BanMetadata metadata) {
if(metadata.isBanForDisconnect()){
return;
}
inMemory.recordPeerBan(address, metadata);
// 将数据库 IO 移动到虚拟线程上
Thread.ofVirtual().start(() -> {
Expand Down Expand Up @@ -134,6 +137,9 @@ public void recordPeerBan(PeerAddress address, BanMetadata metadata) {

@Override
public void recordPeerUnban(PeerAddress address, BanMetadata metadata) {
if(metadata.isBanForDisconnect()){
return;
}
inMemory.recordPeerUnban(address, metadata);
// no record
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public enum PeerAction {
NO_ACTION,
BAN,
BAN_FOR_DISCONNECT,
SKIP
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ private void reloadConfig() {
peerAddress = peerAddress.toIPv4();
}
for (Map.Entry<PeerAddress, BanMetadata> bannedPeerEntry : getServer().getBannedPeers().entrySet()) {
if(bannedPeerEntry.getValue().isBanForDisconnect()){
continue;
}
PeerAddress bannedPeer = bannedPeerEntry.getKey();
IPAddress bannedAddress = bannedPeer.getAddress().withoutPrefixLength();
if (bannedAddress.isIPv4Convertible()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public class ProgressCheatBlocker extends AbstractRuleFeatureModule implements R
private boolean enablePersist;
private long persistDuration;
private long maxWaitDuration;
private long fastPcbTestBlockingDuration;
private double fastPcbTestPercentage;

@Override
public @NotNull String getName() {
Expand Down Expand Up @@ -179,6 +181,8 @@ private void reloadConfig() {
this.enablePersist = getConfig().getBoolean("enable-persist");
this.persistDuration = getConfig().getLong("persist-duration");
this.maxWaitDuration = getConfig().getLong("max-wait-duration");
this.fastPcbTestPercentage = getConfig().getDouble("fast-pcb-test-percentage");
this.fastPcbTestBlockingDuration = getConfig().getLong("fast-pcb-test-block-duration");
}


Expand Down Expand Up @@ -208,7 +212,7 @@ private void reloadConfig() {
if (lastRecordedProgress == null) lastRecordedProgress = new CopyOnWriteArrayList<>();
ClientTask clientTask = lastRecordedProgress.stream().filter(task -> task.getPeerIp().equals(peerIpString)).findFirst().orElse(null);
if (clientTask == null) {
clientTask = new ClientTask(peerIpString, 0d, 0L, 0L, 0, 0, System.currentTimeMillis(), System.currentTimeMillis(), downloader.getName(), 0L);
clientTask = new ClientTask(peerIpString, 0d, 0L, 0L, 0, 0, System.currentTimeMillis(), System.currentTimeMillis(), downloader.getName(), 0L, 0L);
lastRecordedProgress.add(clientTask);
}
long uploadedIncremental; // 上传增量
Expand All @@ -232,6 +236,22 @@ private void reloadConfig() {
if (torrentSize < torrentMinimumSize) {
return pass();
}

// 是否需要进行快速 PCB 测试
if (fastPcbTestPercentage > 0) {
// 只在 <= 0(也就是从未测试过)的情况下对其进行测试
if (clientTask.getFastPcbTestExecuteAt() <= 0) {
// 如果上传量大于设置的比率,我们主动断开一次连接,封禁 Peer 一段时间,并尽快解除封禁
if (actualUploaded >= fastPcbTestPercentage * torrentSize) {
clientTask.setFastPcbTestExecuteAt(actualUploaded);
return new CheckResult(getClass(), PeerAction.BAN_FOR_DISCONNECT, fastPcbTestBlockingDuration,
new TranslationComponent(Lang.PCB_RULE_PEER_PROGRESS_CHEAT_TESTING),
new TranslationComponent(Lang.PCB_DESCRIPTION_PEER_PROGRESS_CHEAT_TESTING)
);
}
}
}

// 计算进度信息
final double actualProgress = (double) actualUploaded / torrentSize; // 实际进度
final double clientProgress = peer.getProgress(); // 客户端汇报进度
Expand Down Expand Up @@ -277,7 +297,6 @@ private void reloadConfig() {
clientTask.setBanDelayWindowEndAt(0L);
progressRecorder.invalidate(client); // 封禁时,移除缓存
}

return new CheckResult(getClass(), ban ? PeerAction.BAN : PeerAction.NO_ACTION, 0, new TranslationComponent(Lang.PCB_RULE_PROGRESS_REWIND),
new TranslationComponent(Lang.MODULE_PCB_PEER_BAN_REWIND,
percent(clientProgress),
Expand Down Expand Up @@ -324,7 +343,7 @@ private int calcClientTaskSize(ClientTask clientTask) {
// double = 8
// int = 4
// 对象头 = 12
return calcStringSize(clientTask.peerIp) + (5 * 8) + 8 + (2 * 4) + 12;
return calcStringSize(clientTask.peerIp) + (7 * 8) + (2 * 4) + 12;
}

private int calcClientSize(Client client) {
Expand All @@ -351,6 +370,7 @@ public static class ClientTask {
private long lastTimeSeen;
private String downloader;
private long banDelayWindowEndAt;
private long fastPcbTestExecuteAt;
}

@AllArgsConstructor
Expand Down
27 changes: 22 additions & 5 deletions src/main/java/com/ghostchu/peerbanhelper/text/Lang.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,28 @@ public enum Lang {
CLEANED_BANLOGS,
OPERATION_EXECUTE_SUCCESSFULLY,
PBH_PLUS_LICENSE_UPDATE,
GUI_MENU_DEBUG_RELOAD_CONFIGURATION, GUI_MENU_DEBUG_HEAP_DUMP, GUI_MENU_PRINT_THREADS,
RELOAD_COMPLETED_TITLE, RELOAD_COMPLETED_DESCRIPTION, HEAPDUMP_COMPLETED_TITLE,
HEAPDUMP_COMPLETED_DESCRIPTION, HEAPDUMP_FAILED_DESCRIPTION, HEAPDUMP_FAILED_TITLE,
RELOADING_MODULE, GUI_MENU_DEBUG, TORRENT_NOT_FOUND, PEER_NOT_FOUND,
PBH_PLUS_LICENSE_INVALID, PBH_PLUS_LICENSE_EXPIRED, IN_ECOMODE_DESCRIPTION, IN_ECOMODE_SHORT, DOWNLOADER_TRANSMISSION_DISCOURAGE, DOWNLOADER_FAILED_REQUEST_STATISTICS, DOWNLOADER_QBITTORRENTEE_SHADOWBANAPI_TEST_FAILURE;
GUI_MENU_DEBUG_RELOAD_CONFIGURATION,
GUI_MENU_DEBUG_HEAP_DUMP,
GUI_MENU_PRINT_THREADS,
RELOAD_COMPLETED_TITLE,
RELOAD_COMPLETED_DESCRIPTION,
HEAPDUMP_COMPLETED_TITLE,
HEAPDUMP_COMPLETED_DESCRIPTION,
HEAPDUMP_FAILED_DESCRIPTION,
HEAPDUMP_FAILED_TITLE,
RELOADING_MODULE,
GUI_MENU_DEBUG,
TORRENT_NOT_FOUND,
PEER_NOT_FOUND,
PBH_PLUS_LICENSE_INVALID,
PBH_PLUS_LICENSE_EXPIRED,
IN_ECOMODE_DESCRIPTION,
IN_ECOMODE_SHORT,
DOWNLOADER_TRANSMISSION_DISCOURAGE,
DOWNLOADER_FAILED_REQUEST_STATISTICS,
DOWNLOADER_QBITTORRENTEE_SHADOWBANAPI_TEST_FAILURE,
PCB_RULE_PEER_PROGRESS_CHEAT_TESTING,
PCB_DESCRIPTION_PEER_PROGRESS_CHEAT_TESTING;

public String getKey() {
return name();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@ public class BanMetadata extends PeerMetadata implements Comparable<PeerMetadata
private String context;
private long banAt;
private long unbanAt;
private boolean banForDisconnect;
private TranslationComponent rule;
private TranslationComponent description;

public BanMetadata(String context, String downloader, long banAt, long unbanAt, Torrent torrent, Peer peer, TranslationComponent rule,
public BanMetadata(String context, String downloader, long banAt, long unbanAt, boolean banForDisconnect, Torrent torrent, Peer peer, TranslationComponent rule,
TranslationComponent description) {
super(downloader, torrent, peer);
this.context = context;
this.banAt = banAt;
this.unbanAt = unbanAt;
this.banForDisconnect = banForDisconnect;
this.rule = rule;
this.description = description;
}

public BanMetadata(String context, String downloader, long banAt, long unbanAt, TorrentWrapper torrent, PeerWrapper peer, TranslationComponent rule,
public BanMetadata(String context, String downloader, long banAt, long unbanAt, boolean banForDisconnect, TorrentWrapper torrent, PeerWrapper peer, TranslationComponent rule,
TranslationComponent description) {
super(downloader, torrent, peer);
this.context = context;
this.banAt = banAt;
this.unbanAt = unbanAt;
this.banForDisconnect = banForDisconnect;
this.rule = rule;
this.description = description;
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/resources/lang/en_us/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,6 @@ IN_ECOMODE_DESCRIPTION: "EcoQoS API load successfully,Windows Efficiency Mode
DOWNLOADER_TRANSMISSION_DISCOURAGE: "Warning: Use Transmission Adapter is discourage. Frequent starting and stopping of torrents on seeds that are often subject to bans can result in frequent updates to the tracker server, indirectly triggering DoS attacks. This increases the load on the tracker server and may lead to your IP address being banned by the tracker. We encourage you to migrate to other downloaders whenever possible. https://github.com/PBH-BTN/PeerBanHelper/issues/382"
DOWNLOADER_QBITTORRENTEE_SHADOWBANAPI_TEST_FAILURE: "Your current version of qBittorrentEE does not support ShadowBan or has not enabled ShadowBan in the qBittorrentEE options. Please turn off the ShadowBan switch."

DOWNLOADER_FAILED_REQUEST_STATISTICS: "Getting stats data from downloader {} failed: {}"
DOWNLOADER_FAILED_REQUEST_STATISTICS: "Getting stats data from downloader {} failed: {}"
PCB_RULE_PEER_PROGRESS_CHEAT_TESTING: "Peer Anti-Cheat fast test"
PCB_DESCRIPTION_PEER_PROGRESS_CHEAT_TESTING: "PeerBanHelper banned Peer to disconnect it from downloader for anti-cheat fast test, It will be un-banned soon."
4 changes: 3 additions & 1 deletion src/main/resources/lang/messages_fallback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,6 @@ IN_ECOMODE_DESCRIPTION: "EcoQoS API 加载成功,Windows 效率模式已应用
DOWNLOADER_TRANSMISSION_DISCOURAGE: "警告:Transmission 适配器已被废弃,不再推荐使用。在频繁发生封禁事件的种子上,频繁启停 Torrent 将导致对 Tracker 服务器的频繁更新,并间接引发 DoS 攻击,这会增加 Tracker 服务器压力并可能导致您的 IP 地址被 Tracker 服务器封禁。我们鼓励您尽可能迁移到其它下载器上。https://github.com/PBH-BTN/PeerBanHelper/issues/382"
DOWNLOADER_QBITTORRENTEE_SHADOWBANAPI_TEST_FAILURE: "您当前版本的 qBittorrentEE 不支持 ShadowBan 或未勾选 qBittorrentEE 设置中的 启用 ShadowBan,请关闭 ShadowBan 开关。"

DOWNLOADER_FAILED_REQUEST_STATISTICS: "获取下载器 {} 的统计数据信息失败: {}"
DOWNLOADER_FAILED_REQUEST_STATISTICS: "获取下载器 {} 的统计数据信息失败: {}"
PCB_RULE_PEER_PROGRESS_CHEAT_TESTING: "Peer 作弊快速测试"
PCB_DESCRIPTION_PEER_PROGRESS_CHEAT_TESTING: "PeerBanHelper 已短暂封禁此 Peer 以断开连接以便进行快速测试,它将很快在稍后解除封禁"
4 changes: 3 additions & 1 deletion src/main/resources/lang/zh_cn/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,6 @@ IN_ECOMODE_DESCRIPTION: "EcoQoS API 加载成功,Windows 效率模式已应用
DOWNLOADER_TRANSMISSION_DISCOURAGE: "警告:Transmission 适配器已被废弃,不再推荐使用。在频繁发生封禁事件的种子上,频繁启停 Torrent 将导致对 Tracker 服务器的频繁更新,并间接引发 DoS 攻击,这会增加 Tracker 服务器压力并可能导致您的 IP 地址被 Tracker 服务器封禁。我们鼓励您尽可能迁移到其它下载器上。https://github.com/PBH-BTN/PeerBanHelper/issues/382"
DOWNLOADER_QBITTORRENTEE_SHADOWBANAPI_TEST_FAILURE: "您当前版本的 qBittorrentEE 不支持 ShadowBan 或未勾选 qBittorrentEE 设置中的 启用 ShadowBan,请关闭 ShadowBan 开关。"

DOWNLOADER_FAILED_REQUEST_STATISTICS: "获取下载器 {} 的统计数据信息失败: {}"
DOWNLOADER_FAILED_REQUEST_STATISTICS: "获取下载器 {} 的统计数据信息失败: {}"
PCB_RULE_PEER_PROGRESS_CHEAT_TESTING: "Peer 作弊快速测试"
PCB_DESCRIPTION_PEER_PROGRESS_CHEAT_TESTING: "PeerBanHelper 正短暂封禁此 Peer 以便断开其连接,它将很快在稍后解除封禁"
Loading
Loading