From 2f7f213f1d8d97fb065482a029d5f3113341e03e Mon Sep 17 00:00:00 2001 From: PluieM <644202913@qq.com> Date: Wed, 19 Jun 2024 15:31:38 +0800 Subject: [PATCH 1/5] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=9F=BA=E4=BA=8E=E7=8A=B6=E6=80=81=E6=9C=BA=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E8=A7=84=E5=88=99=E6=A8=A1=E5=9D=97=202?= =?UTF-8?q?=E3=80=81=E5=B0=86=E8=A7=84=E5=88=99=E8=AE=A2=E9=98=85=E5=92=8C?= =?UTF-8?q?Peer=E9=BB=91=E5=90=8D=E5=8D=95=E7=9A=84=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=9F=BA=E4=BA=8E=E6=96=B0=E7=9A=84=E8=A7=84?= =?UTF-8?q?=E5=88=99=E8=A7=84=E5=88=99=E6=A8=A1=E5=9D=97=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=203=E3=80=81=E5=BE=AE=E8=B0=83=E8=A7=84?= =?UTF-8?q?=E5=88=99=E8=AE=A2=E9=98=85=E6=A8=A1=E5=9D=97=E6=96=B9=E4=BE=BF?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E5=8A=A0=E5=85=A5=E6=9B=B4=E5=A4=9A=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E8=A7=84=E5=88=99=E7=9A=84=E5=8C=B9=E9=85=8D=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 + .../peerbanhelper/PeerBanHelperServer.java | 110 +++++++++++---- .../module/AbstractRuleBlocker.java | 33 +++++ .../peerbanhelper/module/MatchEvents.java | 27 ++++ .../module/MatchResultDetail.java | 5 + .../peerbanhelper/module/PeerMatchRecord.java | 21 +++ .../peerbanhelper/module/PeerState.java | 27 ++++ .../peerbanhelper/module/RuleBlocker.java | 115 ++++++++++++++++ .../module/impl/rule/PeerIdBlocker.java | 70 ++++++++++ ...BlackRuleList.java => RuleSubBlocker.java} | 125 +++++++----------- .../module/impl/webapi/RuleSubController.java | 50 +++---- .../com/ghostchu/peerbanhelper/text/Lang.java | 2 + .../peerbanhelper/util/rule/RuleMatcher.java | 31 +++++ .../peerbanhelper/util/rule/SubRuleType.java | 19 +++ .../{IPBanMatcher.java => IPMatcher.java} | 23 ++-- src/main/resources/profile.yml | 4 +- 16 files changed, 523 insertions(+), 144 deletions(-) create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchRecord.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java rename src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/{IPBlackRuleList.java => RuleSubBlocker.java} (82%) create mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java rename src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/{IPBanMatcher.java => IPMatcher.java} (79%) diff --git a/pom.xml b/pom.xml index f29042df..3bfa71a1 100644 --- a/pom.xml +++ b/pom.xml @@ -403,5 +403,10 @@ provided linux + + com.alibaba.cola + cola-component-statemachine + 5.0.0 + diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java index 894d0369..8105f578 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java +++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java @@ -71,6 +71,8 @@ public class PeerBanHelperServer { @Getter private final long banDuration; @Getter + private final long disconnectDuration; + @Getter private final int httpdPort; @Getter private final boolean hideFinishLogs; @@ -95,19 +97,22 @@ public class PeerBanHelperServer { @Getter private DatabaseHelper databaseHelper; @Getter - private ModuleManager moduleManager; + private final ModuleManager moduleManager; @Getter @Nullable private IPDB ipdb = null; private WatchDog banWaveWatchDog; @Getter private JavalinWebContainer webContainer; - + @Getter + // private List matchRecords = new CopyOnWriteArrayList<>(); + private final Map matchRecords = new ConcurrentHashMap<>(); public PeerBanHelperServer(String pbhServerAddress, YamlConfiguration profile, YamlConfiguration mainConfig) throws SQLException { this.pbhServerAddress = pbhServerAddress; this.profile = profile; this.banDuration = profile.getLong("ban-duration"); + this.disconnectDuration = profile.getLong("disconnect-duration"); this.mainConfig = mainConfig; this.httpdPort = mainConfig.getInt("server.http"); this.hideFinishLogs = mainConfig.getBoolean("logger.hide-finish-log"); @@ -374,7 +379,7 @@ public void banWave() { banWaveWatchDog.setLastOperation("Reset last status"); // 声明基本集合 // 需要重启的种子列表 - Map> needRelaunched = new ConcurrentHashMap<>(); + Map> needRelaunched = new ConcurrentHashMap<>(); // 被解除封禁的对等体列表 banWaveWatchDog.setLastOperation("Remove expired bans"); Collection unbannedPeers = removeExpiredBans(); @@ -386,6 +391,43 @@ public void banWave() { // 更新 LIVE_PEERS 用于数据展示 banWaveWatchDog.setLastOperation("Update live peers"); executor.submit(() -> updateLivePeers(peers)); + // ===============基于 状态机 的封禁逻辑 + banWaveWatchDog.setLastOperation("Check Bans New"); + // long t1 = System.currentTimeMillis(); + // 断开连接超时剔除 + matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.ACTIVE && ele.getResult().expireTime() < System.currentTimeMillis()) + .map(ele -> ele.getPeer().getAddress()).forEach(matchRecords::remove); + // 封禁超时解禁后剔除 + matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.BAN && ele.getResult().expireTime() < System.currentTimeMillis()) + .map(ele -> ele.getPeer().getAddress()).forEach(ele -> { + unbanPeer(ele); + matchRecords.remove(ele); + }); + // 按模块并行检查标记Peer + List ruleBlockers = moduleManager.getModules().stream().filter(ele -> ele instanceof RuleBlocker).toList(); + CountDownLatch fsmLatch = new CountDownLatch(ruleBlockers.size()); + ruleBlockers.forEach(ele -> { + RuleBlocker blocker = (RuleBlocker) ele; + try (TimeoutProtect protect = new TimeoutProtect(ExceptedTime.CHECK_BANS.getTimeout(), (t) -> { + log.warn(Lang.TIMING_CHECK_BANS); + fsmLatch.countDown(); + })) { + protect.getService().submit(() -> { + blocker.runCheck(); + fsmLatch.countDown(); + }); + } + }); + fsmLatch.await(); + // 标记完成后将结果为Ban的Peer封禁 + banWaveWatchDog.setLastOperation("Add banned peers into banlist New"); + matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.BAN && !BAN_LIST.containsKey(ele.getPeer().getAddress())) + .forEach(record -> Optional.ofNullable(needRelaunched.get(record.getDownloader())).ifPresentOrElse(torrents -> + banPeer(record.getDownloader(), record.getTorrent(), record.getPeer(), record.getResult().moduleContext().getClass().getName(), record.getResult().rule(), record.getResult().reason(), bannedPeers, torrents), () -> { + List torrents = new ArrayList<>(); + banPeer(record.getDownloader(), record.getTorrent(), record.getPeer(), record.getResult().moduleContext().getClass().getName(), record.getResult().rule(), record.getResult().reason(), bannedPeers, torrents); + needRelaunched.put(record.getDownloader(), torrents); + })); // ========== 处理封禁逻辑 ========== Map> downloaderBanDetailMap = new ConcurrentHashMap<>(); banWaveWatchDog.setLastOperation("Check Bans"); @@ -399,25 +441,20 @@ public void banWave() { try (TimeoutProtect protect = new TimeoutProtect(ExceptedTime.ADD_BAN_ENTRY.getTimeout(), (t) -> { log.warn(Lang.TIMING_ADD_BANS); })) { - downloaderBanDetailMap.forEach((downloader, details) -> { - List relaunch = Collections.synchronizedList(new ArrayList<>()); - details.forEach(detail -> { - protect.getService().submit(() -> { + downloaderBanDetailMap.forEach((downloader, details) -> Optional.ofNullable(needRelaunched.get(downloader)).ifPresentOrElse(torrents -> + details.forEach(detail -> protect.getService().submit(() -> { if (detail.result().action() == PeerAction.BAN) { - IPDBResponse ipdbResponse = queryIPDB(detail.peer().getAddress()); - BanMetadata banMetadata = new BanMetadata(detail.result().moduleContext().getClass().getName(), downloader.getName(), - System.currentTimeMillis(), System.currentTimeMillis() + banDuration, - detail.torrent(), detail.peer(), detail.result().rule(), detail.result().reason(), - ipdbResponse.cityResponse(), ipdbResponse.asnResponse()); - bannedPeers.add(banMetadata); - relaunch.add(detail.torrent()); - banPeer(banMetadata, detail.torrent(), detail.peer()); - log.warn(Lang.BAN_PEER, detail.peer().getAddress(), detail.peer().getPeerId(), detail.peer().getClientName(), detail.peer().getProgress(), detail.peer().getUploaded(), detail.peer().getDownloaded(), detail.torrent().getName(), detail.result().reason()); + banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, torrents); } - }); - }); - needRelaunched.put(downloader, relaunch); - }); + })), () -> { + List torrents = new ArrayList<>(); + details.forEach(detail -> protect.getService().submit(() -> { + if (detail.result().action() == PeerAction.BAN) { + banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, torrents); + } + })); + needRelaunched.put(downloader, torrents); + })); } banWaveWatchDog.setLastOperation("Apply banlist"); // 如果需要,则应用更改封禁列表到下载器 @@ -435,6 +472,8 @@ public void banWave() { log.info(Lang.BAN_WAVE_CHECK_COMPLETED, downloadersCount, torrentsCount, peersCount, bannedPeers.size(), unbannedPeers.size(), System.currentTimeMillis() - startTimer); } banWaveWatchDog.setLastOperation("Completed"); + } catch (InterruptedException e) { + throw new RuntimeException(e); } finally { banWaveWatchDog.feed(); metrics.recordCheck(); @@ -474,6 +513,15 @@ private void updateLivePeers(Map>> peers) { torrent, p, ipdbResponse.cityResponse(), ipdbResponse.asnResponse() ); livePeers.put(address, metadata); + // 更新匹配记录 + if (matchRecords.containsKey(address)) { + PeerMatchRecord peerMatchRecord = matchRecords.get(address); + peerMatchRecord.setDownloader(downloader); + peerMatchRecord.setTorrent(torrent); + peerMatchRecord.setPeer(p); + } else { + matchRecords.put(address, new PeerMatchRecord(downloader, torrent, p, new MatchResultDetail(null, PeerState.INIT, "N/A", "no matches", 0))); + } }); }))); } @@ -534,7 +582,8 @@ public Collection removeExpiredBans() { private void registerModules() { log.info(Lang.WAIT_FOR_MODULES_STARTUP); moduleManager.register(new IPBlackList(this, profile)); - moduleManager.register(new PeerIdBlacklist(this, profile)); + //moduleManager.register(new PeerIdBlacklist(this, profile)); + moduleManager.register(new PeerIdBlocker(this, profile)); moduleManager.register(new ClientNameBlacklist(this, profile)); moduleManager.register(new ProgressCheatBlocker(this, profile)); moduleManager.register(new MultiDialingBlocker(this, profile)); @@ -542,7 +591,7 @@ private void registerModules() { moduleManager.register(new AutoRangeBan(this, profile)); moduleManager.register(new BtnNetworkOnline(this, profile)); moduleManager.register(new DownloaderCIDRBlockList(this, profile)); - moduleManager.register(new IPBlackRuleList(this, profile, databaseHelper)); + moduleManager.register(new RuleSubBlocker(this, profile, databaseHelper)); moduleManager.register(new PBHMetricsController(this, profile)); moduleManager.register(new PBHBanController(this, profile, databaseHelper)); moduleManager.register(new PBHMetadataController(this, profile)); @@ -674,10 +723,18 @@ public Map getBannedPeers() { /** * 以指定元数据封禁一个特定的对等体 * - * @param peer 对等体 IP 地址 - * @param banMetadata 封禁元数据 + * @param peer 对等体 IP 地址 */ - public void banPeer(@NotNull BanMetadata banMetadata, @NotNull Torrent torrentObj, @NotNull Peer peer) { + public void banPeer(@NotNull Downloader downloader, @NotNull Torrent torrent, @NotNull Peer peer, @NotNull String module, @NotNull String ruleName, @NotNull String reason, @NotNull Collection bannedPeers, @NotNull List relaunch) { + if (BAN_LIST.containsKey(peer.getAddress())) { + return; + } + relaunch.add(torrent); + IPDBResponse ipdbResponse = queryIPDB(peer.getAddress()); + BanMetadata banMetadata = new BanMetadata(module, downloader.getName(), + System.currentTimeMillis(), System.currentTimeMillis() + banDuration, + torrent, peer, ruleName, reason, ipdbResponse.cityResponse(), ipdbResponse.asnResponse()); + bannedPeers.add(banMetadata); BAN_LIST.put(peer.getAddress(), banMetadata); metrics.recordPeerBan(peer.getAddress(), banMetadata); banListInvoker.forEach(i -> i.add(peer.getAddress(), banMetadata)); @@ -697,7 +754,8 @@ public void banPeer(@NotNull BanMetadata banMetadata, @NotNull Torrent torrentOb } else { banMetadata.setReverseLookup("N/A"); } - Main.getEventBus().post(new PeerBanEvent(peer.getAddress(), banMetadata, torrentObj, peer)); + Main.getEventBus().post(new PeerBanEvent(peer.getAddress(), banMetadata, torrent, peer)); + log.warn(Lang.BAN_PEER, peer.getAddress(), peer.getPeerId(), peer.getClientName(), peer.getProgress(), peer.getUploaded(), peer.getDownloaded(), torrent.getName(), reason); } public List getDownloaders() { diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java new file mode 100644 index 00000000..8cf2c35b --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java @@ -0,0 +1,33 @@ +package com.ghostchu.peerbanhelper.module; + +import com.alibaba.cola.statemachine.StateMachine; +import com.ghostchu.peerbanhelper.PeerBanHelperServer; +import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; + +public abstract class AbstractRuleBlocker extends AbstractFeatureModule implements RuleBlocker { + + public StateMachine stateMachine; + + public AbstractRuleBlocker(PeerBanHelperServer server, YamlConfiguration profile) { + super(server, profile); + } + + @Override + public boolean isConfigurable() { + return true; + } + + @Override + public void onEnable() { + stateMachine = ruleSmBuilder().build(getConfigName()); + } + + @Override + public void onDisable() { + } + + @Override + public StateMachine getStateMachine() { + return stateMachine; + } +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java b/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java new file mode 100644 index 00000000..557364df --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java @@ -0,0 +1,27 @@ +package com.ghostchu.peerbanhelper.module; + +/** + * + */ +public enum MatchEvents { + /** + * 匹配规则 + */ + MATCH, + /** + * 命中规则 + */ + HIT, + /** + * 通过规则 + */ + PASS, + /** + * 断开 + */ + DISCONNECT, + /** + * 超时 + */ + TIMEOUT +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java b/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java new file mode 100644 index 00000000..a0c3e7b8 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java @@ -0,0 +1,5 @@ +package com.ghostchu.peerbanhelper.module; + +public record MatchResultDetail(FeatureModule moduleContext, PeerState state, String rule, String reason, + long expireTime) { +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchRecord.java b/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchRecord.java new file mode 100644 index 00000000..219f2a9d --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchRecord.java @@ -0,0 +1,21 @@ +package com.ghostchu.peerbanhelper.module; + +import com.ghostchu.peerbanhelper.downloader.Downloader; +import com.ghostchu.peerbanhelper.peer.Peer; +import com.ghostchu.peerbanhelper.torrent.Torrent; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Peer匹配记录 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PeerMatchRecord { + Downloader downloader; + Torrent torrent; + Peer peer; + MatchResultDetail result; +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java b/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java new file mode 100644 index 00000000..fbcc0c73 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java @@ -0,0 +1,27 @@ +package com.ghostchu.peerbanhelper.module; + +/** + * Peer状态 + */ +public enum PeerState { + /** + * 初始化 + */ + INIT, + /** + * 匹配 + */ + MATCH, + /** + * 封禁 + */ + BAN, + /** + * 活跃 + */ + ACTIVE, + /** + * 结束 + */ + END +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java new file mode 100644 index 00000000..15663c91 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java @@ -0,0 +1,115 @@ +package com.ghostchu.peerbanhelper.module; + +import com.alibaba.cola.statemachine.StateMachine; +import com.alibaba.cola.statemachine.builder.StateMachineBuilder; +import com.alibaba.cola.statemachine.builder.StateMachineBuilderFactory; +import com.ghostchu.peerbanhelper.PeerBanHelperServer; +import com.ghostchu.peerbanhelper.text.Lang; +import org.slf4j.Logger; + +import java.util.Optional; + +/** + * 基于状态机模型的规则模块接口 + */ +public interface RuleBlocker extends FeatureModule { + + /** + * 规则状态机构造器 + * + * @return 状态机构造器 + */ + default StateMachineBuilder ruleSmBuilder() { + StateMachineBuilder fsmBuilder = StateMachineBuilderFactory.create(); + fsmBuilder.externalTransitions().fromAmong(PeerState.INIT, PeerState.ACTIVE).to(PeerState.MATCH).on(MatchEvents.MATCH) + .perform((from, to, event, context) -> { + // 匹配 + CheckResult result = shouldBanPeer(context); + if (result.hit()) { + context.setResult(new MatchResultDetail(this, to, result.rule(), result.reason(), System.currentTimeMillis() + getServer().getDisconnectDuration())); + triggerEvent(MatchEvents.HIT, context); + } else { + context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectDuration())); + triggerEvent(MatchEvents.PASS, context); + } + }); + fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.ACTIVE).on(MatchEvents.PASS) + .perform((from, to, event, context) -> { + // 活跃 + context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectDuration())); + }); + fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.BAN).on(MatchEvents.HIT) + .perform((from, to, event, context) -> { + // 封禁 + getLogger().info(Lang.RULE_MODULE_PEER_BAN, getName(), context.getPeer().getAddress()); + context.setResult(new MatchResultDetail(this, to, context.getResult().rule(), context.getResult().reason(), System.currentTimeMillis() + getServer().getBanDuration())); + }); + /*fsmBuilder.externalTransition().from(PeerState.ACTIVE).to(PeerState.END).on(PeerEvents.DISCONNECT) + .perform((from, to, event, context) -> { + // 断开连接 + getLogger().info(Lang.RULE_MODULE_PEER_DISCONNECT, context.getPeer().getPeerId(), context.getPeer().getAddress().getIp(), context.getPeer().getAddress().getPort()); + context.setResult(new MatchResultDetail(this, to, null, null, 0)); + }); + fsmBuilder.externalTransition().from(PeerState.BAN).to(PeerState.END).on(PeerEvents.TIMEOUT) + .perform((from, to, event, context) -> { + // 解除封禁 + getLogger().info(Lang.RULE_MODULE_PEER_BAN_TIMEOUT, context.getPeer().getPeerId(), context.getPeer().getAddress().getIp(), context.getPeer().getAddress().getPort()); + context.setResult(new MatchResultDetail(this, to, null, null, 0)); + });*/ + return fsmBuilder; + } + + /** + * 获取Server + * + * @return Server + */ + PeerBanHelperServer getServer(); + + /** + * 获取Logger + * + * @return Logger + */ + Logger getLogger(); + + /** + * 获取状态机 + * + * @return 状态机 + */ + StateMachine getStateMachine(); + + /** + * 是否应该封禁Peer + * + * @param ctx 匹配上下文 + * @return 是否封禁 + */ + CheckResult shouldBanPeer(PeerMatchRecord ctx); + + /** + * 运行规则检查 + */ + default void runCheck() { + // long t1 = System.currentTimeMillis(); + getServer().getLivePeersSnapshot().keySet().forEach(peerAddress -> Optional.ofNullable(getServer().getMatchRecords().get(peerAddress)).ifPresent(record -> triggerEvent(MatchEvents.MATCH, record))); + // long t2 = System.currentTimeMillis(); + // getLogger().debug(Lang.RULE_MODULE_MATCH_TIME, getName(), t2 - t1); + } + + /** + * 触发事件 + * + * @param event 事件 + * @param context 上下文 + */ + default void triggerEvent(MatchEvents event, PeerMatchRecord context) { + getStateMachine().fireEvent(context.getResult().state(), event, context); + } + + record CheckResult(boolean hit, String rule, String reason) { + + } + +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java new file mode 100644 index 00000000..a517ef80 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java @@ -0,0 +1,70 @@ +package com.ghostchu.peerbanhelper.module.impl.rule; + +import com.ghostchu.peerbanhelper.PeerBanHelperServer; +import com.ghostchu.peerbanhelper.module.AbstractRuleBlocker; +import com.ghostchu.peerbanhelper.module.PeerMatchRecord; +import com.ghostchu.peerbanhelper.text.Lang; +import com.ghostchu.peerbanhelper.util.rule.Rule; +import com.ghostchu.peerbanhelper.util.rule.RuleMatchResult; +import com.ghostchu.peerbanhelper.util.rule.RuleParser; +import com.ghostchu.peerbanhelper.web.Role; +import io.javalin.http.Context; +import io.javalin.http.HttpStatus; +import lombok.extern.slf4j.Slf4j; +import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; + +import java.util.List; +import java.util.Map; + +@Slf4j +public class PeerIdBlocker extends AbstractRuleBlocker { + + private List bannedPeers; + + public PeerIdBlocker(PeerBanHelperServer server, YamlConfiguration profile) { + super(server, profile); + } + + @Override + public Logger getLogger() { + return log; + } + + @Override + public @NotNull String getName() { + return "PeerId Blocker"; + } + + @Override + public @NotNull String getConfigName() { + return "peer-id-blacklist"; + } + + @Override + public void onEnable() { + stateMachine = ruleSmBuilder().build(getConfigName()); + reloadConfig(); + getServer().getWebContainer().javalin() + .get("/api/modules/" + getConfigName(), this::handleWebAPI, Role.USER_READ); + } + + public void reloadConfig() { + this.bannedPeers = RuleParser.parse(getConfig().getStringList("banned-peer-id")); + } + + private void handleWebAPI(Context ctx) { + ctx.status(HttpStatus.OK); + ctx.json(Map.of("peerId", bannedPeers.stream().map(Rule::toPrintableText).toList())); + } + + @Override + public CheckResult shouldBanPeer(PeerMatchRecord ctx) { + RuleMatchResult matchResult = RuleParser.matchRule(bannedPeers, ctx.getPeer().getPeerId()); + if (matchResult.hit()) { + return new CheckResult(true, matchResult.rule().toString(), String.format(Lang.MODULE_PID_MATCH_PEER_ID, matchResult.rule())); + } + return new CheckResult(false, null, null); + } +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/IPBlackRuleList.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java similarity index 82% rename from src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/IPBlackRuleList.java rename to src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java index 80984a64..4b0d0236 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/IPBlackRuleList.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java @@ -5,18 +5,16 @@ import com.ghostchu.peerbanhelper.database.DatabaseHelper; import com.ghostchu.peerbanhelper.database.RuleSubInfo; import com.ghostchu.peerbanhelper.database.RuleSubLog; -import com.ghostchu.peerbanhelper.module.AbstractRuleFeatureModule; -import com.ghostchu.peerbanhelper.module.BanResult; +import com.ghostchu.peerbanhelper.module.AbstractRuleBlocker; import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; -import com.ghostchu.peerbanhelper.module.PeerAction; +import com.ghostchu.peerbanhelper.module.PeerMatchRecord; import com.ghostchu.peerbanhelper.module.impl.webapi.common.SlimMsg; -import com.ghostchu.peerbanhelper.peer.Peer; import com.ghostchu.peerbanhelper.text.Lang; -import com.ghostchu.peerbanhelper.torrent.Torrent; import com.ghostchu.peerbanhelper.util.HTTPUtil; import com.ghostchu.peerbanhelper.util.IPAddressUtil; import com.ghostchu.peerbanhelper.util.rule.MatchResult; -import com.ghostchu.peerbanhelper.util.rule.matcher.IPBanMatcher; +import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; +import com.ghostchu.peerbanhelper.util.rule.matcher.IPMatcher; import com.github.mizosoft.methanol.MutableRequest; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; @@ -27,6 +25,7 @@ import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection; import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; import java.io.File; import java.io.IOException; @@ -37,57 +36,46 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -/** - * IP黑名单远程订阅模块 - */ -@Getter @Slf4j -public class IPBlackRuleList extends AbstractRuleFeatureModule { +public class RuleSubBlocker extends AbstractRuleBlocker { final private DatabaseHelper db; - private List ipBanMatchers; + @Getter + private List ruleMatchers; + @Getter private long checkInterval = 86400000; // 默认24小时检查一次 private ScheduledExecutorService scheduledExecutorService; - public IPBlackRuleList(PeerBanHelperServer server, YamlConfiguration profile, DatabaseHelper db) { + public RuleSubBlocker(PeerBanHelperServer server, YamlConfiguration profile, DatabaseHelper db) { super(server, profile); this.db = db; } @Override - public boolean isConfigurable() { - return true; - } - - @Override - public boolean isCheckCacheable() { - return true; - } - - @Override - public boolean needCheckHandshake() { - return false; + public @NotNull String getName() { + return "Rule Sub Blocker"; } @Override - public @NotNull String getName() { - return "IP Blacklist Rule List"; + public @NotNull String getConfigName() { + return "rule-sub-blockers"; } @Override - public @NotNull String getConfigName() { - return "ip-address-blocker-rules"; + public Logger getLogger() { + return log; } @Override public void onEnable() { + stateMachine = ruleSmBuilder().build(getConfigName()); ConfigurationSection config = getConfig(); // 读取检查间隔 checkInterval = config.getLong("check-interval", checkInterval); @@ -95,47 +83,12 @@ public void onEnable() { scheduledExecutorService.scheduleAtFixedRate(this::reloadConfig, 0, checkInterval, TimeUnit.MILLISECONDS); } - @Override - public void onDisable() { - } - - @Override - public @NotNull BanResult shouldBanPeer(@NotNull Torrent torrent, @NotNull Peer peer, @NotNull ExecutorService ruleExecuteExecutor) { - long t1 = System.currentTimeMillis(); - String ip = peer.getAddress().getIp(); - List results = new ArrayList<>(); - try (var service = Executors.newVirtualThreadPerTaskExecutor()) { - ipBanMatchers.forEach(rule -> service.submit(() -> { - results.add(new IPBanResult(rule.getRuleName(), rule.match(ip))); - })); - } - AtomicReference matchRule = new AtomicReference<>(); - boolean mr = results.stream().anyMatch(ipBanResult -> { - try { - boolean match = ipBanResult.matchResult() == MatchResult.TRUE; - if (match) { - matchRule.set(ipBanResult); - } - return match; - } catch (Exception e) { - log.error(Lang.IP_BAN_RULE_MATCH_ERROR, e); - return false; - } - }); - long t2 = System.currentTimeMillis(); - log.debug(Lang.IP_BAN_RULE_MATCH_TIME, t2 - t1); - if (mr) { - return new BanResult(this, PeerAction.BAN, ip, String.format(Lang.MODULE_IBL_MATCH_IP_RULE, matchRule.get().ruleName())); - } - return new BanResult(this, PeerAction.NO_ACTION, "N/A", "No matches"); - } - /** * Reload the configuration for this module. */ private void reloadConfig() { - if (null == ipBanMatchers) { - ipBanMatchers = new ArrayList<>(); + if (null == ruleMatchers) { + ruleMatchers = new ArrayList<>(); } ConfigurationSection config = getConfig(); // 读取检查间隔 @@ -152,6 +105,27 @@ private void reloadConfig() { } } + @Override + public CheckResult shouldBanPeer(PeerMatchRecord ctx) { + String ip = ctx.getPeer().getAddress().getIp(); + AtomicReference result = new AtomicReference<>(new CheckResult(false, null, null)); + CountDownLatch latch = new CountDownLatch(ruleMatchers.size()); + try (var service = Executors.newVirtualThreadPerTaskExecutor()) { + ruleMatchers.forEach(matcher -> service.submit(() -> { + if (matcher.match(ip) == MatchResult.TRUE) { + result.set(new CheckResult(true, matcher.getRuleName(), Lang.MODULE_IBL_MATCH_IP_RULE.replace("{}", matcher.getRuleName()))); + } + latch.countDown(); + })); + } + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return result.get(); + } + /** * 更新规则 * @@ -162,7 +136,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp String ruleId = rule.getName(); if (!rule.getBoolean("enabled", false)) { // 检查ipBanMatchers是否有对应的规则,有则删除 - ipBanMatchers.removeIf(ele -> ele.getRuleId().equals(ruleId)); + ruleMatchers.removeIf(ele -> ele.getRuleId().equals(ruleId)); // 未启用跳过更新逻辑 return new SlimMsg(false, Lang.IP_BAN_RULE_DISABLED.replace("{}", ruleId)); } @@ -183,10 +157,10 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp // 加载远程订阅文件出错,尝试从本地缓存中加载 if (ruleFile.exists()) { // 如果一致,但ipBanMatchers没有对应的规则内容,则加载内容 - if (ipBanMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { + if (ruleMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { try { fileToIPList(name, ruleFile, ipAddresses, subnetAddresses); - ipBanMatchers.add(new IPBanMatcher(ruleId, name, ipAddresses, subnetAddresses)); + ruleMatchers.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); log.warn(Lang.IP_BAN_RULE_USE_CACHE, name); result.set(new SlimMsg(false, Lang.IP_BAN_RULE_USE_CACHE.replace("{}", name))); } catch (IOException ex) { @@ -217,7 +191,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp tempFile.renameTo(ruleFile); } else { // 如果一致,但ipBanMatchers没有对应的规则内容,则加载内容 - if (ipBanMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { + if (ruleMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { ent_count = fileToIPList(name, tempFile, ipAddresses, subnetAddresses); } else { log.info(Lang.IP_BAN_RULE_NO_UPDATE, name); @@ -228,12 +202,12 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp // ip列表或者subnet列表不为空代表需要更新matcher if (!ipAddresses.isEmpty() || !subnetAddresses.isEmpty()) { // 如果已经存在则更新,否则添加 - ipBanMatchers.stream().filter(ele -> ele.getRuleId().equals(ruleId)).findFirst().ifPresentOrElse(ele -> { + ruleMatchers.stream().filter(ele -> ele.getRuleId().equals(ruleId)).findFirst().ifPresentOrElse(ele -> { ele.setData(name, ipAddresses, subnetAddresses); log.info(Lang.IP_BAN_RULE_UPDATE_SUCCESS, name); result.set(new SlimMsg(true, Lang.IP_BAN_RULE_UPDATE_SUCCESS.replace("{}", name))); }, () -> { - ipBanMatchers.add(new IPBanMatcher(ruleId, name, ipAddresses, subnetAddresses)); + ruleMatchers.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); log.info(Lang.IP_BAN_RULE_LOAD_SUCCESS, name); result.set(new SlimMsg(true, Lang.IP_BAN_RULE_LOAD_SUCCESS.replace("{}", name))); }); @@ -400,9 +374,4 @@ public void changeCheckInterval(long checkInterval) throws IOException { scheduledExecutorService = Executors.newScheduledThreadPool(1, r -> Thread.ofVirtual().name("IPBlackRuleList - Update Thread").unstarted(r)); scheduledExecutorService.scheduleAtFixedRate(this::reloadConfig, 0, checkInterval, TimeUnit.MILLISECONDS); } - - record IPBanResult(String ruleName, MatchResult matchResult) { - } } - - diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java index 7640f027..20f565d4 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java @@ -4,7 +4,7 @@ import com.ghostchu.peerbanhelper.database.RuleSubInfo; import com.ghostchu.peerbanhelper.module.AbstractFeatureModule; import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; -import com.ghostchu.peerbanhelper.module.impl.rule.IPBlackRuleList; +import com.ghostchu.peerbanhelper.module.impl.rule.RuleSubBlocker; import com.ghostchu.peerbanhelper.module.impl.webapi.common.SlimMsg; import com.ghostchu.peerbanhelper.module.impl.webapi.common.StdMsg; import com.ghostchu.peerbanhelper.text.Lang; @@ -25,7 +25,7 @@ @Slf4j public class RuleSubController extends AbstractFeatureModule { - IPBlackRuleList ipBlackRuleList; + RuleSubBlocker ruleSubBlocker; public RuleSubController(PeerBanHelperServer server, YamlConfiguration profile) { super(server, profile); @@ -48,8 +48,8 @@ public boolean isConfigurable() { @Override public void onEnable() { - getServer().getModuleManager().getModules().stream().filter(ele -> ele.getConfigName().equals("ip-address-blocker-rules")).findFirst().ifPresent(ele -> { - ipBlackRuleList = (IPBlackRuleList) ele; + getServer().getModuleManager().getModules().stream().filter(ele -> ele instanceof RuleSubBlocker).findFirst().ifPresent(ele -> { + ruleSubBlocker = (RuleSubBlocker) ele; getServer().getWebContainer().javalin() // 查询检查间隔 .get("/api/sub/interval", this::getCheckInterval, Role.USER_READ) @@ -88,7 +88,7 @@ public void onDisable() { * @param ctx 上下文 */ private void getCheckInterval(Context ctx) { - ctx.json(new StdMsg(true, Lang.IP_BAN_RULE_CHECK_INTERVAL_QUERY_SUCCESS, ipBlackRuleList.getCheckInterval())); + ctx.json(new StdMsg(true, Lang.IP_BAN_RULE_CHECK_INTERVAL_QUERY_SUCCESS, ruleSubBlocker.getCheckInterval())); } /** @@ -99,7 +99,7 @@ private void getCheckInterval(Context ctx) { private void changeCheckInterval(Context ctx) { try { long interval = JsonUtil.readObject(ctx.body()).get("checkInterval").getAsLong(); - ipBlackRuleList.changeCheckInterval(interval); + ruleSubBlocker.changeCheckInterval(interval); ctx.json(new SlimMsg(true, Lang.IP_BAN_RULE_CHECK_INTERVAL_UPDATED)); } catch (Exception e) { ctx.status(HttpStatus.BAD_REQUEST); @@ -122,8 +122,8 @@ private void logs(Context ctx, String ruleId) { Map map = new HashMap<>(); map.put("pageIndex", pageIndex); map.put("pageSize", pageSize); - map.put("results", ipBlackRuleList.queryRuleSubLogs(ruleId, pageIndex, pageSize)); - map.put("total", ipBlackRuleList.countRuleSubLogs(ruleId)); + map.put("results", ruleSubBlocker.queryRuleSubLogs(ruleId, pageIndex, pageSize)); + map.put("total", ruleSubBlocker.countRuleSubLogs(ruleId)); ctx.json(new StdMsg(true, Lang.IP_BAN_RULE_LOG_QUERY_SUCCESS, map)); } catch (Exception e) { log.error(Lang.IP_BAN_RULE_LOG_QUERY_ERROR, e); @@ -139,7 +139,7 @@ private void logs(Context ctx, String ruleId) { */ private SlimMsg updateAll() { AtomicReference result = new AtomicReference<>(); - ipBlackRuleList.getRuleSubsConfig().getKeys(false).stream().map(this::update).filter(ele -> !ele.success()).findFirst().ifPresentOrElse(result::set, () -> result.set(new SlimMsg(true, Lang.IP_BAN_RULE_ALL_UPDATED))); + ruleSubBlocker.getRuleSubsConfig().getKeys(false).stream().map(this::update).filter(ele -> !ele.success()).findFirst().ifPresentOrElse(result::set, () -> result.set(new SlimMsg(true, Lang.IP_BAN_RULE_ALL_UPDATED))); return result.get(); } @@ -153,11 +153,11 @@ private SlimMsg update(String ruleId) { if (ruleId == null || ruleId.isEmpty()) { return new SlimMsg(false, Lang.IP_BAN_RULE_NO_ID); } - ConfigurationSection configurationSection = ipBlackRuleList.getRuleSubsConfig().getConfigurationSection(ruleId); + ConfigurationSection configurationSection = ruleSubBlocker.getRuleSubsConfig().getConfigurationSection(ruleId); if (null == configurationSection) { return new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId)); } - return ipBlackRuleList.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + return ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); } /** @@ -175,7 +175,7 @@ private void switcher(Context ctx) throws SQLException, IOException { ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_ENABLED_WRONG_PARAM)); return; } - RuleSubInfo ruleSubInfo = ipBlackRuleList.getRuleSubInfo(ruleId); + RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId))); @@ -183,8 +183,8 @@ private void switcher(Context ctx) throws SQLException, IOException { } String msg = (enabled ? Lang.IP_BAN_RULE_ENABLED : Lang.IP_BAN_RULE_DISABLED).replace("{}", ruleSubInfo.ruleName()); if (enabled != ruleSubInfo.enabled()) { - ConfigurationSection configurationSection = ipBlackRuleList.saveRuleSubInfo(new RuleSubInfo(ruleId, enabled, ruleSubInfo.ruleName(), ruleSubInfo.subUrl(), 0, 0)); - ipBlackRuleList.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + ConfigurationSection configurationSection = ruleSubBlocker.saveRuleSubInfo(new RuleSubInfo(ruleId, enabled, ruleSubInfo.ruleName(), ruleSubInfo.subUrl(), 0, 0)); + ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); log.info(msg); ctx.json(new SlimMsg(true, msg)); } else { @@ -200,14 +200,14 @@ private void switcher(Context ctx) throws SQLException, IOException { */ private void delete(Context ctx) throws IOException, SQLException { String ruleId = ctx.pathParam("ruleId"); - RuleSubInfo ruleSubInfo = ipBlackRuleList.getRuleSubInfo(ruleId); + RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId))); return; } - ipBlackRuleList.deleteRuleSubInfo(ruleId); - ipBlackRuleList.getIpBanMatchers().removeIf(ele -> ele.getRuleId().equals(ruleId)); + ruleSubBlocker.deleteRuleSubInfo(ruleId); + ruleSubBlocker.getRuleMatchers().removeIf(ele -> ele.getRuleId().equals(ruleId)); String msg = Lang.IP_BAN_RULE_DELETED.replace("{}", ruleSubInfo.ruleName()); log.info(msg); ctx.json(new SlimMsg(true, msg)); @@ -230,7 +230,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_NO_ID)); return; } - RuleSubInfo ruleSubInfo = ipBlackRuleList.getRuleSubInfo(ruleId); + RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (isAdd && ruleSubInfo != null) { // 新增时检查规则是否存在 ctx.status(HttpStatus.BAD_REQUEST); @@ -259,10 +259,10 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException subUrl = ruleSubInfo.subUrl(); } } - ConfigurationSection configurationSection = ipBlackRuleList.saveRuleSubInfo(new RuleSubInfo(ruleId, isAdd || ruleSubInfo.enabled(), ruleName, subUrl, 0, 0)); + ConfigurationSection configurationSection = ruleSubBlocker.saveRuleSubInfo(new RuleSubInfo(ruleId, isAdd || ruleSubInfo.enabled(), ruleName, subUrl, 0, 0)); assert configurationSection != null; try { - SlimMsg msg = ipBlackRuleList.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + SlimMsg msg = ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); if (!msg.success()) { ctx.status(HttpStatus.BAD_REQUEST); ctx.json(msg); @@ -272,9 +272,9 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException } catch (Exception e) { // 更新失败时回滚 if (isAdd) { - ipBlackRuleList.deleteRuleSubInfo(ruleId); + ruleSubBlocker.deleteRuleSubInfo(ruleId); } else { - ipBlackRuleList.saveRuleSubInfo(ruleSubInfo); + ruleSubBlocker.saveRuleSubInfo(ruleSubInfo); } ctx.status(HttpStatus.BAD_REQUEST); ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_URL_WRONG.replace("{}", ruleName))); @@ -288,7 +288,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException * @return 响应 */ private StdMsg get(String ruleId) throws SQLException { - return new StdMsg(true, Lang.IP_BAN_RULE_INFO_QUERY_SUCCESS, ipBlackRuleList.getRuleSubInfo(ruleId)); + return new StdMsg(true, Lang.IP_BAN_RULE_INFO_QUERY_SUCCESS, ruleSubBlocker.getRuleSubInfo(ruleId)); } /** @@ -297,10 +297,10 @@ private StdMsg get(String ruleId) throws SQLException { * @return 响应 */ private StdMsg list() throws SQLException { - List list = ipBlackRuleList.getRuleSubsConfig().getKeys(false).stream().toList(); + List list = ruleSubBlocker.getRuleSubsConfig().getKeys(false).stream().toList(); List data = new ArrayList<>(list.size()); for (String s : list) { - data.add(ipBlackRuleList.getRuleSubInfo(s)); + data.add(ruleSubBlocker.getRuleSubInfo(s)); } return new StdMsg(true, Lang.IP_BAN_RULE_INFO_QUERY_SUCCESS, data); } diff --git a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java index b2f7e123..5fad4b5f 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java +++ b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java @@ -211,4 +211,6 @@ public class Lang { public static final String GUI_TITLE_LOADED = "PeerBanHelper (%s) - %s (%s)"; public static final String WEBVIEW_DISABLED_WEBKIT_NOT_INCLUDED = "未找到 JavaFx Web 模块,您正在使用精简构建,WebUI 选项卡未启用"; public static final String WEBVIEW_ENABLED = "已找到 JavaFx Web,WebUI 选项卡已启用"; + public static final String RULE_MODULE_MATCH_TIME = "规则模块 {} 匹配花费时间:{}ms"; + public static final String RULE_MODULE_PEER_BAN = "[封禁] 规则模块 {} : {} "; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java new file mode 100644 index 00000000..7a8819cf --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java @@ -0,0 +1,31 @@ +package com.ghostchu.peerbanhelper.util.rule; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +/** + * 规则Matcher + */ +@Getter +@Slf4j +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public abstract class RuleMatcher extends AbstractMatcher { + + private final String ruleId; + + private final SubRuleType ruleType; + + protected String ruleName; + + public RuleMatcher(SubRuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + this.ruleType = ruleType; + this.ruleId = ruleId; + setData(ruleName, ruleData); + } + + public abstract void setData(String ruleName, Object... ruleData); + +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java new file mode 100644 index 00000000..f0af99af --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java @@ -0,0 +1,19 @@ +package com.ghostchu.peerbanhelper.util.rule; + +/** + * 订阅规则类型 + */ +public enum SubRuleType { + /** + * IP/CIDR + */ + IP, + /** + * 子串 + */ + SUBSTR, + /** + * 前缀 + */ + PREFIX +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPBanMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java similarity index 79% rename from src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPBanMatcher.java rename to src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java index e46e50f6..c6f9c4ea 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPBanMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java @@ -2,13 +2,13 @@ import com.ghostchu.peerbanhelper.text.Lang; import com.ghostchu.peerbanhelper.util.IPAddressUtil; -import com.ghostchu.peerbanhelper.util.rule.AbstractMatcher; import com.ghostchu.peerbanhelper.util.rule.MatchResult; +import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; +import com.ghostchu.peerbanhelper.util.rule.SubRuleType; import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import inet.ipaddr.IPAddress; import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @@ -20,26 +20,21 @@ @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IPBanMatcher extends AbstractMatcher { +public class IPMatcher extends RuleMatcher { - @Getter - private final String ruleId; - - @Getter - private String ruleName; private List subnets; private List ips; private BloomFilter bloomFilter; - public IPBanMatcher(String ruleId, String ruleName, List ips, List subnets) { - this.ruleId = ruleId; - setData(ruleName, ips, subnets); + public IPMatcher(String ruleId, String ruleName, Object... ruleData) { + super(SubRuleType.IP, ruleId, ruleName, ruleData); } - public void setData(String ruleName, List ips, List subnets) { + @Override + public void setData(String ruleName, Object... ruleData) { this.ruleName = ruleName; - this.ips = ips; - this.subnets = subnets; + this.ips = ruleData.length > 0 ? (List) ruleData[0] : List.of(); + this.subnets = ruleData.length > 1 ? (List) ruleData[1] : List.of(); bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), ips.size(), 0.01); ips.forEach(ip -> bloomFilter.put(ip.toString())); } diff --git a/src/main/resources/profile.yml b/src/main/resources/profile.yml index 20959fec..5d2614fa 100644 --- a/src/main/resources/profile.yml +++ b/src/main/resources/profile.yml @@ -5,6 +5,8 @@ check-interval: 5000 update-live-peers-interval: 13000 # 封禁持续时间(单位:毫秒) ban-duration: 86400000 +# 断开连接判定时间(单位:毫秒) +disconnect-duration: 60000 # 功能模块 module: # PeerId 封禁 @@ -162,7 +164,7 @@ module: # keep-hunting为true时有效,和cache-lifespan相似,对被猎杀IP的缓存持续时间 keep-hunting-time: 2592000 # 订阅规则 - ip-address-blocker-rules: + rule-sub-blockers: enabled: false # 检查间隔 check-interval: 86400000 # 24小时检查一次 毫秒 From b0cce39469b6770e7934a424bcbbc8bb89a71151 Mon Sep 17 00:00:00 2001 From: PluieM <644202913@qq.com> Date: Wed, 19 Jun 2024 20:54:22 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E8=A7=84=E5=88=99=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=AE=A2=E9=98=85=E4=BB=A5=E4=B8=8B=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E7=B1=BB=E5=9E=8B=20IP/PEER=5FID=5FCONTAINS/PEER=5FID?= =?UTF-8?q?=5FSTARTS=5FWITH/CLIENT=5FNAME=5FCONTAINS/CLIENT=5FNAME=5FSTART?= =?UTF-8?q?S=5FWITH?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../peerbanhelper/PeerBanHelperServer.java | 5 +- .../config/ProfileUpdateScript.java | 11 +- .../database/DatabaseHelper.java | 6 +- .../peerbanhelper/database/RuleSubLog.java | 4 +- .../module/AbstractRuleBlocker.java | 10 ++ .../peerbanhelper/module/RuleBlocker.java | 8 +- ...uleUpdateType.java => RuleUpdateType.java} | 2 +- .../module/impl/rule/PeerIdBlocker.java | 9 +- .../module/impl/rule/RuleSubBlocker.java | 165 ++++++++++++------ .../module/impl/webapi/RuleSubController.java | 54 +++--- .../com/ghostchu/peerbanhelper/text/Lang.java | 57 +++--- .../peerbanhelper/util/rule/RuleMatcher.java | 12 +- .../peerbanhelper/util/rule/RuleType.java | 27 +++ .../peerbanhelper/util/rule/SubRuleType.java | 19 -- .../util/rule/matcher/IPMatcher.java | 12 +- .../util/rule/matcher/PrefixMatcher.java | 48 +++++ .../util/rule/matcher/SubStrMatcher.java | 48 +++++ src/main/resources/profile.yml | 2 + 18 files changed, 341 insertions(+), 158 deletions(-) rename src/main/java/com/ghostchu/peerbanhelper/module/{IPBanRuleUpdateType.java => RuleUpdateType.java} (66%) create mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleType.java delete mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java create mode 100644 src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java index 8105f578..2e7087e8 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java +++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java @@ -71,7 +71,7 @@ public class PeerBanHelperServer { @Getter private final long banDuration; @Getter - private final long disconnectDuration; + private final long disconnectTimeout; @Getter private final int httpdPort; @Getter @@ -105,14 +105,13 @@ public class PeerBanHelperServer { @Getter private JavalinWebContainer webContainer; @Getter - // private List matchRecords = new CopyOnWriteArrayList<>(); private final Map matchRecords = new ConcurrentHashMap<>(); public PeerBanHelperServer(String pbhServerAddress, YamlConfiguration profile, YamlConfiguration mainConfig) throws SQLException { this.pbhServerAddress = pbhServerAddress; this.profile = profile; this.banDuration = profile.getLong("ban-duration"); - this.disconnectDuration = profile.getLong("disconnect-duration"); + this.disconnectTimeout = profile.getLong("disconnect-timeout"); this.mainConfig = mainConfig; this.httpdPort = mainConfig.getInt("server.http"); this.hideFinishLogs = mainConfig.getBoolean("logger.hide-finish-log"); diff --git a/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java b/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java index c2e1befa..95ed445a 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java +++ b/src/main/java/com/ghostchu/peerbanhelper/config/ProfileUpdateScript.java @@ -3,6 +3,7 @@ import com.ghostchu.peerbanhelper.text.Lang; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; +import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection; import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; import java.util.ArrayList; @@ -18,6 +19,15 @@ public ProfileUpdateScript(YamlConfiguration conf) { this.conf = conf; } + @UpdateScript(version = 8) + public void subModuleUpdate() { + conf.set("disconnect-timeout", 60000); + conf.set("module.rule-sub-blockers", conf.getConfigurationSection("module.ip-address-blocker-rules")); + conf.set("module.ip-address-blocker-rules", null); + ConfigurationSection rules = conf.getConfigurationSection("module.rule-sub-blockers.rules"); + rules.getKeys(false).forEach(ruleId -> rules.getConfigurationSection(ruleId).set("type", "IP")); + } + @UpdateScript(version = 7) public void progressCheckerIPPrefixLength() { conf.set("module.progress-cheat-blocker.ipv4-prefix-length", 32); @@ -33,7 +43,6 @@ public void subModule() { conf.set("module.ip-address-blocker-rules.rules.example-rule.url", "https://example.com/example.txt"); } - @UpdateScript(version = 4) public void ipDatabase() { conf.set("module.ip-address-blocker.asns", new ArrayList<>()); diff --git a/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java b/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java index 80ddd70a..5eb43714 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java +++ b/src/main/java/com/ghostchu/peerbanhelper/database/DatabaseHelper.java @@ -1,6 +1,6 @@ package com.ghostchu.peerbanhelper.database; -import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; +import com.ghostchu.peerbanhelper.module.RuleUpdateType; import com.ghostchu.peerbanhelper.text.Lang; import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; @@ -366,7 +366,7 @@ public List queryRuleSubLogs(String ruleId, int pageIndex, int pageS set.getString("rule_id"), set.getLong("update_time"), set.getInt("ent_count"), - IPBanRuleUpdateType.valueOf(set.getString("update_type")) + RuleUpdateType.valueOf(set.getString("update_type")) )); } return infos; @@ -383,7 +383,7 @@ public List queryRuleSubLogs(String ruleId, int pageIndex, int pageS * @param updateType 更新类型 * @throws SQLException SQL异常 */ - public void insertRuleSubLog(String ruleId, int count, IPBanRuleUpdateType updateType) throws SQLException { + public void insertRuleSubLog(String ruleId, int count, RuleUpdateType updateType) throws SQLException { try (Connection connection = manager.getConnection()) { PreparedStatement ps; ps = connection.prepareStatement("INSERT INTO rule_sub_logs (rule_id, update_time, ent_count, update_type) VALUES (?,?,?,?)"); diff --git a/src/main/java/com/ghostchu/peerbanhelper/database/RuleSubLog.java b/src/main/java/com/ghostchu/peerbanhelper/database/RuleSubLog.java index ab557f50..d4658267 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/database/RuleSubLog.java +++ b/src/main/java/com/ghostchu/peerbanhelper/database/RuleSubLog.java @@ -1,11 +1,11 @@ package com.ghostchu.peerbanhelper.database; -import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; +import com.ghostchu.peerbanhelper.module.RuleUpdateType; public record RuleSubLog( String ruleId, long updateTime, int count, - IPBanRuleUpdateType updateType + RuleUpdateType updateType ) { } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java index 8cf2c35b..a342e945 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java @@ -2,10 +2,17 @@ import com.alibaba.cola.statemachine.StateMachine; import com.ghostchu.peerbanhelper.PeerBanHelperServer; +import com.ghostchu.peerbanhelper.util.rule.Rule; +import lombok.Getter; import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; +import java.util.List; + public abstract class AbstractRuleBlocker extends AbstractFeatureModule implements RuleBlocker { + @Getter + public List rules; + public StateMachine stateMachine; public AbstractRuleBlocker(PeerBanHelperServer server, YamlConfiguration profile) { @@ -20,6 +27,7 @@ public boolean isConfigurable() { @Override public void onEnable() { stateMachine = ruleSmBuilder().build(getConfigName()); + init(); } @Override @@ -30,4 +38,6 @@ public void onDisable() { public StateMachine getStateMachine() { return stateMachine; } + + public abstract void init(); } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java index 15663c91..afc06789 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java @@ -26,22 +26,22 @@ default StateMachineBuilder ruleSmBuild // 匹配 CheckResult result = shouldBanPeer(context); if (result.hit()) { - context.setResult(new MatchResultDetail(this, to, result.rule(), result.reason(), System.currentTimeMillis() + getServer().getDisconnectDuration())); + context.setResult(new MatchResultDetail(this, to, result.rule(), result.reason(), System.currentTimeMillis() + getServer().getDisconnectTimeout())); triggerEvent(MatchEvents.HIT, context); } else { - context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectDuration())); + context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectTimeout())); triggerEvent(MatchEvents.PASS, context); } }); fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.ACTIVE).on(MatchEvents.PASS) .perform((from, to, event, context) -> { // 活跃 - context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectDuration())); + context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectTimeout())); }); fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.BAN).on(MatchEvents.HIT) .perform((from, to, event, context) -> { // 封禁 - getLogger().info(Lang.RULE_MODULE_PEER_BAN, getName(), context.getPeer().getAddress()); + getLogger().debug(Lang.RULE_MODULE_PEER_BAN, getName(), context.getPeer().getAddress()); context.setResult(new MatchResultDetail(this, to, context.getResult().rule(), context.getResult().reason(), System.currentTimeMillis() + getServer().getBanDuration())); }); /*fsmBuilder.externalTransition().from(PeerState.ACTIVE).to(PeerState.END).on(PeerEvents.DISCONNECT) diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/IPBanRuleUpdateType.java b/src/main/java/com/ghostchu/peerbanhelper/module/RuleUpdateType.java similarity index 66% rename from src/main/java/com/ghostchu/peerbanhelper/module/IPBanRuleUpdateType.java rename to src/main/java/com/ghostchu/peerbanhelper/module/RuleUpdateType.java index 72f70ac1..f1daa49f 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/IPBanRuleUpdateType.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/RuleUpdateType.java @@ -1,6 +1,6 @@ package com.ghostchu.peerbanhelper.module; -public enum IPBanRuleUpdateType { +public enum RuleUpdateType { AUTO, MANUAL } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java index a517ef80..376c7574 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/PeerIdBlocker.java @@ -43,17 +43,12 @@ public Logger getLogger() { } @Override - public void onEnable() { - stateMachine = ruleSmBuilder().build(getConfigName()); - reloadConfig(); + public void init() { + this.bannedPeers = RuleParser.parse(getConfig().getStringList("banned-peer-id")); getServer().getWebContainer().javalin() .get("/api/modules/" + getConfigName(), this::handleWebAPI, Role.USER_READ); } - public void reloadConfig() { - this.bannedPeers = RuleParser.parse(getConfig().getStringList("banned-peer-id")); - } - private void handleWebAPI(Context ctx) { ctx.status(HttpStatus.OK); ctx.json(Map.of("peerId", bannedPeers.stream().map(Rule::toPrintableText).toList())); diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java index 4b0d0236..a815016f 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java @@ -6,15 +6,18 @@ import com.ghostchu.peerbanhelper.database.RuleSubInfo; import com.ghostchu.peerbanhelper.database.RuleSubLog; import com.ghostchu.peerbanhelper.module.AbstractRuleBlocker; -import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; import com.ghostchu.peerbanhelper.module.PeerMatchRecord; +import com.ghostchu.peerbanhelper.module.RuleUpdateType; import com.ghostchu.peerbanhelper.module.impl.webapi.common.SlimMsg; import com.ghostchu.peerbanhelper.text.Lang; import com.ghostchu.peerbanhelper.util.HTTPUtil; import com.ghostchu.peerbanhelper.util.IPAddressUtil; import com.ghostchu.peerbanhelper.util.rule.MatchResult; import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; +import com.ghostchu.peerbanhelper.util.rule.RuleType; import com.ghostchu.peerbanhelper.util.rule.matcher.IPMatcher; +import com.ghostchu.peerbanhelper.util.rule.matcher.PrefixMatcher; +import com.ghostchu.peerbanhelper.util.rule.matcher.SubStrMatcher; import com.github.mizosoft.methanol.MutableRequest; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; @@ -47,8 +50,7 @@ public class RuleSubBlocker extends AbstractRuleBlocker { final private DatabaseHelper db; - @Getter - private List ruleMatchers; + @Getter private long checkInterval = 86400000; // 默认24小时检查一次 private ScheduledExecutorService scheduledExecutorService; @@ -83,37 +85,50 @@ public void onEnable() { scheduledExecutorService.scheduleAtFixedRate(this::reloadConfig, 0, checkInterval, TimeUnit.MILLISECONDS); } - /** - * Reload the configuration for this module. - */ - private void reloadConfig() { - if (null == ruleMatchers) { - ruleMatchers = new ArrayList<>(); - } - ConfigurationSection config = getConfig(); - // 读取检查间隔 - checkInterval = config.getLong("check-interval", checkInterval); + @Override + public void init() { // 读取规则 ConfigurationSection rules = getRuleSubsConfig(); if (null != rules) { for (String ruleId : rules.getKeys(false)) { ConfigurationSection rule = rules.getConfigurationSection(ruleId); assert rule != null; - updateRule(rule, IPBanRuleUpdateType.AUTO); + updateRule(rule, RuleUpdateType.AUTO); } - log.info(Lang.IP_BAN_RULE_UPDATE_FINISH); + log.info(Lang.SUB_RULE_UPDATE_FINISH); } } + /** + * Reload the configuration for this module. + */ + private void reloadConfig() { + if (null == rules) { + rules = new ArrayList<>(); + } + ConfigurationSection config = getConfig(); + // 读取检查间隔 + checkInterval = config.getLong("check-interval", checkInterval); + init(); + } + @Override public CheckResult shouldBanPeer(PeerMatchRecord ctx) { - String ip = ctx.getPeer().getAddress().getIp(); AtomicReference result = new AtomicReference<>(new CheckResult(false, null, null)); - CountDownLatch latch = new CountDownLatch(ruleMatchers.size()); + CountDownLatch latch = new CountDownLatch(rules.size()); try (var service = Executors.newVirtualThreadPerTaskExecutor()) { - ruleMatchers.forEach(matcher -> service.submit(() -> { - if (matcher.match(ip) == MatchResult.TRUE) { - result.set(new CheckResult(true, matcher.getRuleName(), Lang.MODULE_IBL_MATCH_IP_RULE.replace("{}", matcher.getRuleName()))); + rules.forEach(matcher -> service.submit(() -> { + String matchStr; + Object type = matcher.metadata().get("type"); + if (type == RuleType.PEER_ID_STARTS_WITH || type == RuleType.PEER_ID_CONTAINS) { + matchStr = ctx.getPeer().getPeerId(); + } else if (type == RuleType.CLIENT_NAME_STARTS_WITH || type == RuleType.CLIENT_NAME_CONTAINS) { + matchStr = ctx.getPeer().getClientName(); + } else { + matchStr = ctx.getPeer().getAddress().getIp(); + } + if (matcher.match(matchStr) == MatchResult.TRUE) { + result.set(new CheckResult(true, matcher.metadata().get("name").toString(), String.format(Lang.MODULE_IBL_MATCH_SUB_RULE, matcher.metadata().get("name").toString()))); } latch.countDown(); })); @@ -131,17 +146,18 @@ public CheckResult shouldBanPeer(PeerMatchRecord ctx) { * * @param rule 规则 */ - public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateType updateType) { + public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType updateType) { AtomicReference result = new AtomicReference<>(); String ruleId = rule.getName(); if (!rule.getBoolean("enabled", false)) { // 检查ipBanMatchers是否有对应的规则,有则删除 - ruleMatchers.removeIf(ele -> ele.getRuleId().equals(ruleId)); + rules.removeIf(ele -> ele.metadata().get("id").equals(ruleId)); // 未启用跳过更新逻辑 - return new SlimMsg(false, Lang.IP_BAN_RULE_DISABLED.replace("{}", ruleId)); + return new SlimMsg(false, Lang.SUB_RULE_DISABLED.replace("{}", ruleId)); } String name = rule.getString("name", ruleId); String url = rule.getString("url"); + RuleType ruleType = RuleType.valueOf(Optional.ofNullable(rule.getString("type")).orElse("IP")); if (null != url && url.startsWith("http")) { // 解析远程订阅 String ruleFileName = ruleId + ".txt"; @@ -151,28 +167,41 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp File ruleFile = new File(dir, ruleFileName); List ipAddresses = new ArrayList<>(); List subnetAddresses = new ArrayList<>(); + List fileLines = new ArrayList<>(); HTTPUtil.retryableSend(HTTPUtil.getHttpClient(false, null), MutableRequest.GET(url), HttpResponse.BodyHandlers.ofFile(Path.of(tempFile.getPath()))).whenComplete((pathHttpResponse, throwable) -> { if (throwable != null) { tempFile.delete(); // 加载远程订阅文件出错,尝试从本地缓存中加载 if (ruleFile.exists()) { // 如果一致,但ipBanMatchers没有对应的规则内容,则加载内容 - if (ruleMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { + if (rules.stream().noneMatch(ele -> ele.metadata().get("id").equals(ruleId))) { try { - fileToIPList(name, ruleFile, ipAddresses, subnetAddresses); - ruleMatchers.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); - log.warn(Lang.IP_BAN_RULE_USE_CACHE, name); - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_USE_CACHE.replace("{}", name))); + switch (ruleType) { + case PEER_ID_STARTS_WITH, CLIENT_NAME_STARTS_WITH -> { + fileToLines(name, ruleFile, fileLines); + rules.add(new PrefixMatcher(ruleType, ruleId, name, fileLines)); + } + case PEER_ID_CONTAINS, CLIENT_NAME_CONTAINS -> { + fileToLines(name, ruleFile, fileLines); + rules.add(new SubStrMatcher(ruleType, ruleId, name, fileLines)); + } + default -> { + fileToIPList(name, ruleFile, ipAddresses, subnetAddresses); + rules.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); + } + } + log.warn(Lang.SUB_RULE_USE_CACHE, name); + result.set(new SlimMsg(false, Lang.SUB_RULE_USE_CACHE.replace("{}", name))); } catch (IOException ex) { - log.error(Lang.IP_BAN_RULE_LOAD_FAILED, name, ex); - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_LOAD_FAILED.replace("{}", name))); + log.error(Lang.SUB_RULE_LOAD_FAILED, name, ex); + result.set(new SlimMsg(false, Lang.SUB_RULE_LOAD_FAILED.replace("{}", name))); } } else { - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_UPDATE_FAILED.replace("{}", name))); + result.set(new SlimMsg(false, Lang.SUB_RULE_UPDATE_FAILED.replace("{}", name))); } } else { // log.error(Lang.IP_BAN_RULE_LOAD_FAILED, name, throwable); - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_LOAD_FAILED.replace("{}", name))); + result.set(new SlimMsg(false, Lang.SUB_RULE_LOAD_FAILED.replace("{}", name))); } throw new RuntimeException(throwable); } @@ -185,55 +214,91 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, IPBanRuleUpdateTyp int ent_count = 0; if (!tempHash.equals(ruleHash)) { // 规则文件不存在或者规则文件与临时文件sha256不一致则需要更新 - ent_count = fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + ent_count = switch (ruleType) { + case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, CLIENT_NAME_CONTAINS -> + fileToLines(name, ruleFile, fileLines); + default -> fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + }; // 更新后重命名临时文件 ruleFile.delete(); tempFile.renameTo(ruleFile); } else { // 如果一致,但ipBanMatchers没有对应的规则内容,则加载内容 - if (ruleMatchers.stream().noneMatch(ele -> ele.getRuleId().equals(ruleId))) { - ent_count = fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + if (rules.stream().noneMatch(ele -> ele.metadata().get("id").equals(ruleId))) { + ent_count = switch (ruleType) { + case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, + CLIENT_NAME_CONTAINS -> fileToLines(name, ruleFile, fileLines); + default -> fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + }; } else { - log.info(Lang.IP_BAN_RULE_NO_UPDATE, name); - result.set(new SlimMsg(true, Lang.IP_BAN_RULE_NO_UPDATE.replace("{}", name))); + log.info(Lang.SUB_RULE_NO_UPDATE, name); + result.set(new SlimMsg(true, Lang.SUB_RULE_NO_UPDATE.replace("{}", name))); } tempFile.delete(); } // ip列表或者subnet列表不为空代表需要更新matcher - if (!ipAddresses.isEmpty() || !subnetAddresses.isEmpty()) { + if (!ipAddresses.isEmpty() || !subnetAddresses.isEmpty() || !fileLines.isEmpty()) { // 如果已经存在则更新,否则添加 - ruleMatchers.stream().filter(ele -> ele.getRuleId().equals(ruleId)).findFirst().ifPresentOrElse(ele -> { - ele.setData(name, ipAddresses, subnetAddresses); - log.info(Lang.IP_BAN_RULE_UPDATE_SUCCESS, name); - result.set(new SlimMsg(true, Lang.IP_BAN_RULE_UPDATE_SUCCESS.replace("{}", name))); + rules.stream().filter(ele -> ele.metadata().get("id").equals(ruleId)).findFirst().ifPresentOrElse(ele -> { + switch (ruleType) { + case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, + CLIENT_NAME_CONTAINS -> ((RuleMatcher) ele).setData(name, fileLines); + default -> ((IPMatcher) ele).setData(name, ipAddresses, subnetAddresses); + } + log.info(Lang.SUB_RULE_UPDATE_SUCCESS, name); + result.set(new SlimMsg(true, Lang.SUB_RULE_UPDATE_SUCCESS.replace("{}", name))); }, () -> { - ruleMatchers.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); - log.info(Lang.IP_BAN_RULE_LOAD_SUCCESS, name); - result.set(new SlimMsg(true, Lang.IP_BAN_RULE_LOAD_SUCCESS.replace("{}", name))); + switch (ruleType) { + case PEER_ID_STARTS_WITH, CLIENT_NAME_STARTS_WITH -> + rules.add(new PrefixMatcher(ruleType, ruleId, name, fileLines)); + case PEER_ID_CONTAINS, CLIENT_NAME_CONTAINS -> + rules.add(new SubStrMatcher(ruleType, ruleId, name, fileLines)); + default -> rules.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); + } + log.info(Lang.SUB_RULE_LOAD_SUCCESS, name); + result.set(new SlimMsg(true, Lang.SUB_RULE_LOAD_SUCCESS.replace("{}", name))); }); } if (ent_count > 0) { // 更新日志 try { db.insertRuleSubLog(ruleId, ent_count, updateType); - result.set(new SlimMsg(true, Lang.IP_BAN_RULE_UPDATED.replace("{}", name))); + result.set(new SlimMsg(true, Lang.SUB_RULE_UPDATED.replace("{}", name))); } catch (SQLException e) { - log.error(Lang.IP_BAN_RULE_UPDATE_LOG_ERROR, ruleId, e); - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_UPDATE_LOG_ERROR.replace("{}", name))); + log.error(Lang.SUB_RULE_UPDATE_LOG_ERROR, ruleId, e); + result.set(new SlimMsg(false, Lang.SUB_RULE_UPDATE_LOG_ERROR.replace("{}", name))); } } else { - result.set(new SlimMsg(true, Lang.IP_BAN_RULE_NO_UPDATE.replace("{}", name))); + result.set(new SlimMsg(true, Lang.SUB_RULE_NO_UPDATE.replace("{}", name))); } } catch (IOException e) { throw new RuntimeException(e); } }).join(); } else { - result.set(new SlimMsg(false, Lang.IP_BAN_RULE_URL_WRONG.replace("{}", name))); + result.set(new SlimMsg(false, Lang.SUB_RULE_URL_WRONG.replace("{}", name))); } return result.get(); } + /** + * 读取规则文件并转为字符串列表 + * + * @param ruleName 规则名称 + * @param ruleFile 规则文件 + * @param lines 字符串列表 + * @return 加载的行数 + */ + private int fileToLines(String ruleName, File ruleFile, List lines) throws IOException { + AtomicInteger count = new AtomicInteger(); + Files.readLines(ruleFile, StandardCharsets.UTF_8).forEach(ele -> { + count.getAndIncrement(); + log.debug(Lang.SUB_RULE_LOAD_CONTENT, ruleName, ele); + lines.add(ele); + }); + return count.get(); + } + /** * 读取规则文件并转为IpList * 其中ipv4网段地址转为精确ip diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java index 20f565d4..5cef0dba 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java @@ -3,7 +3,7 @@ import com.ghostchu.peerbanhelper.PeerBanHelperServer; import com.ghostchu.peerbanhelper.database.RuleSubInfo; import com.ghostchu.peerbanhelper.module.AbstractFeatureModule; -import com.ghostchu.peerbanhelper.module.IPBanRuleUpdateType; +import com.ghostchu.peerbanhelper.module.RuleUpdateType; import com.ghostchu.peerbanhelper.module.impl.rule.RuleSubBlocker; import com.ghostchu.peerbanhelper.module.impl.webapi.common.SlimMsg; import com.ghostchu.peerbanhelper.module.impl.webapi.common.StdMsg; @@ -88,7 +88,7 @@ public void onDisable() { * @param ctx 上下文 */ private void getCheckInterval(Context ctx) { - ctx.json(new StdMsg(true, Lang.IP_BAN_RULE_CHECK_INTERVAL_QUERY_SUCCESS, ruleSubBlocker.getCheckInterval())); + ctx.json(new StdMsg(true, Lang.SUB_RULE_CHECK_INTERVAL_QUERY_SUCCESS, ruleSubBlocker.getCheckInterval())); } /** @@ -100,10 +100,10 @@ private void changeCheckInterval(Context ctx) { try { long interval = JsonUtil.readObject(ctx.body()).get("checkInterval").getAsLong(); ruleSubBlocker.changeCheckInterval(interval); - ctx.json(new SlimMsg(true, Lang.IP_BAN_RULE_CHECK_INTERVAL_UPDATED)); + ctx.json(new SlimMsg(true, Lang.SUB_RULE_CHECK_INTERVAL_UPDATED)); } catch (Exception e) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CHECK_INTERVAL_WRONG_PARAM)); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_CHECK_INTERVAL_WRONG_PARAM)); } } @@ -124,11 +124,11 @@ private void logs(Context ctx, String ruleId) { map.put("pageSize", pageSize); map.put("results", ruleSubBlocker.queryRuleSubLogs(ruleId, pageIndex, pageSize)); map.put("total", ruleSubBlocker.countRuleSubLogs(ruleId)); - ctx.json(new StdMsg(true, Lang.IP_BAN_RULE_LOG_QUERY_SUCCESS, map)); + ctx.json(new StdMsg(true, Lang.SUB_RULE_LOG_QUERY_SUCCESS, map)); } catch (Exception e) { - log.error(Lang.IP_BAN_RULE_LOG_QUERY_ERROR, e); + log.error(Lang.SUB_RULE_LOG_QUERY_ERROR, e); ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_LOG_QUERY_WRONG_PARAM)); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_LOG_QUERY_WRONG_PARAM)); } } @@ -139,7 +139,7 @@ private void logs(Context ctx, String ruleId) { */ private SlimMsg updateAll() { AtomicReference result = new AtomicReference<>(); - ruleSubBlocker.getRuleSubsConfig().getKeys(false).stream().map(this::update).filter(ele -> !ele.success()).findFirst().ifPresentOrElse(result::set, () -> result.set(new SlimMsg(true, Lang.IP_BAN_RULE_ALL_UPDATED))); + ruleSubBlocker.getRuleSubsConfig().getKeys(false).stream().map(this::update).filter(ele -> !ele.success()).findFirst().ifPresentOrElse(result::set, () -> result.set(new SlimMsg(true, Lang.SUB_RULE_ALL_UPDATED))); return result.get(); } @@ -151,13 +151,13 @@ private SlimMsg updateAll() { */ private SlimMsg update(String ruleId) { if (ruleId == null || ruleId.isEmpty()) { - return new SlimMsg(false, Lang.IP_BAN_RULE_NO_ID); + return new SlimMsg(false, Lang.SUB_RULE_NO_ID); } ConfigurationSection configurationSection = ruleSubBlocker.getRuleSubsConfig().getConfigurationSection(ruleId); if (null == configurationSection) { - return new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId)); + return new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId)); } - return ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + return ruleSubBlocker.updateRule(configurationSection, RuleUpdateType.MANUAL); } /** @@ -172,19 +172,19 @@ private void switcher(Context ctx) throws SQLException, IOException { enabled = JsonUtil.readObject(ctx.body()).get("enabled").getAsBoolean(); } catch (Exception e) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_ENABLED_WRONG_PARAM)); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_ENABLED_WRONG_PARAM)); return; } RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); return; } - String msg = (enabled ? Lang.IP_BAN_RULE_ENABLED : Lang.IP_BAN_RULE_DISABLED).replace("{}", ruleSubInfo.ruleName()); + String msg = (enabled ? Lang.SUB_RULE_ENABLED : Lang.SUB_RULE_DISABLED).replace("{}", ruleSubInfo.ruleName()); if (enabled != ruleSubInfo.enabled()) { ConfigurationSection configurationSection = ruleSubBlocker.saveRuleSubInfo(new RuleSubInfo(ruleId, enabled, ruleSubInfo.ruleName(), ruleSubInfo.subUrl(), 0, 0)); - ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + ruleSubBlocker.updateRule(configurationSection, RuleUpdateType.MANUAL); log.info(msg); ctx.json(new SlimMsg(true, msg)); } else { @@ -203,12 +203,12 @@ private void delete(Context ctx) throws IOException, SQLException { RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); return; } ruleSubBlocker.deleteRuleSubInfo(ruleId); - ruleSubBlocker.getRuleMatchers().removeIf(ele -> ele.getRuleId().equals(ruleId)); - String msg = Lang.IP_BAN_RULE_DELETED.replace("{}", ruleSubInfo.ruleName()); + ruleSubBlocker.getRules().removeIf(ele -> ele.metadata().get("id").equals(ruleId)); + String msg = Lang.SUB_RULE_DELETED.replace("{}", ruleSubInfo.ruleName()); log.info(msg); ctx.json(new SlimMsg(true, msg)); } @@ -227,20 +227,20 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException } if (ruleId == null || ruleId.isEmpty()) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_NO_ID)); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_NO_ID)); return; } RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (isAdd && ruleSubInfo != null) { // 新增时检查规则是否存在 ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_ID_CONFLICT.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_ID_CONFLICT.replace("{}", ruleId))); return; } if (!isAdd && ruleSubInfo == null) { // 更新时检查规则是否存在 ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); return; } String ruleName = subInfo.ruleName(); @@ -248,7 +248,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException if (isAdd) { if (ruleName == null || subUrl == null || ruleName.isEmpty() || subUrl.isEmpty()) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_PARAM_WRONG)); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_PARAM_WRONG)); return; } } else { @@ -262,13 +262,13 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException ConfigurationSection configurationSection = ruleSubBlocker.saveRuleSubInfo(new RuleSubInfo(ruleId, isAdd || ruleSubInfo.enabled(), ruleName, subUrl, 0, 0)); assert configurationSection != null; try { - SlimMsg msg = ruleSubBlocker.updateRule(configurationSection, IPBanRuleUpdateType.MANUAL); + SlimMsg msg = ruleSubBlocker.updateRule(configurationSection, RuleUpdateType.MANUAL); if (!msg.success()) { ctx.status(HttpStatus.BAD_REQUEST); ctx.json(msg); return; } - ctx.json(new SlimMsg(true, Lang.IP_BAN_RULE_SAVED)); + ctx.json(new SlimMsg(true, Lang.SUB_RULE_SAVED)); } catch (Exception e) { // 更新失败时回滚 if (isAdd) { @@ -277,7 +277,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException ruleSubBlocker.saveRuleSubInfo(ruleSubInfo); } ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.IP_BAN_RULE_URL_WRONG.replace("{}", ruleName))); + ctx.json(new SlimMsg(false, Lang.SUB_RULE_URL_WRONG.replace("{}", ruleName))); } } @@ -288,7 +288,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException * @return 响应 */ private StdMsg get(String ruleId) throws SQLException { - return new StdMsg(true, Lang.IP_BAN_RULE_INFO_QUERY_SUCCESS, ruleSubBlocker.getRuleSubInfo(ruleId)); + return new StdMsg(true, Lang.SUB_RULE_INFO_QUERY_SUCCESS, ruleSubBlocker.getRuleSubInfo(ruleId)); } /** @@ -302,7 +302,7 @@ private StdMsg list() throws SQLException { for (String s : list) { data.add(ruleSubBlocker.getRuleSubInfo(s)); } - return new StdMsg(true, Lang.IP_BAN_RULE_INFO_QUERY_SUCCESS, data); + return new StdMsg(true, Lang.SUB_RULE_INFO_QUERY_SUCCESS, data); } /** diff --git a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java index 5fad4b5f..3c553872 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java +++ b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java @@ -21,7 +21,7 @@ public class Lang { public static final String ERR_INVALID_RULE_SYNTAX = "规则 {} 的表达式无效,请检查是否存在拼写错误"; public static final String MODULE_CNB_MATCH_CLIENT_NAME = "匹配 ClientName (UserAgent): %s"; public static final String MODULE_IBL_MATCH_IP = "匹配 IP 规则: %s"; - public static final String MODULE_IBL_MATCH_IP_RULE = "匹配 IP黑名单订阅 规则: %s"; + public static final String MODULE_IBL_MATCH_SUB_RULE = "匹配 订阅规则: %s"; public static final String MODULE_IBL_MATCH_ASN = "匹配 ASN 规则: %s"; public static final String MODULE_IBL_MATCH_REGION = "匹配国家或地区 ISO 代码规则: %s"; public static final String MODULE_IBL_EXCEPTION_GEOIP = "匹配 GeoIP 信息时出现异常,请反馈错误给开发者"; @@ -169,36 +169,37 @@ public class Lang { public static final String IP_BAN_RULE_MATCH_TIME = "匹配IP黑名单订阅规则花费时间:{}"; public static final String IP_BAN_RULE_UPDATE_TYPE_AUTO = "自动更新"; public static final String IP_BAN_RULE_UPDATE_TYPE_MANUAL = "手动更新"; - public static final String IP_BAN_RULE_UPDATE_FINISH = "IP黑名单规则订阅完毕"; - public static final String IP_BAN_RULE_NO_UPDATE = "IP黑名单订阅规则 {} 未发生更新"; - public static final String IP_BAN_RULE_UPDATE_SUCCESS = "IP黑名单订阅规则 {} 更新成功"; - public static final String IP_BAN_RULE_UPDATE_FAILED = "IP黑名单订阅规则 {} 更新失败"; - public static final String IP_BAN_RULE_LOAD_SUCCESS = "IP黑名单订阅规则 {} 加载成功"; - public static final String IP_BAN_RULE_UPDATE_LOG_ERROR = "[错误] IP黑名单订阅规则 {} 更新日志失败"; - public static final String IP_BAN_RULE_USE_CACHE = "[警告] IP黑名单订阅规则 {} 订阅失败,使用本地缓存加载成功"; - public static final String IP_BAN_RULE_LOAD_FAILED = "[错误] IP黑名单订阅规则 {} 加载失败"; + public static final String SUB_RULE_UPDATE_FINISH = "规则订阅完毕"; + public static final String SUB_RULE_NO_UPDATE = "订阅规则 {} 未发生更新"; + public static final String SUB_RULE_UPDATE_SUCCESS = "订阅规则 {} 更新成功"; + public static final String SUB_RULE_UPDATE_FAILED = "订阅规则 {} 更新失败"; + public static final String SUB_RULE_LOAD_SUCCESS = "订阅规则 {} 加载成功"; + public static final String SUB_RULE_UPDATE_LOG_ERROR = "[错误] 订阅规则 {} 更新日志失败"; + public static final String SUB_RULE_USE_CACHE = "[警告] 订阅规则 {} 订阅失败,使用本地缓存加载成功"; + public static final String SUB_RULE_LOAD_FAILED = "[错误] 订阅规则 {} 加载失败"; public static final String IP_BAN_RULE_LOAD_CIDR = "IP黑名单订阅规则 {} 加载CIDR : {}"; public static final String IP_BAN_RULE_LOAD_IP = "IP黑名单订阅规则 {} 加载精确IP : {}"; + public static final String SUB_RULE_LOAD_CONTENT = "订阅规则 {} 加载内容 : {}"; public static final String RULE_SUB_API_INTERNAL_ERROR = "[错误] 订阅规则API遇到非预期错误"; - public static final String IP_BAN_RULE_NO_ID = "[错误] IP黑名单订阅规则ID为空"; - public static final String IP_BAN_RULE_ID_CONFLICT = "[错误] IP黑名单订阅规则ID冲突: {}"; - public static final String IP_BAN_RULE_CANT_FIND = "[错误] 未找到IP黑名单订阅规则: {}"; - public static final String IP_BAN_RULE_PARAM_WRONG = "[错误] IP黑名单订阅规则参数错误"; - public static final String IP_BAN_RULE_URL_WRONG = "[错误] IP黑名单订阅规则 {} URL错误"; - public static final String IP_BAN_RULE_ENABLED = "IP黑名单订阅规则 {} 已启用"; - public static final String IP_BAN_RULE_DISABLED = "IP黑名单订阅规则 {} 已禁用"; - public static final String IP_BAN_RULE_UPDATED = "IP黑名单订阅规则 {} 已更新"; - public static final String IP_BAN_RULE_ALL_UPDATED = "IP黑名单订阅规则已全部更新"; - public static final String IP_BAN_RULE_SAVED = "IP黑名单订阅规则已保存"; - public static final String IP_BAN_RULE_DELETED = "IP黑名单订阅规则 {} 已删除"; - public static final String IP_BAN_RULE_INFO_QUERY_SUCCESS = "IP黑名单订阅规则查询成功"; - public static final String IP_BAN_RULE_LOG_QUERY_SUCCESS = "IP黑名单订阅规则更新日志查询成功"; - public static final String IP_BAN_RULE_LOG_QUERY_ERROR = "IP黑名单订阅规则更新日志查询出错"; - public static final String IP_BAN_RULE_LOG_QUERY_WRONG_PARAM = "IP黑名单订阅规则更新日志查询参数错误"; - public static final String IP_BAN_RULE_CHECK_INTERVAL_QUERY_SUCCESS = "IP黑名单订阅规则更新间隔查询成功"; - public static final String IP_BAN_RULE_CHECK_INTERVAL_WRONG_PARAM = "IP黑名单订阅规则更新间隔参数错误"; - public static final String IP_BAN_RULE_CHECK_INTERVAL_UPDATED = "IP黑名单订阅规则更新间隔设置成功"; - public static final String IP_BAN_RULE_ENABLED_WRONG_PARAM = "IP黑名单订阅规则启用禁用参数错误"; + public static final String SUB_RULE_NO_ID = "[错误] 订阅规则ID为空"; + public static final String SUB_RULE_ID_CONFLICT = "[错误] 订阅规则ID冲突: {}"; + public static final String SUB_RULE_CANT_FIND = "[错误] 未找到订阅规则: {}"; + public static final String SUB_RULE_PARAM_WRONG = "[错误] 订阅规则参数错误"; + public static final String SUB_RULE_URL_WRONG = "[错误] 订阅规则 {} URL错误"; + public static final String SUB_RULE_ENABLED = "订阅规则 {} 已启用"; + public static final String SUB_RULE_DISABLED = "订阅规则 {} 已禁用"; + public static final String SUB_RULE_UPDATED = "订阅规则 {} 已更新"; + public static final String SUB_RULE_ALL_UPDATED = "订阅规则已全部更新"; + public static final String SUB_RULE_SAVED = "订阅规则已保存"; + public static final String SUB_RULE_DELETED = "订阅规则 {} 已删除"; + public static final String SUB_RULE_INFO_QUERY_SUCCESS = "订阅规则查询成功"; + public static final String SUB_RULE_LOG_QUERY_SUCCESS = "订阅规则更新日志查询成功"; + public static final String SUB_RULE_LOG_QUERY_ERROR = "订阅规则更新日志查询出错"; + public static final String SUB_RULE_LOG_QUERY_WRONG_PARAM = "IP黑名单订阅规则更新日志查询参数错误"; + public static final String SUB_RULE_CHECK_INTERVAL_QUERY_SUCCESS = "订阅规则更新间隔查询成功"; + public static final String SUB_RULE_CHECK_INTERVAL_WRONG_PARAM = "订阅规则更新间隔参数错误"; + public static final String SUB_RULE_CHECK_INTERVAL_UPDATED = "订阅规则更新间隔设置成功"; + public static final String SUB_RULE_ENABLED_WRONG_PARAM = "IP黑名单订阅规则启用禁用参数错误"; public static final String WEBAPI_AUTH_INVALID_TOKEN = "登录失败,Token 无效"; public static final String WEBAPI_AUTH_OK = "登录成功"; public static final String WEBAPI_AUTH_BANNED_TOO_FREQ = "登录错误次数过多,此 IP 地址已被暂时封禁"; diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java index 7a8819cf..951a60f1 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java @@ -1,14 +1,14 @@ package com.ghostchu.peerbanhelper.util.rule; import lombok.EqualsAndHashCode; -import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import java.util.Map; + /** * 规则Matcher */ -@Getter @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @@ -16,11 +16,11 @@ public abstract class RuleMatcher extends AbstractMatcher { private final String ruleId; - private final SubRuleType ruleType; + private final RuleType ruleType; protected String ruleName; - public RuleMatcher(SubRuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + public RuleMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { this.ruleType = ruleType; this.ruleId = ruleId; setData(ruleName, ruleData); @@ -28,4 +28,8 @@ public RuleMatcher(SubRuleType ruleType, String ruleId, String ruleName, Object. public abstract void setData(String ruleName, Object... ruleData); + public Map metadata() { + return Map.of("id", ruleId, "name", ruleName, "type", ruleType); + } + } diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleType.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleType.java new file mode 100644 index 00000000..8eef6eb3 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleType.java @@ -0,0 +1,27 @@ +package com.ghostchu.peerbanhelper.util.rule; + +/** + * 订阅规则类型 + */ +public enum RuleType { + /** + * IP/CIDR + */ + IP, + /** + * peer-id子串 + */ + PEER_ID_CONTAINS, + /** + * peer-id前缀 + */ + PEER_ID_STARTS_WITH, + /** + * client-name子串 + */ + CLIENT_NAME_CONTAINS, + /** + * client-name前缀 + */ + CLIENT_NAME_STARTS_WITH +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java deleted file mode 100644 index f0af99af..00000000 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/SubRuleType.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.ghostchu.peerbanhelper.util.rule; - -/** - * 订阅规则类型 - */ -public enum SubRuleType { - /** - * IP/CIDR - */ - IP, - /** - * 子串 - */ - SUBSTR, - /** - * 前缀 - */ - PREFIX -} diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java index c6f9c4ea..f85e733b 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java @@ -4,7 +4,7 @@ import com.ghostchu.peerbanhelper.util.IPAddressUtil; import com.ghostchu.peerbanhelper.util.rule.MatchResult; import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; -import com.ghostchu.peerbanhelper.util.rule.SubRuleType; +import com.ghostchu.peerbanhelper.util.rule.RuleType; import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import inet.ipaddr.IPAddress; @@ -15,7 +15,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.Map; @Slf4j @EqualsAndHashCode(callSuper = true) @@ -27,7 +26,7 @@ public class IPMatcher extends RuleMatcher { private BloomFilter bloomFilter; public IPMatcher(String ruleId, String ruleName, Object... ruleData) { - super(SubRuleType.IP, ruleId, ruleName, ruleData); + super(RuleType.IP, ruleId, ruleName, ruleData); } @Override @@ -60,11 +59,6 @@ public void setData(String ruleName, Object... ruleData) { return MatchResult.DEFAULT; } - @Override - public Map metadata() { - return Map.of("rule", ruleName); - } - @Override public @NotNull String matcherName() { return Lang.RULE_MATCHER_SUB_RULE; @@ -72,6 +66,6 @@ public Map metadata() { @Override public String matcherIdentifier() { - return "peerbanhelper:ipbanmatcher"; + return "peerbanhelper:ipmatcher"; } } diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java new file mode 100644 index 00000000..56c94669 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java @@ -0,0 +1,48 @@ +package com.ghostchu.peerbanhelper.util.rule.matcher; + +import com.ghostchu.peerbanhelper.text.Lang; +import com.ghostchu.peerbanhelper.util.rule.MatchResult; +import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; +import com.ghostchu.peerbanhelper.util.rule.RuleType; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +@Slf4j +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PrefixMatcher extends RuleMatcher { + + private List prefixes; + + public PrefixMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + super(ruleType, ruleId, ruleName, ruleData); + } + + @Override + public void setData(String ruleName, Object... ruleData) { + this.ruleName = ruleName; + this.prefixes = ruleData.length > 0 ? (List) ruleData[0] : List.of(); + } + + @Override + public @NotNull MatchResult match0(@NotNull String content) { + if (prefixes.parallelStream().anyMatch(content::startsWith)) { + return MatchResult.TRUE; + } + return MatchResult.DEFAULT; + } + + @Override + public @NotNull String matcherName() { + return Lang.RULE_MATCHER_SUB_RULE; + } + + @Override + public String matcherIdentifier() { + return "peerbanhelper:prefixmatcher"; + } +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java new file mode 100644 index 00000000..f6c84513 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java @@ -0,0 +1,48 @@ +package com.ghostchu.peerbanhelper.util.rule.matcher; + +import com.ghostchu.peerbanhelper.text.Lang; +import com.ghostchu.peerbanhelper.util.rule.MatchResult; +import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; +import com.ghostchu.peerbanhelper.util.rule.RuleType; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +@Slf4j +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SubStrMatcher extends RuleMatcher { + + private List subStrs; + + public SubStrMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + super(ruleType, ruleId, ruleName, ruleData); + } + + @Override + public void setData(String ruleName, Object... ruleData) { + this.ruleName = ruleName; + this.subStrs = ruleData.length > 0 ? (List) ruleData[0] : List.of(); + } + + @Override + public @NotNull MatchResult match0(@NotNull String content) { + if (subStrs.parallelStream().anyMatch(content::contains)) { + return MatchResult.TRUE; + } + return MatchResult.DEFAULT; + } + + @Override + public @NotNull String matcherName() { + return Lang.RULE_MATCHER_SUB_RULE; + } + + @Override + public String matcherIdentifier() { + return "peerbanhelper:substrmatcher"; + } +} diff --git a/src/main/resources/profile.yml b/src/main/resources/profile.yml index 5d2614fa..9bbb8b5b 100644 --- a/src/main/resources/profile.yml +++ b/src/main/resources/profile.yml @@ -174,6 +174,8 @@ module: dh_torrents: # 是否启用 enabled: false + # 匹配类型 IP PEER_ID_CONTAINS PEER_ID_STARTS_WITH CLIENT_NAME_CONTAINS CLIENT_NAME_STARTS_WITH + type: IP # 显示名称 name: dh_torrents # 规则文件订阅地址 From f05b6bdf09a7a9710e18a82d6084ddd55ded194c Mon Sep 17 00:00:00 2001 From: PluieM <644202913@qq.com> Date: Wed, 19 Jun 2024 22:08:56 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E9=83=A8=E5=88=86=E5=85=83=E7=B4=A0=E6=9C=AA?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java | 2 +- .../java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java index a815016f..fd2323c2 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java @@ -128,7 +128,7 @@ public CheckResult shouldBanPeer(PeerMatchRecord ctx) { matchStr = ctx.getPeer().getAddress().getIp(); } if (matcher.match(matchStr) == MatchResult.TRUE) { - result.set(new CheckResult(true, matcher.metadata().get("name").toString(), String.format(Lang.MODULE_IBL_MATCH_SUB_RULE, matcher.metadata().get("name").toString()))); + result.set(new CheckResult(true, matcher.metadata().get("rule").toString(), String.format(Lang.MODULE_IBL_MATCH_SUB_RULE, matcher.metadata().get("rule").toString()))); } latch.countDown(); })); diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java index 951a60f1..98f34a68 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java @@ -29,7 +29,7 @@ public RuleMatcher(RuleType ruleType, String ruleId, String ruleName, Object... public abstract void setData(String ruleName, Object... ruleData); public Map metadata() { - return Map.of("id", ruleId, "name", ruleName, "type", ruleType); + return Map.of("id", ruleId, "rule", ruleName, "type", ruleType); } } From ef101b3b6d6da29f6c97d46b840fa2e04c33a38c Mon Sep 17 00:00:00 2001 From: PluieM <644202913@qq.com> Date: Thu, 20 Jun 2024 16:41:06 +0800 Subject: [PATCH 4/5] =?UTF-8?q?1=E3=80=81=E4=BD=BF=E7=94=A8=E6=B3=9B?= =?UTF-8?q?=E5=9E=8B=E9=87=8D=E6=9E=84RuleMatcher=202=E3=80=81=E7=B2=BE?= =?UTF-8?q?=E7=AE=80RuleBlocker=E7=9A=84=E8=B0=83=E7=94=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../peerbanhelper/PeerBanHelperServer.java | 76 ++++++------- .../module/AbstractRuleBlocker.java | 8 +- .../peerbanhelper/module/MatchEvents.java | 4 - .../module/MatchResultDetail.java | 15 ++- .../module/PeerMatchContext.java | 18 +++ .../peerbanhelper/module/PeerState.java | 4 - .../peerbanhelper/module/RuleBlocker.java | 106 ++++++++++++------ .../module/impl/rule/RuleSubBlocker.java | 62 +++------- .../module/impl/webapi/RuleSubController.java | 16 +-- .../com/ghostchu/peerbanhelper/text/Lang.java | 18 +-- .../peerbanhelper/util/rule/RuleMatcher.java | 7 +- .../util/rule/matcher/IPMatcher.java | 26 ++++- .../util/rule/matcher/PrefixMatcher.java | 12 +- .../util/rule/matcher/SubStrMatcher.java | 14 ++- .../peerbanhelper/util/time/ExceptedTime.java | 4 +- 15 files changed, 218 insertions(+), 172 deletions(-) create mode 100644 src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchContext.java diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java index 2e7087e8..411ec7d5 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java +++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java @@ -105,7 +105,7 @@ public class PeerBanHelperServer { @Getter private JavalinWebContainer webContainer; @Getter - private final Map matchRecords = new ConcurrentHashMap<>(); + private final Map matchRecords = new ConcurrentHashMap<>(); public PeerBanHelperServer(String pbhServerAddress, YamlConfiguration profile, YamlConfiguration mainConfig) throws SQLException { this.pbhServerAddress = pbhServerAddress; @@ -392,41 +392,25 @@ public void banWave() { executor.submit(() -> updateLivePeers(peers)); // ===============基于 状态机 的封禁逻辑 banWaveWatchDog.setLastOperation("Check Bans New"); - // long t1 = System.currentTimeMillis(); - // 断开连接超时剔除 - matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.ACTIVE && ele.getResult().expireTime() < System.currentTimeMillis()) - .map(ele -> ele.getPeer().getAddress()).forEach(matchRecords::remove); - // 封禁超时解禁后剔除 - matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.BAN && ele.getResult().expireTime() < System.currentTimeMillis()) - .map(ele -> ele.getPeer().getAddress()).forEach(ele -> { - unbanPeer(ele); - matchRecords.remove(ele); - }); - // 按模块并行检查标记Peer + // 按模块并行检查Peer List ruleBlockers = moduleManager.getModules().stream().filter(ele -> ele instanceof RuleBlocker).toList(); - CountDownLatch fsmLatch = new CountDownLatch(ruleBlockers.size()); - ruleBlockers.forEach(ele -> { - RuleBlocker blocker = (RuleBlocker) ele; - try (TimeoutProtect protect = new TimeoutProtect(ExceptedTime.CHECK_BANS.getTimeout(), (t) -> { - log.warn(Lang.TIMING_CHECK_BANS); - fsmLatch.countDown(); - })) { + try (TimeoutProtect protect = new TimeoutProtect(ExceptedTime.RUN_BLOCKER.getTimeout(), (t) -> { + log.warn(Lang.TIMING_CHECK_BANS); + })) { + ruleBlockers.forEach(ele -> { + RuleBlocker blocker = (RuleBlocker) ele; protect.getService().submit(() -> { - blocker.runCheck(); - fsmLatch.countDown(); + try (TimeoutProtect banProtect = new TimeoutProtect(ExceptedTime.BAN_PEER.getTimeout(), (t) -> log.warn(Lang.TIMING_ADD_BANS))) { + blocker.runCheck( + null, (record) -> + banProtect.getService().submit(() -> + banPeer(record.getDownloader(), record.getTorrent(), record.getPeer(), record.getResult().getModuleContext().getClass().getName(), record.getResult().getRule(), record.getResult().getReason(), bannedPeers, needRelaunched) + ), null, null + ); + } }); - } - }); - fsmLatch.await(); - // 标记完成后将结果为Ban的Peer封禁 - banWaveWatchDog.setLastOperation("Add banned peers into banlist New"); - matchRecords.values().stream().filter(ele -> ele.getResult().state() == PeerState.BAN && !BAN_LIST.containsKey(ele.getPeer().getAddress())) - .forEach(record -> Optional.ofNullable(needRelaunched.get(record.getDownloader())).ifPresentOrElse(torrents -> - banPeer(record.getDownloader(), record.getTorrent(), record.getPeer(), record.getResult().moduleContext().getClass().getName(), record.getResult().rule(), record.getResult().reason(), bannedPeers, torrents), () -> { - List torrents = new ArrayList<>(); - banPeer(record.getDownloader(), record.getTorrent(), record.getPeer(), record.getResult().moduleContext().getClass().getName(), record.getResult().rule(), record.getResult().reason(), bannedPeers, torrents); - needRelaunched.put(record.getDownloader(), torrents); - })); + }); + } // ========== 处理封禁逻辑 ========== Map> downloaderBanDetailMap = new ConcurrentHashMap<>(); banWaveWatchDog.setLastOperation("Check Bans"); @@ -443,16 +427,15 @@ public void banWave() { downloaderBanDetailMap.forEach((downloader, details) -> Optional.ofNullable(needRelaunched.get(downloader)).ifPresentOrElse(torrents -> details.forEach(detail -> protect.getService().submit(() -> { if (detail.result().action() == PeerAction.BAN) { - banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, torrents); + banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, needRelaunched); } })), () -> { - List torrents = new ArrayList<>(); details.forEach(detail -> protect.getService().submit(() -> { if (detail.result().action() == PeerAction.BAN) { - banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, torrents); + banPeer(downloader, detail.torrent(), detail.peer(), detail.result().moduleContext().getClass().getName(), detail.result().rule(), detail.result().reason(), bannedPeers, needRelaunched); } })); - needRelaunched.put(downloader, torrents); + //needRelaunched.put(downloader, torrents); })); } banWaveWatchDog.setLastOperation("Apply banlist"); @@ -471,8 +454,6 @@ public void banWave() { log.info(Lang.BAN_WAVE_CHECK_COMPLETED, downloadersCount, torrentsCount, peersCount, bannedPeers.size(), unbannedPeers.size(), System.currentTimeMillis() - startTimer); } banWaveWatchDog.setLastOperation("Completed"); - } catch (InterruptedException e) { - throw new RuntimeException(e); } finally { banWaveWatchDog.feed(); metrics.recordCheck(); @@ -513,13 +494,14 @@ private void updateLivePeers(Map>> peers) { ); livePeers.put(address, metadata); // 更新匹配记录 - if (matchRecords.containsKey(address)) { - PeerMatchRecord peerMatchRecord = matchRecords.get(address); + String recordKey = downloader.getName() + "@" + torrent.getHash() + "@" + address.getIp(); + if (matchRecords.containsKey(recordKey)) { + PeerMatchRecord peerMatchRecord = matchRecords.get(recordKey); peerMatchRecord.setDownloader(downloader); peerMatchRecord.setTorrent(torrent); peerMatchRecord.setPeer(p); } else { - matchRecords.put(address, new PeerMatchRecord(downloader, torrent, p, new MatchResultDetail(null, PeerState.INIT, "N/A", "no matches", 0))); + matchRecords.put(recordKey, new PeerMatchRecord(downloader, torrent, p, new MatchResultDetail(null, PeerState.INIT, "N/A", "no matches", System.currentTimeMillis() + disconnectTimeout))); } }); }))); @@ -724,11 +706,17 @@ public Map getBannedPeers() { * * @param peer 对等体 IP 地址 */ - public void banPeer(@NotNull Downloader downloader, @NotNull Torrent torrent, @NotNull Peer peer, @NotNull String module, @NotNull String ruleName, @NotNull String reason, @NotNull Collection bannedPeers, @NotNull List relaunch) { + public synchronized void banPeer(@NotNull Downloader downloader, @NotNull Torrent torrent, @NotNull Peer peer, @NotNull String module, @NotNull String ruleName, @NotNull String reason, @NotNull Collection bannedPeers, @NotNull Map> needRelaunched) { if (BAN_LIST.containsKey(peer.getAddress())) { return; } - relaunch.add(torrent); + Optional.ofNullable(needRelaunched.get(downloader)).ifPresentOrElse(torrents -> { + if (torrents.contains(torrent)) { + torrents.add(torrent); + } else { + needRelaunched.put(downloader, List.of(torrent)); + } + }, () -> needRelaunched.put(downloader, List.of(torrent))); IPDBResponse ipdbResponse = queryIPDB(peer.getAddress()); BanMetadata banMetadata = new BanMetadata(module, downloader.getName(), System.currentTimeMillis(), System.currentTimeMillis() + banDuration, diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java index a342e945..c96dbca7 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/AbstractRuleBlocker.java @@ -2,7 +2,7 @@ import com.alibaba.cola.statemachine.StateMachine; import com.ghostchu.peerbanhelper.PeerBanHelperServer; -import com.ghostchu.peerbanhelper.util.rule.Rule; +import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; import lombok.Getter; import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; @@ -11,9 +11,9 @@ public abstract class AbstractRuleBlocker extends AbstractFeatureModule implements RuleBlocker { @Getter - public List rules; + public List rules; - public StateMachine stateMachine; + public StateMachine stateMachine; public AbstractRuleBlocker(PeerBanHelperServer server, YamlConfiguration profile) { super(server, profile); @@ -35,7 +35,7 @@ public void onDisable() { } @Override - public StateMachine getStateMachine() { + public StateMachine getStateMachine() { return stateMachine; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java b/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java index 557364df..34862357 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/MatchEvents.java @@ -4,10 +4,6 @@ * */ public enum MatchEvents { - /** - * 匹配规则 - */ - MATCH, /** * 命中规则 */ diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java b/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java index a0c3e7b8..924655c1 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/MatchResultDetail.java @@ -1,5 +1,16 @@ package com.ghostchu.peerbanhelper.module; -public record MatchResultDetail(FeatureModule moduleContext, PeerState state, String rule, String reason, - long expireTime) { +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MatchResultDetail { + private FeatureModule moduleContext; + private PeerState state; + private String rule; + private String reason; + private long expireTime; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchContext.java b/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchContext.java new file mode 100644 index 00000000..849ebb83 --- /dev/null +++ b/src/main/java/com/ghostchu/peerbanhelper/module/PeerMatchContext.java @@ -0,0 +1,18 @@ +package com.ghostchu.peerbanhelper.module; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.function.Consumer; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PeerMatchContext { + private PeerMatchRecord record; + private Consumer activeFunc; + private Consumer banFunc; + private Consumer disconnectFunc; + private Consumer timeoutFunc; +} diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java b/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java index fbcc0c73..bf93b873 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/PeerState.java @@ -8,10 +8,6 @@ public enum PeerState { * 初始化 */ INIT, - /** - * 匹配 - */ - MATCH, /** * 封禁 */ diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java index afc06789..3747c44c 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/RuleBlocker.java @@ -7,7 +7,11 @@ import com.ghostchu.peerbanhelper.text.Lang; import org.slf4j.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.function.Consumer; /** * 基于状态机模型的规则模块接口 @@ -19,43 +23,44 @@ public interface RuleBlocker extends FeatureModule { * * @return 状态机构造器 */ - default StateMachineBuilder ruleSmBuilder() { - StateMachineBuilder fsmBuilder = StateMachineBuilderFactory.create(); - fsmBuilder.externalTransitions().fromAmong(PeerState.INIT, PeerState.ACTIVE).to(PeerState.MATCH).on(MatchEvents.MATCH) - .perform((from, to, event, context) -> { - // 匹配 - CheckResult result = shouldBanPeer(context); - if (result.hit()) { - context.setResult(new MatchResultDetail(this, to, result.rule(), result.reason(), System.currentTimeMillis() + getServer().getDisconnectTimeout())); - triggerEvent(MatchEvents.HIT, context); - } else { - context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectTimeout())); - triggerEvent(MatchEvents.PASS, context); - } - }); - fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.ACTIVE).on(MatchEvents.PASS) + default StateMachineBuilder ruleSmBuilder() { + StateMachineBuilder fsmBuilder = StateMachineBuilderFactory.create(); + fsmBuilder.externalTransitions().fromAmong(PeerState.INIT, PeerState.ACTIVE).to(PeerState.ACTIVE).on(MatchEvents.PASS) .perform((from, to, event, context) -> { // 活跃 - context.setResult(new MatchResultDetail(this, to, "N/A", "No matches", System.currentTimeMillis() + getServer().getDisconnectTimeout())); + Optional.ofNullable(context.getActiveFunc()).ifPresent(func -> func.accept(context.getRecord())); + // 更新过期时间 + context.getRecord().getResult().setState(to); + context.getRecord().getResult().setExpireTime(System.currentTimeMillis() + getServer().getDisconnectTimeout()); }); - fsmBuilder.externalTransition().from(PeerState.MATCH).to(PeerState.BAN).on(MatchEvents.HIT) + fsmBuilder.externalTransitions().fromAmong(PeerState.INIT, PeerState.ACTIVE).to(PeerState.BAN).on(MatchEvents.HIT) .perform((from, to, event, context) -> { // 封禁 - getLogger().debug(Lang.RULE_MODULE_PEER_BAN, getName(), context.getPeer().getAddress()); - context.setResult(new MatchResultDetail(this, to, context.getResult().rule(), context.getResult().reason(), System.currentTimeMillis() + getServer().getBanDuration())); + getLogger().debug(Lang.RULE_MODULE_PEER_HIT, getName(), context.getRecord().getPeer().getAddress()); + // 执行封禁操作 + Optional.ofNullable(context.getBanFunc()).ifPresent(func -> func.accept(context.getRecord())); + // 更新匹配结果 + context.getRecord().getResult().setModuleContext(this); + context.getRecord().getResult().setState(to); + context.getRecord().getResult().setExpireTime(System.currentTimeMillis() + getServer().getBanDuration()); }); - /*fsmBuilder.externalTransition().from(PeerState.ACTIVE).to(PeerState.END).on(PeerEvents.DISCONNECT) + fsmBuilder.externalTransitions().fromAmong(PeerState.INIT, PeerState.ACTIVE).to(PeerState.END).on(MatchEvents.DISCONNECT) .perform((from, to, event, context) -> { // 断开连接 - getLogger().info(Lang.RULE_MODULE_PEER_DISCONNECT, context.getPeer().getPeerId(), context.getPeer().getAddress().getIp(), context.getPeer().getAddress().getPort()); - context.setResult(new MatchResultDetail(this, to, null, null, 0)); + getLogger().debug(Lang.RULE_MODULE_PEER_DISCONNECT, getName(), context.getRecord().getPeer().getAddress()); + // 执行断开操作 + Optional.ofNullable(context.getDisconnectFunc()).ifPresent(func -> func.accept(context.getRecord())); + // 更新匹配结果 + context.getRecord().setResult(new MatchResultDetail(this, to, null, null, 0)); }); - fsmBuilder.externalTransition().from(PeerState.BAN).to(PeerState.END).on(PeerEvents.TIMEOUT) + fsmBuilder.externalTransition().from(PeerState.BAN).to(PeerState.END).on(MatchEvents.TIMEOUT) .perform((from, to, event, context) -> { - // 解除封禁 - getLogger().info(Lang.RULE_MODULE_PEER_BAN_TIMEOUT, context.getPeer().getPeerId(), context.getPeer().getAddress().getIp(), context.getPeer().getAddress().getPort()); - context.setResult(new MatchResultDetail(this, to, null, null, 0)); - });*/ + getLogger().debug(Lang.RULE_MODULE_PEER_BAN_TIMEOUT, getName(), context.getRecord().getPeer().getAddress()); + // 执行解禁操作 + Optional.ofNullable(context.getTimeoutFunc()).ifPresent(func -> func.accept(context.getRecord())); + // 更新匹配结果 + context.getRecord().setResult(new MatchResultDetail(this, to, null, null, 0)); + }); return fsmBuilder; } @@ -78,7 +83,7 @@ default StateMachineBuilder ruleSmBuild * * @return 状态机 */ - StateMachine getStateMachine(); + StateMachine getStateMachine(); /** * 是否应该封禁Peer @@ -91,9 +96,46 @@ default StateMachineBuilder ruleSmBuild /** * 运行规则检查 */ - default void runCheck() { + default void runCheck(Consumer activeFunc, Consumer banFunc, Consumer disconnectFunc, Consumer timeoutFunc) { // long t1 = System.currentTimeMillis(); - getServer().getLivePeersSnapshot().keySet().forEach(peerAddress -> Optional.ofNullable(getServer().getMatchRecords().get(peerAddress)).ifPresent(record -> triggerEvent(MatchEvents.MATCH, record))); + List removeLIst = new ArrayList<>(); + Map matchRecords = getServer().getMatchRecords(); + matchRecords.forEach((key, record) -> { + // 构建匹配上下文 + PeerMatchContext context = new PeerMatchContext(record, activeFunc, banFunc, disconnectFunc, timeoutFunc); + MatchResultDetail result = record.getResult(); + switch (result.getState()) { + case INIT, ACTIVE: + if (result.getExpireTime() < System.currentTimeMillis()) { + // 已经超时的记录直接归到END状态 + triggerEvent(MatchEvents.DISCONNECT, context); + break; + } + // 判断是否需要封禁 + CheckResult banResult = shouldBanPeer(record); + if (banResult.hit()) { + // 命中 + context.getRecord().getResult().setRule(banResult.rule()); + context.getRecord().getResult().setReason(banResult.reason()); + triggerEvent(MatchEvents.HIT, context); + } else { + // 通过 + triggerEvent(MatchEvents.PASS, context); + } + break; + case BAN: + if (result.getExpireTime() < System.currentTimeMillis()) { + // 已经超时的记录直接归到END状态 + triggerEvent(MatchEvents.TIMEOUT, context); + } + break; + default: + // END状态直接移除 + removeLIst.add(key); + break; + } + }); + removeLIst.forEach(matchRecords::remove); // long t2 = System.currentTimeMillis(); // getLogger().debug(Lang.RULE_MODULE_MATCH_TIME, getName(), t2 - t1); } @@ -104,8 +146,8 @@ default void runCheck() { * @param event 事件 * @param context 上下文 */ - default void triggerEvent(MatchEvents event, PeerMatchRecord context) { - getStateMachine().fireEvent(context.getResult().state(), event, context); + default void triggerEvent(MatchEvents event, PeerMatchContext context) { + getStateMachine().fireEvent(context.getRecord().getResult().getState(), event, context); } record CheckResult(boolean hit, String rule, String reason) { diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java index fd2323c2..744eec32 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/rule/RuleSubBlocker.java @@ -13,7 +13,6 @@ import com.ghostchu.peerbanhelper.util.HTTPUtil; import com.ghostchu.peerbanhelper.util.IPAddressUtil; import com.ghostchu.peerbanhelper.util.rule.MatchResult; -import com.ghostchu.peerbanhelper.util.rule.RuleMatcher; import com.ghostchu.peerbanhelper.util.rule.RuleType; import com.ghostchu.peerbanhelper.util.rule.matcher.IPMatcher; import com.ghostchu.peerbanhelper.util.rule.matcher.PrefixMatcher; @@ -39,7 +38,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -115,7 +113,6 @@ private void reloadConfig() { @Override public CheckResult shouldBanPeer(PeerMatchRecord ctx) { AtomicReference result = new AtomicReference<>(new CheckResult(false, null, null)); - CountDownLatch latch = new CountDownLatch(rules.size()); try (var service = Executors.newVirtualThreadPerTaskExecutor()) { rules.forEach(matcher -> service.submit(() -> { String matchStr; @@ -130,14 +127,8 @@ public CheckResult shouldBanPeer(PeerMatchRecord ctx) { if (matcher.match(matchStr) == MatchResult.TRUE) { result.set(new CheckResult(true, matcher.metadata().get("rule").toString(), String.format(Lang.MODULE_IBL_MATCH_SUB_RULE, matcher.metadata().get("rule").toString()))); } - latch.countDown(); })); } - try { - latch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } return result.get(); } @@ -153,7 +144,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd // 检查ipBanMatchers是否有对应的规则,有则删除 rules.removeIf(ele -> ele.metadata().get("id").equals(ruleId)); // 未启用跳过更新逻辑 - return new SlimMsg(false, Lang.SUB_RULE_DISABLED.replace("{}", ruleId)); + return new SlimMsg(false, String.format(Lang.SUB_RULE_DISABLED, ruleId)); } String name = rule.getString("name", ruleId); String url = rule.getString("url"); @@ -166,7 +157,6 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd File tempFile = new File(dir, "temp_" + ruleFileName); File ruleFile = new File(dir, ruleFileName); List ipAddresses = new ArrayList<>(); - List subnetAddresses = new ArrayList<>(); List fileLines = new ArrayList<>(); HTTPUtil.retryableSend(HTTPUtil.getHttpClient(false, null), MutableRequest.GET(url), HttpResponse.BodyHandlers.ofFile(Path.of(tempFile.getPath()))).whenComplete((pathHttpResponse, throwable) -> { if (throwable != null) { @@ -186,8 +176,8 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd rules.add(new SubStrMatcher(ruleType, ruleId, name, fileLines)); } default -> { - fileToIPList(name, ruleFile, ipAddresses, subnetAddresses); - rules.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); + fileToIPList(name, ruleFile, ipAddresses); + rules.add(new IPMatcher(ruleId, name, ipAddresses)); } } log.warn(Lang.SUB_RULE_USE_CACHE, name); @@ -197,13 +187,13 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd result.set(new SlimMsg(false, Lang.SUB_RULE_LOAD_FAILED.replace("{}", name))); } } else { + log.error(Lang.SUB_RULE_UPDATE_FAILED, name); result.set(new SlimMsg(false, Lang.SUB_RULE_UPDATE_FAILED.replace("{}", name))); } } else { // log.error(Lang.IP_BAN_RULE_LOAD_FAILED, name, throwable); result.set(new SlimMsg(false, Lang.SUB_RULE_LOAD_FAILED.replace("{}", name))); } - throw new RuntimeException(throwable); } try { HashCode ruleHash = null; @@ -217,7 +207,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd ent_count = switch (ruleType) { case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, CLIENT_NAME_CONTAINS -> fileToLines(name, ruleFile, fileLines); - default -> fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + default -> fileToIPList(name, tempFile, ipAddresses); }; // 更新后重命名临时文件 ruleFile.delete(); @@ -228,7 +218,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd ent_count = switch (ruleType) { case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, CLIENT_NAME_CONTAINS -> fileToLines(name, ruleFile, fileLines); - default -> fileToIPList(name, tempFile, ipAddresses, subnetAddresses); + default -> fileToIPList(name, tempFile, ipAddresses); }; } else { log.info(Lang.SUB_RULE_NO_UPDATE, name); @@ -237,13 +227,13 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd tempFile.delete(); } // ip列表或者subnet列表不为空代表需要更新matcher - if (!ipAddresses.isEmpty() || !subnetAddresses.isEmpty() || !fileLines.isEmpty()) { + if (!ipAddresses.isEmpty() || !fileLines.isEmpty()) { // 如果已经存在则更新,否则添加 rules.stream().filter(ele -> ele.metadata().get("id").equals(ruleId)).findFirst().ifPresentOrElse(ele -> { switch (ruleType) { case PEER_ID_STARTS_WITH, PEER_ID_CONTAINS, CLIENT_NAME_STARTS_WITH, - CLIENT_NAME_CONTAINS -> ((RuleMatcher) ele).setData(name, fileLines); - default -> ((IPMatcher) ele).setData(name, ipAddresses, subnetAddresses); + CLIENT_NAME_CONTAINS -> ele.setData(name, fileLines); + default -> ele.setData(name, ipAddresses); } log.info(Lang.SUB_RULE_UPDATE_SUCCESS, name); result.set(new SlimMsg(true, Lang.SUB_RULE_UPDATE_SUCCESS.replace("{}", name))); @@ -253,7 +243,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd rules.add(new PrefixMatcher(ruleType, ruleId, name, fileLines)); case PEER_ID_CONTAINS, CLIENT_NAME_CONTAINS -> rules.add(new SubStrMatcher(ruleType, ruleId, name, fileLines)); - default -> rules.add(new IPMatcher(ruleId, name, ipAddresses, subnetAddresses)); + default -> rules.add(new IPMatcher(ruleId, name, ipAddresses)); } log.info(Lang.SUB_RULE_LOAD_SUCCESS, name); result.set(new SlimMsg(true, Lang.SUB_RULE_LOAD_SUCCESS.replace("{}", name))); @@ -263,7 +253,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd // 更新日志 try { db.insertRuleSubLog(ruleId, ent_count, updateType); - result.set(new SlimMsg(true, Lang.SUB_RULE_UPDATED.replace("{}", name))); + result.set(new SlimMsg(true, String.format(Lang.SUB_RULE_UPDATED, name))); } catch (SQLException e) { log.error(Lang.SUB_RULE_UPDATE_LOG_ERROR, ruleId, e); result.set(new SlimMsg(false, Lang.SUB_RULE_UPDATE_LOG_ERROR.replace("{}", name))); @@ -276,7 +266,7 @@ public SlimMsg updateRule(@NotNull ConfigurationSection rule, RuleUpdateType upd } }).join(); } else { - result.set(new SlimMsg(false, Lang.SUB_RULE_URL_WRONG.replace("{}", name))); + result.set(new SlimMsg(false, String.format(Lang.SUB_RULE_URL_WRONG, name))); } return result.get(); } @@ -306,35 +296,15 @@ private int fileToLines(String ruleName, File ruleFile, List lines) thro * * @param ruleName 规则名称 * @param ruleFile 规则文件 - * @param ips 精确ip列表 - * @param subnets 网段列表 + * @param ips ip列表 * @return 加载的行数 */ - private int fileToIPList(String ruleName, File ruleFile, List ips, List subnets) throws IOException { + private int fileToIPList(String ruleName, File ruleFile, List ips) throws IOException { AtomicInteger count = new AtomicInteger(); Files.readLines(ruleFile, StandardCharsets.UTF_8).forEach(ele -> { count.getAndIncrement(); - IPAddress ipAddress = IPAddressUtil.getIPAddress(ele); - // 判断是否是网段 - List ipsList = new ArrayList<>(); - if (null != ipAddress.getNetworkPrefixLength()) { - if (ipAddress.isIPv4Convertible() && ipAddress.getNetworkPrefixLength() >= 20) { - // 前缀长度 >= 20 的ipv4网段地址转为精确ip - ipAddress.nonZeroHostIterator().forEachRemaining(ipsList::add); - } else { - subnets.add(ipAddress); - log.debug(Lang.IP_BAN_RULE_LOAD_CIDR, ruleName, ipAddress); - } - } else { - ipsList.add(ipAddress); - } - ipsList.forEach(ip -> { - if (ip.isIPv4Convertible()) { - ip = ip.toIPv4().withoutPrefixLength(); - } - ips.add(ip); - log.debug(Lang.IP_BAN_RULE_LOAD_IP, ruleName, ip); - }); + log.debug(Lang.IP_BAN_RULE_LOAD_CIDR, ruleName, ele); + ips.add(IPAddressUtil.getIPAddress(ele)); }); return count.get(); } diff --git a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java index 5cef0dba..fd5bc9ff 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java +++ b/src/main/java/com/ghostchu/peerbanhelper/module/impl/webapi/RuleSubController.java @@ -155,7 +155,7 @@ private SlimMsg update(String ruleId) { } ConfigurationSection configurationSection = ruleSubBlocker.getRuleSubsConfig().getConfigurationSection(ruleId); if (null == configurationSection) { - return new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId)); + return new SlimMsg(false, String.format(Lang.SUB_RULE_CANT_FIND, ruleId)); } return ruleSubBlocker.updateRule(configurationSection, RuleUpdateType.MANUAL); } @@ -178,10 +178,10 @@ private void switcher(Context ctx) throws SQLException, IOException { RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, String.format(Lang.SUB_RULE_CANT_FIND, ruleId))); return; } - String msg = (enabled ? Lang.SUB_RULE_ENABLED : Lang.SUB_RULE_DISABLED).replace("{}", ruleSubInfo.ruleName()); + String msg = String.format(enabled ? Lang.SUB_RULE_ENABLED : Lang.SUB_RULE_DISABLED, ruleSubInfo.ruleName()); if (enabled != ruleSubInfo.enabled()) { ConfigurationSection configurationSection = ruleSubBlocker.saveRuleSubInfo(new RuleSubInfo(ruleId, enabled, ruleSubInfo.ruleName(), ruleSubInfo.subUrl(), 0, 0)); ruleSubBlocker.updateRule(configurationSection, RuleUpdateType.MANUAL); @@ -203,12 +203,12 @@ private void delete(Context ctx) throws IOException, SQLException { RuleSubInfo ruleSubInfo = ruleSubBlocker.getRuleSubInfo(ruleId); if (null == ruleSubInfo) { ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, String.format(Lang.SUB_RULE_CANT_FIND, ruleId))); return; } ruleSubBlocker.deleteRuleSubInfo(ruleId); ruleSubBlocker.getRules().removeIf(ele -> ele.metadata().get("id").equals(ruleId)); - String msg = Lang.SUB_RULE_DELETED.replace("{}", ruleSubInfo.ruleName()); + String msg = String.format(Lang.SUB_RULE_DELETED, ruleSubInfo.ruleName()); log.info(msg); ctx.json(new SlimMsg(true, msg)); } @@ -234,13 +234,13 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException if (isAdd && ruleSubInfo != null) { // 新增时检查规则是否存在 ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.SUB_RULE_ID_CONFLICT.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, String.format(Lang.SUB_RULE_ID_CONFLICT, ruleId))); return; } if (!isAdd && ruleSubInfo == null) { // 更新时检查规则是否存在 ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.SUB_RULE_CANT_FIND.replace("{}", ruleId))); + ctx.json(new SlimMsg(false, String.format(Lang.SUB_RULE_CANT_FIND, ruleId))); return; } String ruleName = subInfo.ruleName(); @@ -277,7 +277,7 @@ private void save(Context ctx, String ruleId, boolean isAdd) throws SQLException ruleSubBlocker.saveRuleSubInfo(ruleSubInfo); } ctx.status(HttpStatus.BAD_REQUEST); - ctx.json(new SlimMsg(false, Lang.SUB_RULE_URL_WRONG.replace("{}", ruleName))); + ctx.json(new SlimMsg(false, String.format(Lang.SUB_RULE_URL_WRONG, ruleName))); } } diff --git a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java index 3c553872..9b1bba87 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java +++ b/src/main/java/com/ghostchu/peerbanhelper/text/Lang.java @@ -182,16 +182,16 @@ public class Lang { public static final String SUB_RULE_LOAD_CONTENT = "订阅规则 {} 加载内容 : {}"; public static final String RULE_SUB_API_INTERNAL_ERROR = "[错误] 订阅规则API遇到非预期错误"; public static final String SUB_RULE_NO_ID = "[错误] 订阅规则ID为空"; - public static final String SUB_RULE_ID_CONFLICT = "[错误] 订阅规则ID冲突: {}"; - public static final String SUB_RULE_CANT_FIND = "[错误] 未找到订阅规则: {}"; + public static final String SUB_RULE_ID_CONFLICT = "[错误] 订阅规则ID冲突: %s"; + public static final String SUB_RULE_CANT_FIND = "[错误] 未找到订阅规则: %s"; public static final String SUB_RULE_PARAM_WRONG = "[错误] 订阅规则参数错误"; - public static final String SUB_RULE_URL_WRONG = "[错误] 订阅规则 {} URL错误"; - public static final String SUB_RULE_ENABLED = "订阅规则 {} 已启用"; - public static final String SUB_RULE_DISABLED = "订阅规则 {} 已禁用"; - public static final String SUB_RULE_UPDATED = "订阅规则 {} 已更新"; + public static final String SUB_RULE_URL_WRONG = "[错误] 订阅规则 %s URL错误"; + public static final String SUB_RULE_ENABLED = "订阅规则 %s 已启用"; + public static final String SUB_RULE_DISABLED = "订阅规则 %s 已禁用"; + public static final String SUB_RULE_UPDATED = "订阅规则 %s 已更新"; public static final String SUB_RULE_ALL_UPDATED = "订阅规则已全部更新"; public static final String SUB_RULE_SAVED = "订阅规则已保存"; - public static final String SUB_RULE_DELETED = "订阅规则 {} 已删除"; + public static final String SUB_RULE_DELETED = "订阅规则 %s 已删除"; public static final String SUB_RULE_INFO_QUERY_SUCCESS = "订阅规则查询成功"; public static final String SUB_RULE_LOG_QUERY_SUCCESS = "订阅规则更新日志查询成功"; public static final String SUB_RULE_LOG_QUERY_ERROR = "订阅规则更新日志查询出错"; @@ -213,5 +213,7 @@ public class Lang { public static final String WEBVIEW_DISABLED_WEBKIT_NOT_INCLUDED = "未找到 JavaFx Web 模块,您正在使用精简构建,WebUI 选项卡未启用"; public static final String WEBVIEW_ENABLED = "已找到 JavaFx Web,WebUI 选项卡已启用"; public static final String RULE_MODULE_MATCH_TIME = "规则模块 {} 匹配花费时间:{}ms"; - public static final String RULE_MODULE_PEER_BAN = "[封禁] 规则模块 {} : {} "; + public static final String RULE_MODULE_PEER_HIT = "规则模块 {} : {} 命中"; + public static final String RULE_MODULE_PEER_DISCONNECT = "规则模块 {} : {} 已断开连接"; + public static final String RULE_MODULE_PEER_BAN_TIMEOUT = "规则模块 {} : {} 已解禁"; } diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java index 98f34a68..c9e6f1bc 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/RuleMatcher.java @@ -4,6 +4,7 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import java.util.List; import java.util.Map; /** @@ -12,7 +13,7 @@ @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public abstract class RuleMatcher extends AbstractMatcher { +public abstract class RuleMatcher extends AbstractMatcher { private final String ruleId; @@ -20,13 +21,13 @@ public abstract class RuleMatcher extends AbstractMatcher { protected String ruleName; - public RuleMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + public RuleMatcher(RuleType ruleType, String ruleId, String ruleName, List ruleData) { this.ruleType = ruleType; this.ruleId = ruleId; setData(ruleName, ruleData); } - public abstract void setData(String ruleName, Object... ruleData); + public abstract void setData(String ruleName, List ruleData); public Map metadata() { return Map.of("id", ruleId, "rule", ruleName, "type", ruleType); diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java index f85e733b..667e7f98 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/IPMatcher.java @@ -14,26 +14,42 @@ import org.jetbrains.annotations.NotNull; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IPMatcher extends RuleMatcher { +public class IPMatcher extends RuleMatcher { private List subnets; private List ips; private BloomFilter bloomFilter; - public IPMatcher(String ruleId, String ruleName, Object... ruleData) { + public IPMatcher(String ruleId, String ruleName, List ruleData) { super(RuleType.IP, ruleId, ruleName, ruleData); } @Override - public void setData(String ruleName, Object... ruleData) { + public void setData(String ruleName, List ruleData) { this.ruleName = ruleName; - this.ips = ruleData.length > 0 ? (List) ruleData[0] : List.of(); - this.subnets = ruleData.length > 1 ? (List) ruleData[1] : List.of(); + this.ips = new ArrayList<>(); + this.subnets = new ArrayList<>(); + Optional.ofNullable(ruleData).ifPresent(list -> list.forEach(ipAddress -> { + // 判断是否是网段 + if (null != ipAddress.getNetworkPrefixLength()) { + if (ipAddress.isIPv4Convertible() && ipAddress.getNetworkPrefixLength() >= 20) { + // 前缀长度 >= 20 的ipv4网段地址转为精确ip + ipAddress.nonZeroHostIterator().forEachRemaining(ips::add); + } else { + subnets.add(ipAddress); + log.debug(Lang.IP_BAN_RULE_LOAD_CIDR, ruleName, ipAddress); + } + } else { + ips.add(ipAddress); + } + })); bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), ips.size(), 0.01); ips.forEach(ip -> bloomFilter.put(ip.toString())); } diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java index 56c94669..54a23137 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/PrefixMatcher.java @@ -9,28 +9,30 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class PrefixMatcher extends RuleMatcher { +public class PrefixMatcher extends RuleMatcher { private List prefixes; - public PrefixMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + public PrefixMatcher(RuleType ruleType, String ruleId, String ruleName, List ruleData) { super(ruleType, ruleId, ruleName, ruleData); } @Override - public void setData(String ruleName, Object... ruleData) { + public void setData(String ruleName, List ruleData) { this.ruleName = ruleName; - this.prefixes = ruleData.length > 0 ? (List) ruleData[0] : List.of(); + Optional.ofNullable(ruleData).ifPresentOrElse(list -> this.prefixes = list, () -> this.prefixes = new ArrayList<>()); } @Override public @NotNull MatchResult match0(@NotNull String content) { - if (prefixes.parallelStream().anyMatch(content::startsWith)) { + if (prefixes.stream().anyMatch(content::startsWith)) { return MatchResult.TRUE; } return MatchResult.DEFAULT; diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java index f6c84513..43787874 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/rule/matcher/SubStrMatcher.java @@ -9,28 +9,30 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Slf4j @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class SubStrMatcher extends RuleMatcher { +public class SubStrMatcher extends RuleMatcher { - private List subStrs; + private List subs; - public SubStrMatcher(RuleType ruleType, String ruleId, String ruleName, Object... ruleData) { + public SubStrMatcher(RuleType ruleType, String ruleId, String ruleName, List ruleData) { super(ruleType, ruleId, ruleName, ruleData); } @Override - public void setData(String ruleName, Object... ruleData) { + public void setData(String ruleName, List ruleData) { this.ruleName = ruleName; - this.subStrs = ruleData.length > 0 ? (List) ruleData[0] : List.of(); + Optional.ofNullable(ruleData).ifPresentOrElse(list -> this.subs = list, () -> this.subs = new ArrayList<>()); } @Override public @NotNull MatchResult match0(@NotNull String content) { - if (subStrs.parallelStream().anyMatch(content::contains)) { + if (subs.stream().anyMatch(content::contains)) { return MatchResult.TRUE; } return MatchResult.DEFAULT; diff --git a/src/main/java/com/ghostchu/peerbanhelper/util/time/ExceptedTime.java b/src/main/java/com/ghostchu/peerbanhelper/util/time/ExceptedTime.java index 7c017cb2..b619888b 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/util/time/ExceptedTime.java +++ b/src/main/java/com/ghostchu/peerbanhelper/util/time/ExceptedTime.java @@ -9,7 +9,9 @@ public enum ExceptedTime { CHECK_BANS(30 * 1000), ADD_BAN_ENTRY(30 * 1000), APPLY_BANLIST(30 * 1000), - STAGE_BAN_WAVE(COLLECT_PEERS.timeout + CHECK_BANS.timeout + ADD_BAN_ENTRY.timeout + APPLY_BANLIST.timeout + 10 * 1000); + STAGE_BAN_WAVE(COLLECT_PEERS.timeout + CHECK_BANS.timeout + ADD_BAN_ENTRY.timeout + APPLY_BANLIST.timeout + 10 * 1000), + RUN_BLOCKER(30 * 1000), + BAN_PEER(3 * 1000); @Getter private final long timeout; From 747e552caaf3872092492cb134b1e5acc4bff85d Mon Sep 17 00:00:00 2001 From: PluieM <644202913@qq.com> Date: Thu, 20 Jun 2024 16:46:03 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E8=B0=83=E6=95=B4merge=E9=81=97=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java index 956be299..9a8860c5 100644 --- a/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java +++ b/src/main/java/com/ghostchu/peerbanhelper/PeerBanHelperServer.java @@ -1,6 +1,5 @@ package com.ghostchu.peerbanhelper; -import com.ghostchu.peerbanhelper.alert.AlertManager; import com.ghostchu.peerbanhelper.btn.BtnNetwork; import com.ghostchu.peerbanhelper.database.DatabaseHelper; import com.ghostchu.peerbanhelper.database.DatabaseManager; @@ -106,7 +105,7 @@ public class PeerBanHelperServer { @Getter private JavalinWebContainer webContainer; @Getter - private final Map matchRecords = new ConcurrentHashMap<>(); + private final Map matchRecords = new ConcurrentHashMap<>(); public PeerBanHelperServer(String pbhServerAddress, YamlConfiguration profile, YamlConfiguration mainConfig) throws SQLException { this.pbhServerAddress = pbhServerAddress;