diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5c32aa1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.iml
+.idea/
+.DS_Store
+target/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1042685
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# ja-netfilter
+
+### A javaagent lib for network filter
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f5c6388
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ io.zhile.research
+ ja-netfilter
+ 1.0.0
+
+
+ UTF-8
+
+
+
+ ja-netfilter
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+
+ 8
+ UTF-8
+ -XDignore.symbol.file
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.0
+
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.0.0
+
+
+
+ true
+
+
+ io.zhile.research.ja.netfilter.Launcher
+ io.zhile.research.ja.netfilter.Launcher
+ true
+ true
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/src/main/java/io/zhile/research/ja/netfilter/Launcher.java b/src/main/java/io/zhile/research/ja/netfilter/Launcher.java
new file mode 100644
index 0000000..89cfdf3
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/Launcher.java
@@ -0,0 +1,77 @@
+package io.zhile.research.ja.netfilter;
+
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.jar.JarFile;
+
+public class Launcher {
+ public static void main(String[] args) {
+ printUsage();
+ }
+
+ public static void premain(String args, Instrumentation inst) {
+ printUsage();
+
+ URL jarURL = getJarURL();
+ if (null == jarURL) {
+ throw new RuntimeException("Can not locate ja-netfilter jar file.");
+ }
+
+ try {
+ inst.appendToBootstrapClassLoaderSearch(new JarFile(jarURL.getPath()));
+ } catch (Throwable e) {
+ throw new RuntimeException("Can not access ja-netfilter jar file.", e);
+ }
+
+ for (Class> c : inst.getAllLoadedClasses()) {
+ try {
+ inst.retransformClasses(c);
+ } catch (UnmodifiableClassException e) {
+ // ok, ok. just ignore
+ }
+ }
+
+ inst.addTransformer(new TransformDispatcher(), true);
+ }
+
+ private static void printUsage() {
+ String content = "\n ============================================================================ \n" +
+ "\n" +
+ " A javaagent lib for network filter :)\n" +
+ "\n" +
+ " https://github.com/pengzhile/ja-netfilter\n" +
+ "\n" +
+ " ============================================================================ \n\n";
+
+ System.out.print(content);
+ System.out.flush();
+ }
+
+ private static URL getJarURL() {
+ URL url = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
+ if (null != url) {
+ return url;
+ }
+
+ String resourcePath = "/442fcf28466515a81d5434931496ffa64611cc8e.txt";
+ url = Launcher.class.getResource(resourcePath);
+ if (null == url) {
+ return null;
+ }
+
+ String path = url.getPath();
+ if (!path.endsWith("!" + resourcePath)) {
+ return null;
+ }
+
+ path = path.substring(0, path.length() - resourcePath.length() - 1);
+
+ try {
+ return new URL(path);
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/TransformDispatcher.java b/src/main/java/io/zhile/research/ja/netfilter/TransformDispatcher.java
new file mode 100644
index 0000000..d6511e3
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/TransformDispatcher.java
@@ -0,0 +1,42 @@
+package io.zhile.research.ja.netfilter;
+
+import io.zhile.research.ja.netfilter.transformers.HttpClientTransformer;
+import io.zhile.research.ja.netfilter.transformers.InetAddressTransformer;
+import io.zhile.research.ja.netfilter.transformers.MyTransformer;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TransformDispatcher implements ClassFileTransformer {
+ public static final Map TRANSFORMER_MAP;
+
+ static {
+ TRANSFORMER_MAP = new HashMap<>();
+ TRANSFORMER_MAP.put("sun/net/www/http/HttpClient", new HttpClientTransformer());
+ TRANSFORMER_MAP.put("java/net/InetAddress", new InetAddressTransformer());
+ }
+
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) throws IllegalClassFormatException {
+ do {
+ if (null == className) {
+ break;
+ }
+
+ MyTransformer transformer = TRANSFORMER_MAP.get(className);
+ if (null == transformer) {
+ break;
+ }
+
+ try {
+ return transformer.transform(className, classFileBuffer);
+ } catch (Exception e) {
+ System.out.println("=== Transform class failed: " + e.getMessage());
+ }
+ } while (false);
+
+ return classFileBuffer;
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/enums/RuleType.java b/src/main/java/io/zhile/research/ja/netfilter/enums/RuleType.java
new file mode 100644
index 0000000..5ecc055
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/enums/RuleType.java
@@ -0,0 +1,9 @@
+package io.zhile.research.ja.netfilter.enums;
+
+public enum RuleType {
+ PREFIX,
+ SUFFIX,
+ KEYWORD,
+ REGEXP,
+ EQUAL
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java b/src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java
new file mode 100644
index 0000000..88f711c
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/filters/DNSFilter.java
@@ -0,0 +1,58 @@
+package io.zhile.research.ja.netfilter.filters;
+
+import io.zhile.research.ja.netfilter.enums.RuleType;
+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 RULES;
+
+ static {
+ RULES = new ArrayList<>(); // TODO read from config file
+ RULES.add(new FilterRule(RuleType.EQUAL, "zhile.io"));
+ }
+
+ public static String testQuery(String host) throws IOException {
+ if (null == host) {
+ return null;
+ }
+
+ for (FilterRule rule : RULES) {
+ switch (rule.getType()) { // TODO rewrite
+ case EQUAL:
+ if (host.equals(rule.getContent())) {
+ System.out.println("=== reject dns query: " + host);
+ throw new java.net.UnknownHostException();
+ }
+ default: // TODO support more rule types
+ return host;
+ }
+ }
+
+ return host;
+ }
+
+ public static Object testReachable(InetAddress n) throws IOException {
+ if (null == n) {
+ return null;
+ }
+
+ for (FilterRule rule : RULES) {
+ switch (rule.getType()) { // TODO rewrite
+ case EQUAL:
+ if (n.getHostName().equals(rule.getContent())) {
+ System.out.println("=== reject dns reachable test: " + n.getHostName());
+ return false;
+ }
+ default: // TODO support more rule types
+ return null;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java b/src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java
new file mode 100644
index 0000000..e7d1c0a
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/filters/URLFilter.java
@@ -0,0 +1,39 @@
+package io.zhile.research.ja.netfilter.filters;
+
+import io.zhile.research.ja.netfilter.enums.RuleType;
+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 RULES;
+
+ static {
+ RULES = new ArrayList<>(); // TODO read from config file
+ RULES.add(new FilterRule(RuleType.PREFIX, "https://zhile.io"));
+ }
+
+ public static URL testURL(URL url) throws IOException {
+ if (null == url) {
+ return null;
+ }
+
+ for (FilterRule rule : RULES) {
+ switch (rule.getType()) { // TODO rewrite
+ case PREFIX:
+ if (url.toString().startsWith(rule.getContent())) {
+ System.out.println("=== reject url: " + url.toString());
+ throw new SocketTimeoutException("connect timed out");
+ }
+ default: // TODO support more rule types
+ return url;
+ }
+ }
+
+ return url;
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/models/FilterRule.java b/src/main/java/io/zhile/research/ja/netfilter/models/FilterRule.java
new file mode 100644
index 0000000..3c61958
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/models/FilterRule.java
@@ -0,0 +1,30 @@
+package io.zhile.research.ja.netfilter.models;
+
+import io.zhile.research.ja.netfilter.enums.RuleType;
+
+public class FilterRule {
+ private RuleType type;
+
+ private String content;
+
+ public FilterRule(RuleType type, String content) {
+ this.type = type;
+ this.content = content;
+ }
+
+ public RuleType getType() {
+ return type;
+ }
+
+ public void setType(RuleType type) {
+ this.type = type;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java
new file mode 100644
index 0000000..bcd094f
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/transformers/HttpClientTransformer.java
@@ -0,0 +1,33 @@
+package io.zhile.research.ja.netfilter.transformers;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.tree.*;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class HttpClientTransformer implements MyTransformer {
+ @Override
+ public byte[] transform(String className, byte[] classBytes) throws Exception {
+ ClassReader reader = new ClassReader(classBytes);
+ ClassNode node = new ClassNode(ASM5);
+ reader.accept(node, 0);
+
+ for (MethodNode mn : node.methods) {
+ if ("openServer".equals(mn.name) && "()V".equals(mn.desc)) {
+ InsnList list = new InsnList();
+ list.add(new VarInsnNode(ALOAD, 0));
+ list.add(new FieldInsnNode(GETFIELD, "sun/net/www/http/HttpClient", "url", "Ljava/net/URL;"));
+ list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/URLFilter", "testURL", "(Ljava/net/URL;)Ljava/net/URL;", false));
+ list.add(new InsnNode(POP));
+
+ mn.instructions.insert(list);
+ }
+ }
+
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ node.accept(writer);
+
+ return writer.toByteArray();
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java
new file mode 100644
index 0000000..c055eda
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/transformers/InetAddressTransformer.java
@@ -0,0 +1,50 @@
+package io.zhile.research.ja.netfilter.transformers;
+
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.tree.*;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class InetAddressTransformer implements MyTransformer {
+ @Override
+ public byte[] transform(String className, byte[] classBytes) throws Exception {
+ ClassReader reader = new ClassReader(classBytes);
+ ClassNode node = new ClassNode(ASM5);
+ reader.accept(node, 0);
+
+ for (MethodNode m : node.methods) {
+ if ("getAllByName".equals(m.name) && "(Ljava/lang/String;Ljava/net/InetAddress;)[Ljava/net/InetAddress;".equals(m.desc)) {
+ InsnList list = new InsnList();
+ list.add(new VarInsnNode(ALOAD, 0));
+ list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/DNSFilter", "testQuery", "(Ljava/lang/String;)Ljava/lang/String;", false));
+ list.add(new InsnNode(POP));
+
+ m.instructions.insert(list);
+ continue;
+ }
+
+ if ("isReachable".equals(m.name) && "(Ljava/net/NetworkInterface;II)Z".equals(m.desc)) {
+ InsnList list = new InsnList();
+ list.add(new VarInsnNode(ALOAD, 0));
+ list.add(new MethodInsnNode(INVOKESTATIC, "io/zhile/research/ja/netfilter/filters/DNSFilter", "testReachable", "(Ljava/net/InetAddress;)Ljava/lang/Object;", false));
+ list.add(new VarInsnNode(ASTORE, 4));
+ list.add(new InsnNode(ACONST_NULL));
+ list.add(new VarInsnNode(ALOAD, 4));
+
+ LabelNode label1 = new LabelNode();
+ list.add(new JumpInsnNode(IF_ACMPEQ, label1));
+ list.add(new InsnNode(ICONST_0));
+ list.add(new InsnNode(IRETURN));
+ list.add(label1);
+
+ m.instructions.insert(list);
+ }
+ }
+
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ node.accept(writer);
+
+ return writer.toByteArray();
+ }
+}
diff --git a/src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java b/src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java
new file mode 100644
index 0000000..1543cb1
--- /dev/null
+++ b/src/main/java/io/zhile/research/ja/netfilter/transformers/MyTransformer.java
@@ -0,0 +1,5 @@
+package io.zhile.research.ja.netfilter.transformers;
+
+public interface MyTransformer {
+ byte[] transform(String className, byte[] classBytes) throws Exception;
+}
diff --git a/src/main/resources/442fcf28466515a81d5434931496ffa64611cc8e.txt b/src/main/resources/442fcf28466515a81d5434931496ffa64611cc8e.txt
new file mode 100644
index 0000000..e69de29