Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/workflows/build-manual.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Build Manual

on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to build from'
required: true
default: 'main'
type: branch
flavor:
description: 'Build flavor'
required: true
default: 'meta'
type: choice
options:
- meta
- alpha
release-tag:
description: 'Release tag'
required: true
default: 'manual'

jobs:
BuildManual:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
with:
ref: ${{ inputs.branch }}
fetch-depth: 0

- name: Checkout submodules
run: git submodule update --init --recursive --force

- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 21

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5

- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
with:
go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build'
go-version: '1.26'
cache: false

- name: Apply Patches
run: |
cd $(go env GOROOT)
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done

- uses: actions/cache@v5
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-

- name: Update CA
run: |
sudo apt-get update && sudo apt-get install ca-certificates
sudo update-ca-certificates
cp -f /etc/ssl/certs/ca-certificates.crt core/src/foss/golang/clash/component/ca/ca-certificates.crt

- name: Restore Keystore
env:
KEYSTORE: ${{ secrets.KEYSTORE }}
run: |
echo "$KEYSTORE" | base64 --decode > release.keystore

- name: Signing properties
env:
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
run: |
echo "keystore.password=$SIGNING_STORE_PASSWORD" > signing.properties
echo "key.alias=$SIGNING_KEY_ALIAS" >> signing.properties
echo "key.password=$SIGNING_KEY_PASSWORD" >> signing.properties

- name: Manual Build
if: success()
run: |
if [ "${{ inputs.flavor }}" = "meta" ]; then
./gradlew --no-daemon app:assembleMetaRelease
else
./gradlew --no-daemon app:assembleAlphaRelease
fi

- name: Upload Release
uses: softprops/action-gh-release@v3
if: ${{ success() }}
with:
tag_name: ${{ inputs.release-tag }}
files: app/build/outputs/apk/${{ inputs.flavor }}/release/*.apk
generate_release_notes: true
4 changes: 3 additions & 1 deletion core/src/main/cpp/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobje
jstring gateway,
jstring portal,
jstring dns,
jboolean disable_icmp_forwarding,
jint icmp_timeout,
jobject cb) {
TRACE_METHOD();

Expand All @@ -125,7 +127,7 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobje
scoped_string _dns = get_string(dns);
jobject _interface = new_global(cb);

startTun(fd, _stack, _gateway, _portal, _dns, _interface);
startTun(fd, _stack, _gateway, _portal, _dns, (int) disable_icmp_forwarding, (int) icmp_timeout, _interface);
}

JNIEXPORT void JNICALL
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/golang/native/tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (t *remoteTun) close() {
}

//export startTun
func startTun(fd C.int, stack, gateway, portal, dns C.c_string, callback unsafe.Pointer) C.int {
func startTun(fd C.int, stack, gateway, portal, dns C.c_string, disableICMPForwarding C.int, icmpTimeout C.int, callback unsafe.Pointer) C.int {
rTunLock.Lock()
defer rTunLock.Unlock()

Expand All @@ -83,7 +83,7 @@ func startTun(fd C.int, stack, gateway, portal, dns C.c_string, callback unsafe.

app.ApplyTunContext(remote.markSocket, remote.querySocketUid)

closer, err := tun.Start(f, s, g, p, d)
closer, err := tun.Start(f, s, g, p, d, disableICMPForwarding != 0, int64(icmpTimeout))
if err != nil {
remote.close()

Expand Down
24 changes: 13 additions & 11 deletions core/src/main/golang/native/tun/tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/metacubex/mihomo/tunnel"
)

func Start(fd int, stack, gateway, portal, dns string) (io.Closer, error) {
func Start(fd int, stack, gateway, portal, dns string, disableICMPForwarding bool, icmpTimeout int64) (io.Closer, error) {
log.Debugln("TUN: fd = %d, stack = %s, gateway = %s, portal = %s, dns = %s", fd, stack, gateway, portal, dns)

tunStack, ok := C.StackTypeMapping[strings.ToLower(stack)]
Expand Down Expand Up @@ -52,16 +52,18 @@ func Start(fd int, stack, gateway, portal, dns string) (io.Closer, error) {
}

options := LC.Tun{
Enable: true,
Device: sing_tun.InterfaceName,
Stack: tunStack,
DNSHijack: dnsHijack,
AutoRoute: false, // had set route in TunService.kt
AutoDetectInterface: false, // implements by VpnService::protect
Inet4Address: prefix4,
Inet6Address: prefix6,
MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt
FileDescriptor: fd,
Enable: true,
Device: sing_tun.InterfaceName,
Stack: tunStack,
DNSHijack: dnsHijack,
AutoRoute: false, // had set route in TunService.kt
AutoDetectInterface: false, // implements by VpnService::protect
Inet4Address: prefix4,
Inet6Address: prefix6,
MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt
FileDescriptor: fd,
DisableICMPForwarding: disableICMPForwarding,
ICMPTimeout: icmpTimeout,
}

tunOptions, _ := json.Marshal(options)
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/java/com/github/kr328/clash/core/Clash.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ object Clash {
gateway: String,
portal: String,
dns: String,
disableIcmpForwarding: Boolean,
icmpTimeout: Int,
markSocket: (Int) -> Boolean,
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
) {
Bridge.nativeStartTun(fd, stack, gateway, portal, dns, object : TunInterface {
Bridge.nativeStartTun(fd, stack, gateway, portal, dns, disableIcmpForwarding, icmpTimeout, object : TunInterface {
override fun markSocket(fd: Int) {
markSocket(fd)
}
Expand Down
11 changes: 10 additions & 1 deletion core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ object Bridge {
external fun nativeNotifyDnsChanged(dnsList: String)
external fun nativeNotifyTimeZoneChanged(name: String, offset: Int)
external fun nativeNotifyInstalledAppChanged(uidList: String)
external fun nativeStartTun(fd: Int, stack: String, gateway: String, portal: String, dns: String, cb: TunInterface)
external fun nativeStartTun(
fd: Int,
stack: String,
gateway: String,
portal: String,
dns: String,
disableIcmpForwarding: Boolean,
icmpTimeout: Int,
cb: TunInterface
)
external fun nativeStopTun()
external fun nativeStartHttp(listenAt: String): String?
external fun nativeStopHttp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ class NetworkSettingsDesign(
configure = vpnDependencies::add,
)

switch(
value = srvStore::disableIcmpForwarding,
title = R.string.disable_icmp_forwarding,
summary = R.string.disable_icmp_forwarding_summary,
configure = vpnDependencies::add,
)

editableText(
value = srvStore::icmpTimeout,
adapter = NullableTextAdapter.Int,
title = R.string.icmp_timeout,
empty = R.string.icmp_timeout_default,
configure = vpnDependencies::add,
)

switch(
value = srvStore::allowBypass,
title = R.string.allow_bypass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ interface NullableTextAdapter<T> {
}
}

val Int = object : NullableTextAdapter<Int> {
override fun from(value: Int): String? {
return if (value > 0) value.toString() else ""
}

override fun to(text: String?): Int {
return text?.trim()?.toIntOrNull()?.coerceAtLeast(0) ?: 0
}
}

val String = object : NullableTextAdapter<String?> {
override fun from(value: String?): String? {
return value
Expand Down
4 changes: 4 additions & 0 deletions design/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@
<string name="bypass_private_network_summary">Bypass private network addresses</string>
<string name="dns_hijacking">DNS Hijacking</string>
<string name="dns_hijacking_summary">Handle all dns packet</string>
<string name="disable_icmp_forwarding">Disable ICMP Forwarding</string>
<string name="disable_icmp_forwarding_summary">Reply ICMP echo requests locally instead of forwarding them</string>
<string name="icmp_timeout">ICMP Timeout (seconds)</string>
<string name="icmp_timeout_default">Default</string>
<string name="block_loopback">Block Loopback</string>
<string name="block_loopback_summary">Block loopback connections</string>
<string name="allow_bypass">Allow Bypass</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX" + if (store.allowIpv6) ",$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "",
portal = TUN_PORTAL + if (store.allowIpv6) ",$TUN_PORTAL6" else "",
dns = if (store.dnsHijacking) NET_ANY else (TUN_DNS + if (store.allowIpv6) ",$TUN_DNS6" else ""),
disableIcmpForwarding = store.disableIcmpForwarding,
icmpTimeout = store.icmpTimeout,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class TunModule(private val vpn: VpnService) : Module<Unit>(vpn) {
val gateway: String,
val portal: String,
val dns: String,
val disableIcmpForwarding: Boolean,
val icmpTimeout: Int,
)

private val connectivity = service.getSystemService<ConnectivityManager>()!!
Expand Down Expand Up @@ -61,6 +63,8 @@ class TunModule(private val vpn: VpnService) : Module<Unit>(vpn) {
gateway = device.gateway,
portal = device.portal,
dns = device.dns,
disableIcmpForwarding = device.disableIcmpForwarding,
icmpTimeout = device.icmpTimeout,
markSocket = vpn::protect,
querySocketUid = this::queryUid
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ class ServiceStore(context: Context) {
defaultValue = "mips"
)

var disableIcmpForwarding by store.boolean(
key = "disable_icmp_forwarding",
defaultValue = true
)

var icmpTimeout by store.int(
key = "icmp_timeout",
defaultValue = 0
)

var dynamicNotification by store.boolean(
key = "dynamic_notification",
defaultValue = true
Expand Down