mirror of
https://gitee.com/ja-netfilter/ja-netfilter.git
synced 2024-12-22 08:59:25 +08:00
add config file
Signed-off-by: pengzhile <pengzhile@gmail.com>
This commit is contained in:
parent
f4a6ff6d43
commit
52af80fb7f
@ -1,5 +1,11 @@
|
||||
package io.zhile.research.ja.netfilter;
|
||||
|
||||
import io.zhile.research.ja.netfilter.commons.ConfigDetector;
|
||||
import io.zhile.research.ja.netfilter.commons.ConfigParser;
|
||||
import io.zhile.research.ja.netfilter.commons.DebugInfo;
|
||||
import io.zhile.research.ja.netfilter.models.FilterConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.UnmodifiableClassException;
|
||||
import java.net.MalformedURLException;
|
||||
@ -25,6 +31,19 @@ public class Launcher {
|
||||
throw new RuntimeException("Can not access ja-netfilter jar file.", e);
|
||||
}
|
||||
|
||||
File configFile = ConfigDetector.detect(new File(jarURL.getPath()).getParentFile().getPath(), args);
|
||||
if (null == configFile) {
|
||||
DebugInfo.output("Could not find any configuration files.");
|
||||
} else {
|
||||
DebugInfo.output("Current config file: " + configFile.getPath());
|
||||
}
|
||||
|
||||
try {
|
||||
FilterConfig.setCurrent(new FilterConfig(ConfigParser.parse(configFile)));
|
||||
} catch (Exception e) {
|
||||
DebugInfo.output(e.getMessage());
|
||||
}
|
||||
|
||||
for (Class<?> c : inst.getAllLoadedClasses()) {
|
||||
try {
|
||||
inst.retransformClasses(c);
|
||||
|
@ -34,7 +34,7 @@ public class TransformDispatcher implements ClassFileTransformer {
|
||||
try {
|
||||
return transformer.transform(className, classFileBuffer);
|
||||
} catch (Exception e) {
|
||||
DebugInfo.output("=== Transform class failed: " + e.getMessage());
|
||||
DebugInfo.output("Transform class failed: " + e.getMessage());
|
||||
}
|
||||
} while (false);
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
package io.zhile.research.ja.netfilter.commons;
|
||||
|
||||
import io.zhile.research.ja.netfilter.utils.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ConfigDetector {
|
||||
private static final String CONFIG_FILENAME = "janf_config.txt";
|
||||
|
||||
public static File detect(String currentDirectory, String args) {
|
||||
File configFile = tryFile(args); // by javaagent argument
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = tryFile(System.getenv("JANF_CONFIG")); // by env
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = tryFile(System.getProperty("janf.config")); // by -D argument
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory(currentDirectory); // in the same dir as the jar
|
||||
}
|
||||
|
||||
String userHome = System.getProperty("user.home");
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory(userHome, "." + CONFIG_FILENAME); // $HOME/.janf_config.txt
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory(userHome + File.pathSeparator + ".config"); // $HOME/.config/janf_config.txt
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory(userHome + File.pathSeparator + ".local" + File.pathSeparator + "/etc"); // $HOME/.local/etc/janf_config.txt
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory("/usr/local/etc"); // /usr/local/etc/janf_config.txt
|
||||
}
|
||||
|
||||
if (null == configFile) {
|
||||
configFile = searchDirectory("/etc"); // /etc/janf_config.txt
|
||||
}
|
||||
|
||||
return configFile;
|
||||
}
|
||||
|
||||
private static File searchDirectory(String dirPath) {
|
||||
return searchDirectory(dirPath, CONFIG_FILENAME);
|
||||
}
|
||||
|
||||
private static File searchDirectory(String dirPath, String filename) {
|
||||
if (StringUtils.isEmpty(dirPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File dirFile = new File(dirPath);
|
||||
if (!dirFile.isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tryFile(new File(dirFile, filename));
|
||||
}
|
||||
|
||||
private static File tryFile(String filePath) {
|
||||
if (StringUtils.isEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tryFile(new File(filePath));
|
||||
}
|
||||
|
||||
private static File tryFile(File file) {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!file.canRead()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package io.zhile.research.ja.netfilter.commons;
|
||||
|
||||
import io.zhile.research.ja.netfilter.models.FilterRule;
|
||||
import io.zhile.research.ja.netfilter.utils.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigParser {
|
||||
public static Map<String, List<FilterRule>> parse(File file) throws Exception {
|
||||
Map<String, List<FilterRule>> map = new HashMap<>();
|
||||
|
||||
if (null == file) {
|
||||
return map;
|
||||
}
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||
int lineNumber = 0;
|
||||
String line, lastSection = null;
|
||||
|
||||
while (null != (line = reader.readLine())) {
|
||||
lineNumber++;
|
||||
line = line.trim();
|
||||
if (StringUtils.isEmpty(line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = line.length();
|
||||
switch (line.charAt(0)) {
|
||||
case '[':
|
||||
if (']' != line.charAt(len - 1)) {
|
||||
throw new Exception("Invalid section! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
String section = line.substring(1, len - 1);
|
||||
if (StringUtils.isEmpty(section)) {
|
||||
throw new Exception("Empty section name! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
lastSection = section;
|
||||
map.put(lastSection, new ArrayList<>());
|
||||
break;
|
||||
case '#':
|
||||
case ';':
|
||||
break; // comment
|
||||
case '/':
|
||||
if (len > 1 && '/' == line.charAt(1)) {
|
||||
break; // comment
|
||||
}
|
||||
throw new Exception("Invalid character! Line: " + lineNumber);
|
||||
default:
|
||||
if (null == lastSection) {
|
||||
break; // ignore rules without section
|
||||
}
|
||||
|
||||
String[] parts = line.split(",", 2);
|
||||
if (2 != parts.length) {
|
||||
throw new Exception("Invalid rule! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
String type = parts[0].trim();
|
||||
String content = parts[1].trim();
|
||||
if (StringUtils.isEmpty(type) || StringUtils.isEmpty(content)) {
|
||||
throw new Exception("Invalid rule! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
if (!Character.isAlphabetic(type.charAt(0))) {
|
||||
throw new Exception("Invalid rule! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
FilterRule rule = FilterRule.of(type, content);
|
||||
if (null == rule) {
|
||||
throw new Exception("Invalid rule type! Line: " + lineNumber);
|
||||
}
|
||||
|
||||
map.get(lastSection).add(rule);
|
||||
DebugInfo.output("Add section: " + lastSection + ", rule: " + rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
@ -1,33 +1,26 @@
|
||||
package io.zhile.research.ja.netfilter.filters;
|
||||
|
||||
import io.zhile.research.ja.netfilter.commons.DebugInfo;
|
||||
import io.zhile.research.ja.netfilter.enums.RuleType;
|
||||
import io.zhile.research.ja.netfilter.models.FilterConfig;
|
||||
import io.zhile.research.ja.netfilter.models.FilterRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DNSFilter {
|
||||
public static final List<FilterRule> RULES;
|
||||
|
||||
static {
|
||||
RULES = new ArrayList<>(); // TODO read from config file
|
||||
RULES.add(new FilterRule(RuleType.REGEXP, ".*?zhile.io"));
|
||||
}
|
||||
private static final String SECTION_NAME = "DNS";
|
||||
|
||||
public static String testQuery(String host) throws IOException {
|
||||
if (null == host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (FilterRule rule : RULES) {
|
||||
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
|
||||
if (!rule.test(host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugInfo.output("=== reject dns query: " + host + ", rule: " + rule);
|
||||
DebugInfo.output("Reject dns query: " + host + ", rule: " + rule);
|
||||
throw new java.net.UnknownHostException();
|
||||
}
|
||||
|
||||
@ -39,12 +32,12 @@ public class DNSFilter {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (FilterRule rule : RULES) {
|
||||
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
|
||||
if (!rule.test(n.getHostName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugInfo.output("=== reject dns reachable test: " + n.getHostName() + ", rule: " + rule);
|
||||
DebugInfo.output("Reject dns reachable test: " + n.getHostName() + ", rule: " + rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,34 +1,27 @@
|
||||
package io.zhile.research.ja.netfilter.filters;
|
||||
|
||||
import io.zhile.research.ja.netfilter.commons.DebugInfo;
|
||||
import io.zhile.research.ja.netfilter.enums.RuleType;
|
||||
import io.zhile.research.ja.netfilter.models.FilterConfig;
|
||||
import io.zhile.research.ja.netfilter.models.FilterRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class URLFilter {
|
||||
public static final List<FilterRule> RULES;
|
||||
|
||||
static {
|
||||
RULES = new ArrayList<>(); // TODO read from config file
|
||||
RULES.add(new FilterRule(RuleType.PREFIX, "https://zhile.io"));
|
||||
}
|
||||
public static final String SECTION_NAME = "URL";
|
||||
|
||||
public static URL testURL(URL url) throws IOException {
|
||||
if (null == url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (FilterRule rule : RULES) {
|
||||
for (FilterRule rule : FilterConfig.getBySection(SECTION_NAME)) {
|
||||
if (!rule.test(url.toString())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugInfo.output("=== reject url: " + url + ", rule: " + rule);
|
||||
DebugInfo.output("Reject url: " + url + ", rule: " + rule);
|
||||
throw new SocketTimeoutException("connect timed out");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,44 @@
|
||||
package io.zhile.research.ja.netfilter.models;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FilterConfig {
|
||||
private static FilterConfig current;
|
||||
|
||||
private final Map<String, List<FilterRule>> data;
|
||||
|
||||
public FilterConfig(Map<String, List<FilterRule>> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static FilterConfig getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public static void setCurrent(FilterConfig current) {
|
||||
FilterConfig.current = current;
|
||||
}
|
||||
|
||||
public static List<FilterRule> getBySection(String section) {
|
||||
do {
|
||||
if (null == current) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (null == current.data) {
|
||||
break;
|
||||
}
|
||||
|
||||
List<FilterRule> list = current.data.get(section);
|
||||
if (null == list) {
|
||||
break;
|
||||
}
|
||||
|
||||
return list;
|
||||
} while (false);
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
@ -2,9 +2,21 @@ package io.zhile.research.ja.netfilter.models;
|
||||
|
||||
import io.zhile.research.ja.netfilter.enums.RuleType;
|
||||
|
||||
public class FilterRule {
|
||||
private RuleType type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FilterRule {
|
||||
private static final Map<String, RuleType> SUPPORTED_TYPE_MAP;
|
||||
|
||||
static {
|
||||
SUPPORTED_TYPE_MAP = new HashMap<>();
|
||||
|
||||
for (RuleType ruleType : RuleType.values()) {
|
||||
SUPPORTED_TYPE_MAP.put(ruleType.name(), ruleType);
|
||||
}
|
||||
}
|
||||
|
||||
private RuleType type;
|
||||
private String rule;
|
||||
|
||||
public FilterRule(RuleType type, String rule) {
|
||||
@ -12,6 +24,15 @@ public class FilterRule {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
public static FilterRule of(String typeStr, String content) {
|
||||
RuleType type = SUPPORTED_TYPE_MAP.get(typeStr.toUpperCase());
|
||||
if (null == type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FilterRule(type, content);
|
||||
}
|
||||
|
||||
public RuleType getType() {
|
||||
return type;
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package io.zhile.research.ja.netfilter.utils;
|
||||
|
||||
public class StringUtils {
|
||||
public static boolean isEmpty(String str) {
|
||||
return null == str || str.isEmpty();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user