diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java index e76333386..974335317 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java +++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java @@ -86,8 +86,9 @@ public class PeerBanHelperServer implements Reloadable { private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); @Getter private final List 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 @@ -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> LIVE_PEERS = new HashMap<>(); @Autowired @@ -467,18 +467,20 @@ public void banWave() { List 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()))); + } } }); }); @@ -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; } } diff --git a/src/main/java/com/ghostchu/peerbanhelper/btn/ability/BtnAbilitySubmitBans.java b/src/main/java/com/ghostchu/peerbanhelper/btn/ability/BtnAbilitySubmitBans.java index 0fa21d767..137d9f1c4 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/btn/ability/BtnAbilitySubmitBans.java +++ b/src/main/java/com/ghostchu/peerbanhelper/btn/ability/BtnAbilitySubmitBans.java @@ -90,6 +90,9 @@ private List 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()); diff --git a/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java b/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java index 1416d278a..93bd2e414 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java +++ b/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java @@ -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); diff --git a/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java b/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java index 16404b4b4..b7e43ddf7 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java +++ b/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java @@ -44,7 +44,7 @@ private void createTables() throws SQLException { private void performUpgrade() throws SQLException { Dao 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 { @@ -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); } diff --git a/src/main/java/com/ghostchu/peerbanhelper/database/dao/impl/ProgressCheatBlockerPersistDao.java b/src/main/java/com/ghostchu/peerbanhelper/database/dao/impl/ProgressCheatBlockerPersistDao.java index 351d4882d..ba616f378 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/database/dao/impl/ProgressCheatBlockerPersistDao.java +++ b/src/main/java/com/ghostchu/peerbanhelper/database/dao/impl/ProgressCheatBlockerPersistDao.java @@ -46,7 +46,8 @@ public List fetchFromDatabase(ProgressCheatBloc entity.getFirstTimeSeen().getTime(), entity.getLastTimeSeen().getTime(), entity.getDownloader(), - entity.getBanDelayWindowEndAt().getTime() + entity.getBanDelayWindowEndAt().getTime(), + entity.getFastPcbTestExecuteAt() ) ).collect(Collectors.toCollection(CopyOnWriteArrayList::new)); // 可变 List,需要并发安全 } @@ -70,7 +71,8 @@ public void flushDatabase(List 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 { diff --git a/src/main/java/com/ghostchu/peerbanhelper/database/table/ProgressCheatBlockerPersistEntity.java b/src/main/java/com/ghostchu/peerbanhelper/database/table/ProgressCheatBlockerPersistEntity.java index 0a1aa385b..7d6ddbe17 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/database/table/ProgressCheatBlockerPersistEntity.java +++ b/src/main/java/com/ghostchu/peerbanhelper/database/table/ProgressCheatBlockerPersistEntity.java @@ -37,4 +37,6 @@ public final class ProgressCheatBlockerPersistEntity { private String downloader; @DatabaseField(canBeNull = false) private Timestamp banDelayWindowEndAt; + @DatabaseField(canBeNull = false) + private long fastPcbTestExecuteAt; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/metric/impl/inmemory/InMemoryMetrics.java b/src/main/java/com/ghostchu/peerbanhelper/metric/impl/inmemory/InMemoryMetrics.java index 671df38e0..68bce2581 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/metric/impl/inmemory/InMemoryMetrics.java +++ b/src/main/java/com/ghostchu/peerbanhelper/metric/impl/inmemory/InMemoryMetrics.java @@ -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++; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/metric/impl/persist/PersistMetrics.java b/src/main/java/com/ghostchu/peerbanhelper/metric/impl/persist/PersistMetrics.java index 7efc531dd..7fa589abc 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/metric/impl/persist/PersistMetrics.java +++ b/src/main/java/com/ghostchu/peerbanhelper/metric/impl/persist/PersistMetrics.java @@ -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(() -> { @@ -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 } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/PeerAction.java b/src/main/java/com/ghostchu/peerbanhelper/module/PeerAction.java index b9f977708..0f131301b 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/PeerAction.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/PeerAction.java @@ -3,5 +3,6 @@ public enum PeerAction { NO_ACTION, BAN, + BAN_FOR_DISCONNECT, SKIP } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/AutoRangeBan.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/AutoRangeBan.java index 2a2e000a0..f5693b932 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/AutoRangeBan.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/AutoRangeBan.java @@ -99,6 +99,9 @@ private void reloadConfig() { peerAddress = peerAddress.toIPv4(); } for (Map.Entry bannedPeerEntry : getServer().getBannedPeers().entrySet()) { + if(bannedPeerEntry.getValue().isBanForDisconnect()){ + continue; + } PeerAddress bannedPeer = bannedPeerEntry.getKey(); IPAddress bannedAddress = bannedPeer.getAddress().withoutPrefixLength(); if (bannedAddress.isIPv4Convertible()) { diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/ProgressCheatBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/ProgressCheatBlocker.java index 5035cbe23..842179406 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/ProgressCheatBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/ProgressCheatBlocker.java @@ -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() { @@ -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"); } @@ -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; // 上传增量 @@ -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(); // 客户端汇报进度 @@ -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), @@ -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) { @@ -351,6 +370,7 @@ public static class ClientTask { private long lastTimeSeen; private String downloader; private long banDelayWindowEndAt; + private long fastPcbTestExecuteAt; } @AllArgsConstructor diff --git a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java index c20fbf543..1f84fc000 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java +++ b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java @@ -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(); diff --git a/src/main/java/com/ghostchu/peerbanhelper/wrapper/BanMetadata.java b/src/main/java/com/ghostchu/peerbanhelper/wrapper/BanMetadata.java index f66554416..7c5c40629 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/wrapper/BanMetadata.java +++ b/src/main/java/com/ghostchu/peerbanhelper/wrapper/BanMetadata.java @@ -16,25 +16,28 @@ public class BanMetadata extends PeerMetadata implements Comparable