From 139efeec0b953a2d9240b98f45d611ea0ecf0b54 Mon Sep 17 00:00:00 2001 From: shizhendong Date: Thu, 12 Sep 2024 15:51:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ASW-64=20=E6=96=B0=E5=A2=9E=20api=20acl?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../geedge/api/controller/APIController.java | 57 +++++++++ .../geedge/api/util/AdbCommandBuilder.java | 62 ++++++++++ .../java/net/geedge/api/util/AdbUtil.java | 108 ++++++++++++++++++ src/main/java/net/geedge/common/Constant.java | 6 +- 4 files changed, 231 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/geedge/api/controller/APIController.java b/src/main/java/net/geedge/api/controller/APIController.java index db2eee3..397b210 100644 --- a/src/main/java/net/geedge/api/controller/APIController.java +++ b/src/main/java/net/geedge/api/controller/APIController.java @@ -189,4 +189,61 @@ public class APIController { Integer timeout = T.MapUtil.getInt(requestBody, "timeout", 10); return R.ok().putData("result", adbUtil.execShellCommand(cmd, timeout)); } + + @GetMapping("/acl") + public R listAcl() { + return R.ok().putData("records", adbUtil.listAcl()); + } + + @PostMapping("/acl") + public R addAcl(@RequestBody Map requestBody) { + String ip = T.MapUtil.getStr(requestBody, "ip"); + String port = T.MapUtil.getStr(requestBody, "port"); + if (T.StrUtil.isAllEmpty(ip, port)) { + return R.error(RCode.BAD_REQUEST); + } + + String protocol = T.MapUtil.getStr(requestBody, "protocol", "all"); + if (!T.StrUtil.equalsAny(protocol, "tcp", "udp", "all")) { + return R.error(RCode.BAD_REQUEST); + } + + if ("all".equals(protocol) && T.StrUtil.isEmpty(ip)) { + return R.error(RCode.BAD_REQUEST); + } + + adbUtil.addAcl(protocol, ip, port); + return R.ok().putData("records", adbUtil.listAcl()); + } + + @DeleteMapping("/acl") + public R deleteAcl(@RequestBody Map requestBody) { + String ip = T.MapUtil.getStr(requestBody, "ip"); + String port = T.MapUtil.getStr(requestBody, "port"); + if (T.StrUtil.isAllEmpty(ip, port)) { + return R.error(RCode.BAD_REQUEST); + } + + String protocol = T.MapUtil.getStr(requestBody, "protocol", "all"); + if (!T.StrUtil.equalsAny(protocol, "tcp", "udp", "all")) { + return R.error(RCode.BAD_REQUEST); + } + + if ("all".equals(protocol) && T.StrUtil.isEmpty(ip)) { + return R.error(RCode.BAD_REQUEST); + } + + adbUtil.deleteAcl(protocol, ip, port); + return R.ok().putData("records", adbUtil.listAcl()); + } + + @DeleteMapping("/acl/flush") + public R flushAcl() { + AdbUtil.CommandResult result = adbUtil.flushAcl(); + if (0 != result.exitCode()) { + return R.error(result.output()); + } + return R.ok(); + } + } \ No newline at end of file diff --git a/src/main/java/net/geedge/api/util/AdbCommandBuilder.java b/src/main/java/net/geedge/api/util/AdbCommandBuilder.java index 5335d41..f77d70e 100644 --- a/src/main/java/net/geedge/api/util/AdbCommandBuilder.java +++ b/src/main/java/net/geedge/api/util/AdbCommandBuilder.java @@ -136,6 +136,68 @@ public class AdbCommandBuilder { return this; } + public AdbCommandBuilder buildIptablesLnRulesCommand(String chainName) { + this.command.add("shell"); + this.command.add("iptables"); + this.command.add("-nL"); + this.command.add(chainName); + this.command.add("--line-numbers"); + return this; + } + + public AdbCommandBuilder buildIptablesAddRuleCommand(String chainName, String protocol, String ip, String port) { + this.command.add("shell"); + this.command.add("iptables"); + this.command.add("-A"); + this.command.add(chainName); + this.command.add("-p"); + this.command.add(protocol); + + if (T.StrUtil.isNotEmpty(ip)) { + this.command.add("-d"); + this.command.add(ip); + } + + if (T.StrUtil.isNotEmpty(port) && !"all".equals(protocol)) { + this.command.add("--dport"); + this.command.add(port); + } + + this.command.add("-j"); + this.command.add("ACCEPT"); + return this; + } + + public AdbCommandBuilder buildIptablesDelRuleCommand(String chainName, String protocol, String ip, String port) { + this.command.add("shell"); + this.command.add("iptables"); + this.command.add("-D"); + this.command.add(chainName); + this.command.add("-p"); + this.command.add(protocol); + + if (T.StrUtil.isNotEmpty(ip)) { + this.command.add("-d"); + this.command.add(ip); + } + + if (T.StrUtil.isNotEmpty(port) && !"all".equals(protocol)) { + this.command.add("--dport"); + this.command.add(port); + } + + this.command.add("-j"); + this.command.add("ACCEPT"); + return this; + } + + public AdbCommandBuilder buildIptablesFlushRuleCommand(String chainName) { + this.command.add("shell"); + this.command.add("iptables"); + this.command.add("-F"); + this.command.add(chainName); + return this; + } public List build() { return this.command; diff --git a/src/main/java/net/geedge/api/util/AdbUtil.java b/src/main/java/net/geedge/api/util/AdbUtil.java index c0df871..a9a609a 100644 --- a/src/main/java/net/geedge/api/util/AdbUtil.java +++ b/src/main/java/net/geedge/api/util/AdbUtil.java @@ -120,6 +120,9 @@ public class AdbUtil { // 后台启动 this.execShellCommand("shell am start-foreground-service -n net.christianbeier.droidvnc_ng/.MainService -a net.christianbeier.droidvnc_ng.ACTION_STOP --es net.christianbeier.droidvnc_ng.EXTRA_ACCESS_KEY d042e2b5d5f348588a4e1a243eb7a9a0"); this.execShellCommand("shell am start-foreground-service -n net.christianbeier.droidvnc_ng/.MainService -a net.christianbeier.droidvnc_ng.ACTION_START --es net.christianbeier.droidvnc_ng.EXTRA_ACCESS_KEY d042e2b5d5f348588a4e1a243eb7a9a0"); + + // 添加自定义链 + this.addAswOutputChain(); } /** @@ -703,6 +706,111 @@ public class AdbUtil { } } + /** + * 1. 添加自定义链 + * 2. 自定义链添加到 OUTPUT 链中 + */ + private void addAswOutputChain() { + // name=ASW_OUTPUT + this.execShellCommand("shell iptables -N ASW_OUTPUT"); + + String outputChainResult = CommandExec.exec(AdbCommandBuilder.builder() + .serial(this.getSerial()) + .buildShellCommand(String.format("shell \"iptables -L OUTPUT --line-numbers | grep ASW_OUTPUT\"")) + .build()); + log.info("[addAswOutputChain] [ASW_OUTPUT in OUTPUT Chain] [result: {}]", outputChainResult); + if (T.StrUtil.isEmpty(outputChainResult)) { + // ASW_OUTPUT 添加到 OUTPUT 链中 + this.execShellCommand("shell iptables -A OUTPUT -j ASW_OUTPUT"); + } + } + + /** + * ASW_OUTPUT chain rules + * iptables -nL ASW_OUTPUT --line-numbers + */ + public List listAcl() { + String result = CommandExec.exec(AdbCommandBuilder.builder() + .serial(this.getSerial()) + .buildIptablesLnRulesCommand("ASW_OUTPUT") + .build()); + + List chainList = T.ListUtil.list(true); + + String[] lines = result.split("\\n"); + for (String line : lines) { + String[] split = line.split("\\s+"); + + String chainIndex = T.StrUtil.trim(split[0]); + + if (T.StrUtil.isNumeric(chainIndex)) { + String protocol = T.StrUtil.trim(split[2]); + Map m = T.MapUtil.builder() + .put("num", Integer.valueOf(chainIndex)) + .put("protocol", protocol) + .build(); + + String destIp = T.StrUtil.trim(split[5]); + if (!T.StrUtil.equals("0.0.0.0/0", destIp)) { + m.put("ip", destIp); + } + + if (split.length == 8) { + String dpt = T.StrUtil.trim(split[7]); + dpt = dpt.replaceAll("dpt:", ""); + if (T.StrUtil.isNumeric(chainIndex)) { + m.put("port", Integer.valueOf(dpt)); + } + } + chainList.add(m); + } + } + return chainList; + } + + /** + * add chain rule + * iptables -A ASW_OUTPUT -p prot -d ip --dport port -j ACCEPT + */ + public void addAcl(String protocol, String ip, String port) { + // add chain + this.addAswOutputChain(); + + // add chain ruls + String result = CommandExec.exec(AdbCommandBuilder.builder() + .serial(this.getSerial()) + .buildIptablesAddRuleCommand("ASW_OUTPUT", protocol, ip, port) + .build()); + log.info("[addAcl] [protocol: {}] [ip: {}] [port: {}] [result: {}]", protocol, ip, port, result); + } + + + /** + * del chain rule + * iptables -D ASW_OUTPUT -p prot -d ip --dport port -j ACCEPT + */ + public void deleteAcl(String protocol, String ip, String port) { + // add chain ruls + String result = CommandExec.exec(AdbCommandBuilder.builder() + .serial(this.getSerial()) + .buildIptablesDelRuleCommand("ASW_OUTPUT", protocol, ip, port) + .build()); + log.info("[deleteAcl] [protocol: {}] [ip: {}] [port: {}] [result: {}]", protocol, ip, port, result); + } + + /** + * flushAcl + * iptables -F ASW_OUTPUT + */ + public CommandResult flushAcl() { + String result = CommandExec.exec(AdbCommandBuilder.builder() + .serial(this.getSerial()) + .buildIptablesFlushRuleCommand("ASW_OUTPUT") + .build()); + log.info("[flushAcl] [result: {}]", result); + return new CommandResult(T.StrUtil.isNotEmpty(result) ? 1 : 0, result); + } + private synchronized ExecutorService getThreadPool() { if (threadPool == null) { threadPool = new ThreadPoolExecutor( diff --git a/src/main/java/net/geedge/common/Constant.java b/src/main/java/net/geedge/common/Constant.java index b1b4f6b..a79b36b 100644 --- a/src/main/java/net/geedge/common/Constant.java +++ b/src/main/java/net/geedge/common/Constant.java @@ -11,7 +11,9 @@ public class Constant { static { File tempPath = T.FileUtil.file(TEMP_PATH); // 程序启动清空临时目录 - T.FileUtil.del(tempPath); - T.FileUtil.mkdir(tempPath); + // T.FileUtil.del(tempPath); + if (!T.FileUtil.exist(tempPath)) { + T.FileUtil.mkdir(tempPath); + } } } \ No newline at end of file