Commit 5ca1e2e4 authored by Mygod's avatar Mygod

Use re2

parent 89988ec3
...@@ -30,3 +30,6 @@ ...@@ -30,3 +30,6 @@
[submodule "core/src/main/jni/libev"] [submodule "core/src/main/jni/libev"]
path = core/src/main/jni/libev path = core/src/main/jni/libev
url = https://git.lighttpd.net/libev.git url = https://git.lighttpd.net/libev.git
[submodule "core/src/main/jni/re2"]
path = core/src/main/jni/re2
url = https://github.com/google/re2.git
...@@ -22,15 +22,37 @@ package com.github.shadowsocks.acl ...@@ -22,15 +22,37 @@ package com.github.shadowsocks.acl
import android.util.Log import android.util.Log
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import java.lang.IllegalArgumentException
import java.net.Inet4Address import java.net.Inet4Address
import java.net.Inet6Address import java.net.Inet6Address
import kotlin.system.measureNanoTime import kotlin.system.measureNanoTime
class AclMatcher { class AclMatcher : AutoCloseable {
companion object {
init {
System.loadLibrary("jni-helper")
}
@JvmStatic private external fun init(): Long
@JvmStatic private external fun close(handle: Long)
@JvmStatic private external fun addBypassDomain(handle: Long, regex: String): String?
@JvmStatic private external fun addProxyDomain(handle: Long, regex: String): String?
@JvmStatic private external fun matchHost(handle: Long, host: String): Int
}
class Re2Exception(message: String) : IllegalArgumentException(message)
private var handle = 0L
override fun close() {
if (handle != 0L) {
close(handle)
handle = 0L
}
}
private var subnetsIpv4 = emptyList<Subnet.Immutable>() private var subnetsIpv4 = emptyList<Subnet.Immutable>()
private var subnetsIpv6 = emptyList<Subnet.Immutable>() private var subnetsIpv6 = emptyList<Subnet.Immutable>()
private val bypassDomains = mutableListOf<Regex>()
private val proxyDomains = mutableListOf<Regex>()
private var bypass = false private var bypass = false
suspend fun init(id: String) { suspend fun init(id: String) {
...@@ -44,11 +66,14 @@ class AclMatcher { ...@@ -44,11 +66,14 @@ class AclMatcher {
current = next current = next
} }
}.toList() }.toList()
fun String.fail(): Nothing = throw Re2Exception(this)
check(handle == 0L)
handle = init()
val time = measureNanoTime { val time = measureNanoTime {
val (bypass, subnets) = Acl.parse(Acl.getFile(id).bufferedReader(), { val (bypass, subnets) = Acl.parse(Acl.getFile(id).bufferedReader(), {
// bypassDomains.add(it.toRegex()) addBypassDomain(handle, it)?.fail()
}, { }, {
if (it.startsWith("(?:^|\\.)googleapis")) proxyDomains.add(it.toRegex()) addProxyDomain(handle, it)?.fail()
}) })
subnetsIpv4 = subnets.asSequence().filter { it.address is Inet4Address }.dedup() subnetsIpv4 = subnets.asSequence().filter { it.address is Inet4Address }.dedup()
subnetsIpv6 = subnets.asSequence().filter { it.address is Inet6Address }.dedup() subnetsIpv6 = subnets.asSequence().filter { it.address is Inet6Address }.dedup()
...@@ -64,9 +89,10 @@ class AclMatcher { ...@@ -64,9 +89,10 @@ class AclMatcher {
fun shouldBypassIpv4(ip: ByteArray) = bypass xor quickMatches(subnetsIpv4, ip) fun shouldBypassIpv4(ip: ByteArray) = bypass xor quickMatches(subnetsIpv4, ip)
fun shouldBypassIpv6(ip: ByteArray) = bypass xor quickMatches(subnetsIpv6, ip) fun shouldBypassIpv6(ip: ByteArray) = bypass xor quickMatches(subnetsIpv6, ip)
fun shouldBypass(host: String): Boolean? { fun shouldBypass(host: String) = when (val e = matchHost(handle, host)) {
if (bypassDomains.any { it.matches(host) }) return true 0 -> null
if (proxyDomains.any { it.matches(host) }) return false 1 -> true
return null 2 -> false
else -> error("matchHost -> $e")
} }
} }
...@@ -278,6 +278,39 @@ LOCAL_LDLIBS := -llog ...@@ -278,6 +278,39 @@ LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_EXECUTABLE) include $(BUILD_SHARED_EXECUTABLE)
########################################################
## jni-helper
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE:= jni-helper
LOCAL_C_INCLUDES := $(LOCAL_PATH)/re2
LOCAL_CFLAGS := -std=c++17
LOCAL_SRC_FILES := jni-helper.cpp \
$(LOCAL_PATH)/re2/re2/bitstate.cc \
$(LOCAL_PATH)/re2/re2/compile.cc \
$(LOCAL_PATH)/re2/re2/dfa.cc \
$(LOCAL_PATH)/re2/re2/nfa.cc \
$(LOCAL_PATH)/re2/re2/onepass.cc \
$(LOCAL_PATH)/re2/re2/parse.cc \
$(LOCAL_PATH)/re2/re2/perl_groups.cc \
$(LOCAL_PATH)/re2/re2/prog.cc \
$(LOCAL_PATH)/re2/re2/re2.cc \
$(LOCAL_PATH)/re2/re2/regexp.cc \
$(LOCAL_PATH)/re2/re2/simplify.cc \
$(LOCAL_PATH)/re2/re2/stringpiece.cc \
$(LOCAL_PATH)/re2/re2/tostring.cc \
$(LOCAL_PATH)/re2/re2/unicode_casefold.cc \
$(LOCAL_PATH)/re2/re2/unicode_groups.cc \
$(LOCAL_PATH)/re2/util/rune.cc \
$(LOCAL_PATH)/re2/util/strutil.cc
include $(BUILD_SHARED_LIBRARY)
######################################################## ########################################################
## tun2socks ## tun2socks
######################################################## ########################################################
......
#include <utility>
#include <vector>
#include "jni.h"
#include "re2/re2.h"
using namespace std;
struct AclMatcher {
vector<RE2 *> bypassDomains, proxyDomains;
~AclMatcher() {
for (RE2 *re2 : bypassDomains) delete re2;
for (RE2 *re2 : proxyDomains) delete re2;
}
};
jstring addDomain(JNIEnv *env, vector<RE2 *> &domains, jstring regex) {
const char *regexChars = env->GetStringUTFChars(regex, nullptr);
RE2 *re2 = new RE2(regexChars);
env->ReleaseStringUTFChars(regex, regexChars);
if (re2->ok()) {
domains.push_back(re2);
return nullptr;
} else {
auto result = env->NewStringUTF(re2->error().c_str());
delete re2;
return result;
}
}
bool matches(const vector<RE2 *> &domains, const char *host) {
return any_of(domains.cbegin(), domains.cend(), [host](const RE2 *re){
return RE2::FullMatch(host, *re);
});
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
extern "C" {
JNIEXPORT jlong JNICALL Java_com_github_shadowsocks_acl_AclMatcher_init(JNIEnv *env, jclass clazz) {
return reinterpret_cast<jlong>(new AclMatcher());
}
JNIEXPORT void JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_close(JNIEnv *env, jclass clazz, jlong handle) {
delete reinterpret_cast<AclMatcher *>(handle);
}
JNIEXPORT jstring JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_addBypassDomain(JNIEnv *env, jclass clazz, jlong handle,
jstring regex) {
if (!handle) return env->NewStringUTF("AclMatcher closed");
return ::addDomain(env, reinterpret_cast<AclMatcher *>(handle)->bypassDomains, regex);
}
JNIEXPORT jstring JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_addProxyDomain(JNIEnv *env, jclass clazz, jlong handle,
jstring regex) {
if (!handle) return env->NewStringUTF("AclMatcher closed");
return ::addDomain(env, reinterpret_cast<AclMatcher *>(handle)->proxyDomains, regex);
}
JNIEXPORT jint JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_matchHost(JNIEnv *env, jclass clazz, jlong handle,
jstring host) {
if (!handle) return -1;
auto matcher = reinterpret_cast<const AclMatcher *>(handle);
const char *hostChars = env->GetStringUTFChars(host, nullptr);
jint result = 0;
if (::matches(matcher->bypassDomains, hostChars)) result = 1;
else if (::matches(matcher->proxyDomains, hostChars)) result = 2;
env->ReleaseStringUTFChars(host, hostChars);
return result;
}
}
Subproject commit bb8e777557ddbdeabdedea4f23613c5021ffd7b1
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment