diff --git a/pom.xml b/pom.xml
index 857ed68..c65d27c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,12 @@
slf4j-simple
${slf4j-version}
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.10.2
+ test
+
@@ -42,6 +48,11 @@
21
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.2
+
org.apache.maven.plugins
maven-assembly-plugin
diff --git a/src/main/java/io/github/danthe1st/httpsintercept/control/HostMatcher.java b/src/main/java/io/github/danthe1st/httpsintercept/control/HostMatcher.java
new file mode 100644
index 0000000..9f10420
--- /dev/null
+++ b/src/main/java/io/github/danthe1st/httpsintercept/control/HostMatcher.java
@@ -0,0 +1,68 @@
+package io.github.danthe1st.httpsintercept.control;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class HostMatcher {
+ private Set exactHosts = new HashSet<>();
+ private Set hostParts = new HashSet<>();
+
+ public static HostMatcher load(Path config) throws IOException {
+ if(!Files.exists(config)){
+ Files.createFile(config);
+ return load(Collections.emptyList());
+ }
+ List hostDeclarations = Files.readAllLines(config);
+ return load(hostDeclarations);
+ }
+
+ static HostMatcher load(List hostDeclarations) {
+ Set exactHosts = new HashSet<>();
+ Set hostParts = new HashSet<>();
+ for(String ignored : hostDeclarations){
+ if(ignored.startsWith("*.")){
+ hostParts.add(ignored.substring(2));
+ }else{
+ exactHosts.add(ignored);
+ }
+ }
+ return new HostMatcher(exactHosts, hostParts);
+ }
+
+ HostMatcher(Set exactHosts, Set hostParts) {
+ this.exactHosts = Set.copyOf(exactHosts);
+ this.hostParts = Set.copyOf(hostParts);
+ }
+
+ public boolean matches(String hostname) {
+ return exactHosts.contains(hostname) || doesMatchPart(hostname);
+ }
+
+ private boolean doesMatchPart(String hostname) {
+ if(hostParts.isEmpty()){
+ return false;
+ }
+
+ int index = 0;
+ while((index = hostname.indexOf('.', index) + 1) != 0 && index < hostname.length()){
+ if(hostParts.contains(hostname.substring(index))){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ Set getExactHosts() {
+ return Collections.unmodifiableSet(exactHosts);
+ }
+
+ Set getHostParts() {
+ return Collections.unmodifiableSet(hostParts);
+ }
+}
diff --git a/src/main/java/io/github/danthe1st/httpsintercept/handler/sni/CustomSniHandler.java b/src/main/java/io/github/danthe1st/httpsintercept/handler/sni/CustomSniHandler.java
index 2d6e25e..f4f712f 100644
--- a/src/main/java/io/github/danthe1st/httpsintercept/handler/sni/CustomSniHandler.java
+++ b/src/main/java/io/github/danthe1st/httpsintercept/handler/sni/CustomSniHandler.java
@@ -10,6 +10,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import io.github.danthe1st.httpsintercept.control.HostMatcher;
import io.github.danthe1st.httpsintercept.handler.raw.RawForwardIncomingRequestHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandler;
@@ -27,13 +28,13 @@ public class CustomSniHandler extends SniHandler {
private final Bootstrap clientBootstrapTemplate;
- private final Set ignoredHosts;
+ private final HostMatcher ignoredHosts;
public CustomSniHandler(Mapping super String, ? extends SslContext> mapping, Bootstrap clientBootstrapTemplate) throws IOException {
super(mapping);
this.clientBootstrapTemplate = clientBootstrapTemplate;
- ignoredHosts = loadIgnoredHosts();
+ ignoredHosts = HostMatcher.load(Path.of("ignoredHosts.txt"));
}
private static Set loadIgnoredHosts() throws IOException {
@@ -50,8 +51,8 @@ private static Set loadIgnoredHosts() throws IOException {
@Override
protected void replaceHandler(ChannelHandlerContext channelHandlerContext, String hostname, SslContext sslContext) throws Exception {
ChannelPipeline pipeline = channelHandlerContext.pipeline();
- if(ignoredHosts.contains(hostname)){
- LOG.debug("skipping hostname {}", hostname);
+ if(ignoredHosts.matches(hostname)){
+ LOG.info("skipping hostname {}", hostname);
boolean foundThis = false;
diff --git a/src/test/java/io/github/danthe1st/httpsintercept/control/HostMatcherTests.java b/src/test/java/io/github/danthe1st/httpsintercept/control/HostMatcherTests.java
new file mode 100644
index 0000000..d23e07c
--- /dev/null
+++ b/src/test/java/io/github/danthe1st/httpsintercept/control/HostMatcherTests.java
@@ -0,0 +1,59 @@
+package io.github.danthe1st.httpsintercept.control;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+class HostMatcherTests {
+ @Test
+ void testExactMatch() {
+ HostMatcher matcher = new HostMatcher(Set.of("example.com"), Collections.emptySet());
+ assertTrue(matcher.matches("example.com"));
+ assertFalse(matcher.matches("github.com"));
+ }
+
+ @Test
+ void testPartMatch() {
+ HostMatcher matcher = new HostMatcher(Collections.emptySet(), Set.of("example.com"));
+ assertTrue(matcher.matches("host.example.com"));
+ assertTrue(matcher.matches(".example.com"));
+ assertFalse(matcher.matches("host.github.com"));
+ assertFalse(matcher.matches("example.com"));
+ assertFalse(matcher.matches("example.com."));
+ assertFalse(matcher.matches(""));
+ assertFalse(matcher.matches("."));
+ }
+
+ @Test
+ void testLoadExact() {
+ HostMatcher hostMatcher = HostMatcher.load(List.of("example.com"));
+ assertEquals(Set.of("example.com"), hostMatcher.getExactHosts());
+ assertEquals(Collections.emptySet(), hostMatcher.getHostParts());
+ }
+
+ @Test
+ void testLoadParts() {
+ HostMatcher hostMatcher = HostMatcher.load(List.of("*.example.com"));
+ assertEquals(Collections.emptySet(), hostMatcher.getExactHosts());
+ assertEquals(Set.of("example.com"), hostMatcher.getHostParts());
+
+ assertTrue(hostMatcher.matches("host.example.com"));
+ }
+
+ @Test
+ void testLoadEmptyPart() {
+ HostMatcher hostMatcher = HostMatcher.load(List.of("*."));
+ assertEquals(Collections.emptySet(), hostMatcher.getExactHosts());
+ assertEquals(Set.of(""), hostMatcher.getHostParts());
+
+ assertFalse(hostMatcher.matches("something"));
+ assertFalse(hostMatcher.matches("example.com"));
+ }
+
+}