mirror of
https://github.com/MetaCubeX/ClashMetaForAndroid.git
synced 2025-02-21 09:13:16 +03:00
Improve: use lwip stack instead of kernel
This commit is contained in:
parent
eff310ac40
commit
7381b787f3
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -4,3 +4,6 @@
|
|||||||
[submodule "kaidl"]
|
[submodule "kaidl"]
|
||||||
path = kaidl
|
path = kaidl
|
||||||
url = https://github.com/Kr328/kaidl.git
|
url = https://github.com/Kr328/kaidl.git
|
||||||
|
[submodule "core/src/main/golang/tun2socket"]
|
||||||
|
path = core/src/main/golang/tun2socket
|
||||||
|
url = https://github.com/Kr328/tun2socket-lwip.git
|
||||||
|
@ -9,6 +9,7 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class GolangBuildTask : DefaultTask() {
|
abstract class GolangBuildTask : DefaultTask() {
|
||||||
abstract val debug: Property<Boolean>
|
abstract val debug: Property<Boolean>
|
||||||
@ -23,7 +24,10 @@ abstract class GolangBuildTask : DefaultTask() {
|
|||||||
abstract val minSdkVersion: Property<Int>
|
abstract val minSdkVersion: Property<Int>
|
||||||
@Input get
|
@Input get
|
||||||
|
|
||||||
abstract val cCompilerBasePath: DirectoryProperty
|
abstract val ndkDirectory: DirectoryProperty
|
||||||
|
@InputDirectory get
|
||||||
|
|
||||||
|
abstract val cmakeDirectory: DirectoryProperty
|
||||||
@InputDirectory get
|
@InputDirectory get
|
||||||
|
|
||||||
abstract val inputDirectory: DirectoryProperty
|
abstract val inputDirectory: DirectoryProperty
|
||||||
@ -36,14 +40,12 @@ abstract class GolangBuildTask : DefaultTask() {
|
|||||||
fun build() {
|
fun build() {
|
||||||
val src = inputDirectory.get().asFile
|
val src = inputDirectory.get().asFile
|
||||||
|
|
||||||
val cmd = if (debug.get()) {
|
val generateCmd = """go run make/make.go bridge native build android %s"""
|
||||||
"""
|
|
||||||
go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system,debug${if (premium.get()) ",premium" else ""}"
|
val buildCmd = if (debug.get()) {
|
||||||
""".trimIndent().trim()
|
"""go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system,debug${if (premium.get()) ",premium" else ""}"""
|
||||||
} else {
|
} else {
|
||||||
"""
|
"""go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system${if (premium.get()) ",premium" else ""}" -ldflags "-s -w""""
|
||||||
go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system${if (premium.get()) ",premium" else ""}" -ldflags "-s -w"
|
|
||||||
""".trimIndent().trim()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"go mod tidy".exec(pwd = src)
|
"go mod tidy".exec(pwd = src)
|
||||||
@ -51,10 +53,23 @@ abstract class GolangBuildTask : DefaultTask() {
|
|||||||
nativeAbis.get().parallelStream().forEach {
|
nativeAbis.get().parallelStream().forEach {
|
||||||
val out = outputDirectory.get().file("$it/libclash.so")
|
val out = outputDirectory.get().file("$it/libclash.so")
|
||||||
|
|
||||||
cmd.format(out).exec(pwd = src, env = generateGolangBuildEnvironment(it))
|
generateCmd.format(it.toGoArch()).exec(pwd = src.resolve("tun2socket/bridge"), env = generateGolangGenerateEnvironment(it))
|
||||||
|
buildCmd.format(out).exec(pwd = src, env = generateGolangBuildEnvironment(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateGolangGenerateEnvironment(abi: String): Map<String, String> {
|
||||||
|
val path = cmakeDirectory.get().asFile.absolutePath + File.pathSeparator + System.getenv("PATH")
|
||||||
|
|
||||||
|
return mapOf(
|
||||||
|
"PATH" to path,
|
||||||
|
"CMAKE_SYSTEM_NAME" to "Android",
|
||||||
|
"CMAKE_ANDROID_NDK" to ndkDirectory.get().asFile.absolutePath,
|
||||||
|
"CMAKE_ANDROID_ARCH_ABI" to abi,
|
||||||
|
"CMAKE_SYSTEM_VERSION" to minSdkVersion.get().toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateGolangBuildEnvironment(abi: String): Map<String, String> {
|
private fun generateGolangBuildEnvironment(abi: String): Map<String, String> {
|
||||||
val (goArch, goArm) = when (abi) {
|
val (goArch, goArm) = when (abi) {
|
||||||
"arm64-v8a" -> "arm64" to ""
|
"arm64-v8a" -> "arm64" to ""
|
||||||
@ -81,15 +96,27 @@ abstract class GolangBuildTask : DefaultTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mapOf(
|
return mapOf(
|
||||||
"CC" to cCompilerBasePath.get().asFile.resolve(compiler).absolutePath,
|
"CC" to compilerBasePath.resolve(compiler).absolutePath,
|
||||||
"GOOS" to "android",
|
"GOOS" to "android",
|
||||||
"GOARCH" to goArch,
|
"GOARCH" to goArch,
|
||||||
"GOARM" to goArm,
|
"GOARM" to goArm,
|
||||||
"CGO_ENABLED" to "1",
|
"CGO_ENABLED" to "1",
|
||||||
"CFLAGS" to "-O3 -Werror",
|
"CFLAGS" to "-O3 -Werror",
|
||||||
|
"CMAKE_ARGS" to "-DCMAKE_TOOLCHAIN_FILE=${ndkDirectory.get().asFile.absolutePath}/build/cmake/android.toolchain.cmake -DANDROID_ABI=$abi -DANDROID_PLATFORM=android-${minSdkVersion.get()} -DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"PATH" to cmakeDirectory.get().asFile.absolutePath + File.pathSeparator + System.getenv("PATH")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String.toGoArch(): String {
|
||||||
|
return when (this) {
|
||||||
|
"arm64-v8a" -> "arm64"
|
||||||
|
"armeabi-v7a" -> "arm"
|
||||||
|
"x86" -> "386"
|
||||||
|
"x86_64" -> "amd64"
|
||||||
|
else -> throw UnsupportedOperationException("unsupported abi: $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.exec(
|
private fun String.exec(
|
||||||
pwd: File,
|
pwd: File,
|
||||||
env: Map<String, String> = System.getenv()
|
env: Map<String, String> = System.getenv()
|
||||||
@ -118,4 +145,23 @@ abstract class GolangBuildTask : DefaultTask() {
|
|||||||
|
|
||||||
return outputStream.toString("utf-8")
|
return outputStream.toString("utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val compilerBasePath: File
|
||||||
|
get() {
|
||||||
|
val host = when {
|
||||||
|
Os.isFamily(Os.FAMILY_WINDOWS) ->
|
||||||
|
"windows"
|
||||||
|
Os.isFamily(Os.FAMILY_MAC) ->
|
||||||
|
"darwin"
|
||||||
|
Os.isFamily(Os.FAMILY_UNIX) ->
|
||||||
|
"linux"
|
||||||
|
else ->
|
||||||
|
throw GradleScriptException(
|
||||||
|
"Unsupported host",
|
||||||
|
FileNotFoundException("Unsupported host")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ndkDirectory.get().asFile.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,6 +11,11 @@ class LibraryGolangPlugin : Plugin<Project> {
|
|||||||
override fun apply(target: Project) {
|
override fun apply(target: Project) {
|
||||||
target.extensions.getByType(LibraryExtension::class.java).apply {
|
target.extensions.getByType(LibraryExtension::class.java).apply {
|
||||||
target.afterEvaluate {
|
target.afterEvaluate {
|
||||||
|
val properties = Properties().apply {
|
||||||
|
target.rootProject.file("local.properties").inputStream().use(this::load)
|
||||||
|
}
|
||||||
|
val cmakeDirectory = target.rootProject.file(properties.getProperty("cmake.dir")!!)
|
||||||
|
|
||||||
libraryVariants.forEach { variant ->
|
libraryVariants.forEach { variant ->
|
||||||
val abis = defaultConfig.externalNativeBuild.cmake.abiFilters +
|
val abis = defaultConfig.externalNativeBuild.cmake.abiFilters +
|
||||||
defaultConfig.externalNativeBuild.ndkBuild.abiFilters
|
defaultConfig.externalNativeBuild.ndkBuild.abiFilters
|
||||||
@ -26,7 +31,8 @@ class LibraryGolangPlugin : Plugin<Project> {
|
|||||||
it.debug.set(variant.name == "debug")
|
it.debug.set(variant.name == "debug")
|
||||||
it.nativeAbis.set(abis)
|
it.nativeAbis.set(abis)
|
||||||
it.minSdkVersion.set(defaultConfig.minSdk!!)
|
it.minSdkVersion.set(defaultConfig.minSdk!!)
|
||||||
it.cCompilerBasePath.set(compilerBasePath)
|
it.ndkDirectory.set(ndkDirectory)
|
||||||
|
it.cmakeDirectory.set(cmakeDirectory)
|
||||||
it.inputDirectory.set(target.golangSource)
|
it.inputDirectory.set(target.golangSource)
|
||||||
it.outputDirectory.set(golangBuildDir)
|
it.outputDirectory.set(golangBuildDir)
|
||||||
}
|
}
|
||||||
@ -47,23 +53,4 @@ class LibraryGolangPlugin : Plugin<Project> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val LibraryExtension.compilerBasePath: File
|
|
||||||
get() {
|
|
||||||
val host = when {
|
|
||||||
Os.isFamily(Os.FAMILY_WINDOWS) ->
|
|
||||||
"windows"
|
|
||||||
Os.isFamily(Os.FAMILY_MAC) ->
|
|
||||||
"darwin"
|
|
||||||
Os.isFamily(Os.FAMILY_UNIX) ->
|
|
||||||
"linux"
|
|
||||||
else ->
|
|
||||||
throw GradleScriptException(
|
|
||||||
"Unsupported host",
|
|
||||||
FileNotFoundException("Unsupported host")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ndkDirectory.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -97,17 +97,14 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(J
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz, jint fd,
|
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz, jint fd,
|
||||||
jint mtu, jstring gateway,
|
jint mtu, jstring dns,
|
||||||
jstring mirror, jstring dns,
|
|
||||||
jobject cb) {
|
jobject cb) {
|
||||||
TRACE_METHOD();
|
TRACE_METHOD();
|
||||||
|
|
||||||
scoped_string _gateway = get_string(gateway);
|
|
||||||
scoped_string _mirror = get_string(mirror);
|
|
||||||
scoped_string _dns = get_string(dns);
|
scoped_string _dns = get_string(dns);
|
||||||
jobject _interface = new_global(cb);
|
jobject _interface = new_global(cb);
|
||||||
|
|
||||||
startTun(fd, mtu, _gateway, _mirror, _dns, _interface);
|
startTun(fd, mtu, _dns, _interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
|
@ -5,8 +5,8 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
cfa/blob v0.0.0 // local generated
|
cfa/blob v0.0.0 // local generated
|
||||||
github.com/Dreamacro/clash v0.0.0 // local
|
github.com/Dreamacro/clash v0.0.0 // local
|
||||||
|
github.com/kr328/tun2socket v0.0.0 // local
|
||||||
github.com/dlclark/regexp2 v1.4.0
|
github.com/dlclark/regexp2 v1.4.0
|
||||||
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99
|
|
||||||
github.com/miekg/dns v1.1.42
|
github.com/miekg/dns v1.1.42
|
||||||
github.com/oschwald/geoip2-golang v1.5.0
|
github.com/oschwald/geoip2-golang v1.5.0
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
@ -15,4 +15,6 @@ require (
|
|||||||
|
|
||||||
replace github.com/Dreamacro/clash => ./clash
|
replace github.com/Dreamacro/clash => ./clash
|
||||||
|
|
||||||
|
replace github.com/kr328/tun2socket => ./tun2socket
|
||||||
|
|
||||||
replace cfa/blob => ../../../build/intermediates/golang_blob
|
replace cfa/blob => ../../../build/intermediates/golang_blob
|
||||||
|
@ -56,17 +56,14 @@ func (t *remoteTun) stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export startTun
|
//export startTun
|
||||||
func startTun(fd, mtu C.int, gateway, mirror, dns C.c_string, callback unsafe.Pointer) C.int {
|
func startTun(fd, mtu C.int, dns C.c_string, callback unsafe.Pointer) C.int {
|
||||||
f := int(fd)
|
f := int(fd)
|
||||||
m := int(mtu)
|
m := int(mtu)
|
||||||
|
|
||||||
g := C.GoString(gateway)
|
|
||||||
mr := C.GoString(mirror)
|
|
||||||
d := C.GoString(dns)
|
d := C.GoString(dns)
|
||||||
|
|
||||||
remote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}
|
remote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}
|
||||||
|
|
||||||
if tun.Start(f, m, g, mr, d, remote.stop) != nil {
|
if tun.Start(f, m, d) != nil {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,24 +7,22 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
|
"github.com/kr328/tun2socket/bridge"
|
||||||
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/kr328/tun2socket/binding"
|
|
||||||
"github.com/kr328/tun2socket/redirect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultDnsReadTimeout = time.Second * 30
|
const defaultDnsReadTimeout = time.Second * 30
|
||||||
|
|
||||||
func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool {
|
func shouldHijackDns(dns net.IP, target net.IP, targetPort int) bool {
|
||||||
if targetAddr.Port != 53 {
|
if targetPort != 53 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP)
|
return net.IPv4zero.Equal(dns) || target.Equal(dns)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
|
func hijackUDPDns(pkt []byte, lAddr, rAddr net.Addr, udp bridge.UDP) {
|
||||||
go func() {
|
go func() {
|
||||||
answer, err := relayDnsPacket(pkt)
|
answer, err := relayDnsPacket(pkt)
|
||||||
|
|
||||||
@ -32,10 +30,9 @@ func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = sender(answer, &binding.Endpoint{
|
_, _ = udp.WriteTo(answer, lAddr, rAddr)
|
||||||
Source: ep.Target,
|
|
||||||
Target: ep.Source,
|
recycleUDP(pkt)
|
||||||
})
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,37 +4,24 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/kr328/tun2socket/binding"
|
|
||||||
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/context"
|
CTX "github.com/Dreamacro/clash/context"
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleTCP(conn net.Conn, endpoint *binding.Endpoint) {
|
func handleTCP(conn net.Conn, source *net.TCPAddr, target *net.TCPAddr) {
|
||||||
src := &net.TCPAddr{
|
|
||||||
IP: endpoint.Source.IP,
|
|
||||||
Port: int(endpoint.Source.Port),
|
|
||||||
Zone: "",
|
|
||||||
}
|
|
||||||
dst := &net.TCPAddr{
|
|
||||||
IP: endpoint.Target.IP,
|
|
||||||
Port: int(endpoint.Target.Port),
|
|
||||||
Zone: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata := &C.Metadata{
|
metadata := &C.Metadata{
|
||||||
NetWork: C.TCP,
|
NetWork: C.TCP,
|
||||||
Type: C.SOCKS,
|
Type: C.SOCKS,
|
||||||
SrcIP: src.IP,
|
SrcIP: source.IP,
|
||||||
DstIP: dst.IP,
|
DstIP: target.IP,
|
||||||
SrcPort: strconv.Itoa(src.Port),
|
SrcPort: strconv.Itoa(source.Port),
|
||||||
DstPort: strconv.Itoa(dst.Port),
|
DstPort: strconv.Itoa(target.Port),
|
||||||
AddrType: C.AtypIPv4,
|
AddrType: C.AtypIPv4,
|
||||||
Host: "",
|
Host: "",
|
||||||
RawSrcAddr: src,
|
RawSrcAddr: source,
|
||||||
RawDstAddr: dst,
|
RawDstAddr: target,
|
||||||
}
|
}
|
||||||
|
|
||||||
tunnel.Add(context.NewConnContext(conn, metadata))
|
tunnel.Add(CTX.NewConnContext(conn, metadata))
|
||||||
}
|
}
|
||||||
|
@ -3,67 +3,126 @@ package tun
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/kr328/tun2socket/binding"
|
|
||||||
"github.com/kr328/tun2socket/redirect"
|
|
||||||
|
|
||||||
"github.com/kr328/tun2socket"
|
"github.com/kr328/tun2socket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var lock sync.Mutex
|
type context struct {
|
||||||
var tun *tun2socket.Tun2Socket
|
stack tun2socket.Stack
|
||||||
|
device *os.File
|
||||||
|
}
|
||||||
|
|
||||||
func Start(fd, mtu int, gateway, mirror, dns string, onStop func()) error {
|
var lock sync.Mutex
|
||||||
|
var tun *context
|
||||||
|
|
||||||
|
func Start(fd, mtu int, dns string) error {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
|
||||||
stopLocked()
|
stopLocked()
|
||||||
|
|
||||||
dnsHost, dnsPort, err := net.SplitHostPort(dns)
|
dnsIP := net.ParseIP(dns)
|
||||||
|
|
||||||
|
device := os.NewFile(uintptr(fd), "/dev/tun")
|
||||||
|
|
||||||
|
stack, err := tun2socket.NewStack(mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = device.Close()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsP, err := strconv.Atoi(dnsPort)
|
go func() {
|
||||||
if err != nil {
|
// device -> lwip
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsAddr := binding.Address{
|
defer device.Close()
|
||||||
IP: net.ParseIP(dnsHost),
|
defer stack.Close()
|
||||||
Port: uint16(dnsP),
|
|
||||||
}
|
|
||||||
|
|
||||||
t := tun2socket.NewTun2Socket(os.NewFile(uintptr(fd), "/dev/tun"), mtu, net.ParseIP(gateway), net.ParseIP(mirror))
|
buf := make([]byte, mtu)
|
||||||
|
|
||||||
t.SetAllocator(allocUDP)
|
for {
|
||||||
t.SetClosedHandler(onStop)
|
n, err := device.Read(buf)
|
||||||
t.SetLogger(&logger{})
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
|
_, _ = stack.Link().Write(buf[:n])
|
||||||
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
|
||||||
hijackTCPDns(conn)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
handleTCP(conn, endpoint)
|
go func() {
|
||||||
})
|
// lwip -> device
|
||||||
t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
|
||||||
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
|
||||||
hijackUDPDns(payload, endpoint, sender)
|
|
||||||
|
|
||||||
return
|
defer device.Close()
|
||||||
|
defer stack.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, mtu)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := stack.Link().Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = device.Write(buf[:n])
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
handleUDP(payload, endpoint, sender)
|
go func() {
|
||||||
})
|
// lwip tcp
|
||||||
|
|
||||||
t.Start()
|
for {
|
||||||
|
conn, err := stack.TCP().Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tun = t
|
source := conn.LocalAddr().(*net.TCPAddr)
|
||||||
|
target := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||||
|
hijackTCPDns(conn)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTCP(conn, source, target)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// lwip udp
|
||||||
|
|
||||||
|
for {
|
||||||
|
buf := allocUDP(mtu)
|
||||||
|
|
||||||
|
n, lAddr, rAddr, err := stack.UDP().ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
source := lAddr.(*net.UDPAddr)
|
||||||
|
target := rAddr.(*net.UDPAddr)
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||||
|
hijackUDPDns(buf[:n], source, target, stack.UDP())
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUDP(buf[:n], source, target, stack.UDP())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
tun = &context{
|
||||||
|
stack: stack,
|
||||||
|
device: device,
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -77,7 +136,8 @@ func Stop() {
|
|||||||
|
|
||||||
func stopLocked() {
|
func stopLocked() {
|
||||||
if tun != nil {
|
if tun != nil {
|
||||||
tun.Close()
|
tun.device.Close()
|
||||||
|
tun.stack.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
tun = nil
|
tun = nil
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
"github.com/Dreamacro/clash/transport/socks5"
|
||||||
"github.com/kr328/tun2socket/binding"
|
"github.com/kr328/tun2socket/bridge"
|
||||||
"github.com/kr328/tun2socket/redirect"
|
|
||||||
|
|
||||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
@ -15,10 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type udpPacket struct {
|
type udpPacket struct {
|
||||||
metadata *C.Metadata
|
source *net.UDPAddr
|
||||||
source binding.Address
|
|
||||||
data []byte
|
data []byte
|
||||||
send redirect.UDPSender
|
udp bridge.UDP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *udpPacket) Data() []byte {
|
func (u *udpPacket) Data() []byte {
|
||||||
@ -26,15 +23,7 @@ func (u *udpPacket) Data() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||||
uAddr, ok := addr.(*net.UDPAddr)
|
return u.udp.WriteTo(b, u.source, addr)
|
||||||
if !ok {
|
|
||||||
return 0, io.ErrClosedPipe
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(b), u.send(b, &binding.Endpoint{
|
|
||||||
Source: binding.Address{IP: uAddr.IP, Port: uint16(uAddr.Port)},
|
|
||||||
Target: u.source,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *udpPacket) Drop() {
|
func (u *udpPacket) Drop() {
|
||||||
@ -49,20 +38,14 @@ func (u *udpPacket) LocalAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUDP(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
func handleUDP(payload []byte, source *net.UDPAddr, target *net.UDPAddr, udp bridge.UDP) {
|
||||||
pkt := &udpPacket{
|
pkt := &udpPacket{
|
||||||
source: endpoint.Source,
|
source: source,
|
||||||
data: payload,
|
data: payload,
|
||||||
send: sender,
|
udp: udp,
|
||||||
}
|
}
|
||||||
|
|
||||||
rAddr := &net.UDPAddr{
|
adapter := adapters.NewPacket(socks5.ParseAddrToSocksAddr(target), pkt, C.SOCKS)
|
||||||
IP: endpoint.Target.IP,
|
|
||||||
Port: int(endpoint.Target.Port),
|
|
||||||
Zone: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter := adapters.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.SOCKS)
|
|
||||||
|
|
||||||
tunnel.AddPacket(adapter)
|
tunnel.AddPacket(adapter)
|
||||||
}
|
}
|
||||||
|
1
core/src/main/golang/tun2socket
Submodule
1
core/src/main/golang/tun2socket
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a57caac68ee30407ceecc6d1c5f8f495bcb4f4fb
|
@ -61,13 +61,11 @@ object Clash {
|
|||||||
fun startTun(
|
fun startTun(
|
||||||
fd: Int,
|
fd: Int,
|
||||||
mtu: Int,
|
mtu: Int,
|
||||||
gateway: String,
|
|
||||||
mirror: String,
|
|
||||||
dns: String,
|
dns: String,
|
||||||
markSocket: (Int) -> Boolean,
|
markSocket: (Int) -> Boolean,
|
||||||
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
|
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
|
||||||
) {
|
) {
|
||||||
Bridge.nativeStartTun(fd, mtu, gateway, mirror, "$dns:53", object : TunInterface {
|
Bridge.nativeStartTun(fd, mtu, dns, object : TunInterface {
|
||||||
override fun markSocket(fd: Int) {
|
override fun markSocket(fd: Int) {
|
||||||
markSocket(fd)
|
markSocket(fd)
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,7 @@ object Bridge {
|
|||||||
external fun nativeQueryTrafficTotal(): Long
|
external fun nativeQueryTrafficTotal(): Long
|
||||||
external fun nativeNotifyDnsChanged(dnsList: String)
|
external fun nativeNotifyDnsChanged(dnsList: String)
|
||||||
external fun nativeNotifyInstalledAppChanged(uidList: String)
|
external fun nativeNotifyInstalledAppChanged(uidList: String)
|
||||||
external fun nativeStartTun(
|
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, cb: TunInterface)
|
||||||
fd: Int,
|
|
||||||
mtu: Int,
|
|
||||||
gateway: String,
|
|
||||||
mirror: String,
|
|
||||||
dns: String,
|
|
||||||
cb: TunInterface
|
|
||||||
)
|
|
||||||
|
|
||||||
external fun nativeStopTun()
|
external fun nativeStopTun()
|
||||||
external fun nativeStartHttp(listenAt: String): String?
|
external fun nativeStartHttp(listenAt: String): String?
|
||||||
external fun nativeStopHttp()
|
external fun nativeStopHttp()
|
||||||
|
@ -217,8 +217,6 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
|
|||||||
fd = establish()?.detachFd()
|
fd = establish()?.detachFd()
|
||||||
?: throw NullPointerException("Establish VPN rejected by system"),
|
?: throw NullPointerException("Establish VPN rejected by system"),
|
||||||
mtu = TUN_MTU,
|
mtu = TUN_MTU,
|
||||||
gateway = TUN_GATEWAY,
|
|
||||||
mirror = TUN_MIRROR,
|
|
||||||
dns = if (store.dnsHijacking) NET_ANY else TUN_DNS,
|
dns = if (store.dnsHijacking) NET_ANY else TUN_DNS,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -230,7 +228,6 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
|
|||||||
private const val TUN_MTU = 9000
|
private const val TUN_MTU = 9000
|
||||||
private const val TUN_SUBNET_PREFIX = 30
|
private const val TUN_SUBNET_PREFIX = 30
|
||||||
private const val TUN_GATEWAY = "172.31.255.253"
|
private const val TUN_GATEWAY = "172.31.255.253"
|
||||||
private const val TUN_MIRROR = "172.31.255.254"
|
|
||||||
private const val TUN_DNS = "198.18.0.1"
|
private const val TUN_DNS = "198.18.0.1"
|
||||||
private const val NET_ANY = "0.0.0.0"
|
private const val NET_ANY = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@ class TunModule(private val vpn: VpnService) : Module<Unit>(vpn) {
|
|||||||
data class TunDevice(
|
data class TunDevice(
|
||||||
val fd: Int,
|
val fd: Int,
|
||||||
val mtu: Int,
|
val mtu: Int,
|
||||||
val gateway: String,
|
|
||||||
val mirror: String,
|
|
||||||
val dns: String
|
val dns: String
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,8 +56,6 @@ class TunModule(private val vpn: VpnService) : Module<Unit>(vpn) {
|
|||||||
Clash.startTun(
|
Clash.startTun(
|
||||||
fd = device.fd,
|
fd = device.fd,
|
||||||
mtu = device.mtu,
|
mtu = device.mtu,
|
||||||
gateway = device.gateway,
|
|
||||||
mirror = device.mirror,
|
|
||||||
dns = device.dns,
|
dns = device.dns,
|
||||||
markSocket = vpn::protect,
|
markSocket = vpn::protect,
|
||||||
querySocketUid = this::queryUid
|
querySocketUid = this::queryUid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user