Feature: add block loopback connections

This commit is contained in:
Kr328 2021-05-28 11:32:21 +08:00
parent a222e90d1f
commit 5917b90837
14 changed files with 76 additions and 33 deletions

View File

@ -98,15 +98,15 @@ 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, Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz,
jint fd, jint mtu, jint fd, jint mtu,
jstring gateway, jstring dns, jstring dns, jstring blocking,
jobject cb) { jobject cb) {
TRACE_METHOD(); TRACE_METHOD();
scoped_string _gateway = get_string(gateway); scoped_string _blocking = get_string(blocking);
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, _dns, _interface); startTun(fd, mtu, _dns, _blocking, _interface);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL

View File

@ -20,6 +20,7 @@ func (a *adapter) tcp() {
defer log.Infoln("[ATUN] TCP listener exited") defer log.Infoln("[ATUN] TCP listener exited")
defer a.stack.Close() defer a.stack.Close()
accept:
for { for {
conn, err := a.stack.TCP().Accept() conn, err := a.stack.TCP().Accept()
if err != nil { if err != nil {
@ -34,9 +35,11 @@ func (a *adapter) tcp() {
continue continue
} }
// drop all connections connect to gateway // drop all connections connect to blocking list
if a.gateway.Contains(tAddr.IP) { for _, b := range a.blocking {
continue if b.Contains(tAddr.IP) {
continue accept
}
} }
metadata := &C.Metadata{ metadata := &C.Metadata{

View File

@ -3,6 +3,7 @@ package tun
import ( import (
"net" "net"
"os" "os"
"strings"
"sync" "sync"
"syscall" "syscall"
@ -12,7 +13,7 @@ import (
type adapter struct { type adapter struct {
device *os.File device *os.File
stack tun2socket.Stack stack tun2socket.Stack
gateway *net.IPNet blocking []*net.IPNet
dns net.IP dns net.IP
mtu int mtu int
once sync.Once once sync.Once
@ -27,7 +28,7 @@ func (a *adapter) close() {
_ = a.device.Close() _ = a.device.Close()
} }
func Start(fd, mtu int, gateway, dns string, stop func()) error { func Start(fd, mtu int, dns string, blocking string, stop func()) error {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
@ -46,12 +47,24 @@ func Start(fd, mtu int, gateway, dns string, stop func()) error {
} }
dn := net.ParseIP(dns) dn := net.ParseIP(dns)
_, gw, _ := net.ParseCIDR(gateway)
var blk []*net.IPNet
for _, b := range strings.Split(blocking, ";") {
_, n, err := net.ParseCIDR(b)
if err != nil {
device.Close()
return err
}
blk = append(blk, n)
}
instance = &adapter{ instance = &adapter{
device: device, device: device,
stack: stack, stack: stack,
gateway: gw, blocking: blk,
dns: dn, dns: dn,
mtu: mtu, mtu: mtu,
once: sync.Once{}, once: sync.Once{},

View File

@ -44,6 +44,7 @@ func (a *adapter) udp() {
defer log.Infoln("[ATUN] UDP receiver exited") defer log.Infoln("[ATUN] UDP receiver exited")
defer a.stack.Close() defer a.stack.Close()
read:
for { for {
buf := pool.Get(a.mtu) buf := pool.Get(a.mtu)
@ -60,11 +61,11 @@ func (a *adapter) udp() {
continue continue
} }
// drop all packets send to gateway // drop all packet send to blocking list
if a.gateway.Contains(tAddr.IP) { for _, b := range a.blocking {
pool.Put(buf) if b.Contains(tAddr.IP) {
continue read
continue }
} }
pkt := &packet{ pkt := &packet{

View File

@ -61,12 +61,12 @@ object Clash {
fun startTun( fun startTun(
fd: Int, fd: Int,
mtu: Int, mtu: Int,
gateway: String,
dns: String, dns: String,
blocking: 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, dns, object : TunInterface { Bridge.nativeStartTun(fd, mtu, dns, blocking, object : TunInterface {
override fun markSocket(fd: Int) { override fun markSocket(fd: Int) {
markSocket(fd) markSocket(fd)
} }

View File

@ -17,7 +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(fd: Int, mtu: Int, gateway: String, dns: String, cb: TunInterface) external fun nativeStartTun(fd: Int, mtu: Int, dns: String, blocking: 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()

View File

@ -70,6 +70,13 @@ class NetworkSettingsDesign(
configure = vpnDependencies::add, configure = vpnDependencies::add,
) )
switch(
value = srvStore::blockLoopback,
title = R.string.block_loopback,
summary = R.string.block_loopback_summary,
configure = vpnDependencies::add,
)
if (Build.VERSION.SDK_INT >= 29) { if (Build.VERSION.SDK_INT >= 29) {
switch( switch(
value = srvStore::systemProxy, value = srvStore::systemProxy,

View File

@ -211,4 +211,6 @@
<string name="sources">源代碼</string> <string name="sources">源代碼</string>
<string name="clash_core">Clash 核心</string> <string name="clash_core">Clash 核心</string>
<string name="name_server_policy">Name Server 策略</string> <string name="name_server_policy">Name Server 策略</string>
<string name="block_loopback">阻止本地迴環</string>
<string name="block_loopback_summary">阻止本地迴環連接</string>
</resources> </resources>

View File

@ -211,4 +211,6 @@
<string name="sources">源代碼</string> <string name="sources">源代碼</string>
<string name="clash_core">Clash 核心</string> <string name="clash_core">Clash 核心</string>
<string name="name_server_policy">Name Server 策略</string> <string name="name_server_policy">Name Server 策略</string>
<string name="block_loopback">阻止本地迴環</string>
<string name="block_loopback_summary">阻止本地迴環連接</string>
</resources> </resources>

View File

@ -211,4 +211,6 @@
<string name="sources">源代码</string> <string name="sources">源代码</string>
<string name="clash_core">Clash 核心</string> <string name="clash_core">Clash 核心</string>
<string name="name_server_policy">Name Server 策略</string> <string name="name_server_policy">Name Server 策略</string>
<string name="block_loopback">阻止本地回环</string>
<string name="block_loopback_summary">阻止本地回环连接</string>
</resources> </resources>

View File

@ -119,6 +119,8 @@
<string name="bypass_private_network_summary">Bypass private network addresses</string> <string name="bypass_private_network_summary">Bypass private network addresses</string>
<string name="dns_hijacking">DNS Hijacking</string> <string name="dns_hijacking">DNS Hijacking</string>
<string name="dns_hijacking_summary">Handle all dns packet</string> <string name="dns_hijacking_summary">Handle all dns packet</string>
<string name="block_loopback">Block Loopback</string>
<string name="block_loopback_summary">Block loopback connections</string>
<string name="system_proxy">System Proxy</string> <string name="system_proxy">System Proxy</string>
<string name="system_proxy_summary">Attach http proxy to VpnService</string> <string name="system_proxy_summary">Attach http proxy to VpnService</string>
<string name="access_control_mode">Access Control Mode</string> <string name="access_control_mode">Access Control Mode</string>

View File

@ -216,11 +216,16 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
} }
} }
val blocking = mutableListOf("$TUN_GATEWAY/$TUN_SUBNET_PREFIX")
if (store.blockLoopback) {
blocking.add(NET_SUBNET_LOOPBACK)
}
TunModule.TunDevice( TunModule.TunDevice(
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/$TUN_SUBNET_PREFIX", blocking = blocking.joinToString(";"),
dns = if (store.dnsHijacking) NET_ANY else TUN_DNS, dns = if (store.dnsHijacking) NET_ANY else TUN_DNS,
) )
} }
@ -234,5 +239,6 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
private const val TUN_GATEWAY = "172.19.0.1" private const val TUN_GATEWAY = "172.19.0.1"
private const val TUN_DNS = "172.19.0.2" private const val TUN_DNS = "172.19.0.2"
private const val NET_ANY = "0.0.0.0" private const val NET_ANY = "0.0.0.0"
private const val NET_SUBNET_LOOPBACK = "127.0.0.0/8"
} }
} }

View File

@ -16,7 +16,7 @@ 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 blocking: String,
val dns: String, val dns: String,
) )
@ -57,8 +57,8 @@ 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,
dns = device.dns, dns = device.dns,
blocking = device.blocking,
markSocket = vpn::protect, markSocket = vpn::protect,
querySocketUid = this::queryUid querySocketUid = this::queryUid
) )

View File

@ -46,6 +46,11 @@ class ServiceStore(context: Context) {
defaultValue = false defaultValue = false
) )
var blockLoopback by store.boolean(
key = "block_loopback",
defaultValue = true
)
var dynamicNotification by store.boolean( var dynamicNotification by store.boolean(
key = "dynamic_notification", key = "dynamic_notification",
defaultValue = true defaultValue = true