Compare commits

...

20 Commits

Author SHA1 Message Date
armv9
d44438df8b 1.3.5 2025-02-23 23:36:56 +09:00
armv9
3b3c6757b9 Fix bugs 2025-02-23 23:36:56 +09:00
armv9
e1b8488f94 go 1.23 + sing-box 1.11.3 + anytls
thanks husi
2025-02-23 23:36:56 +09:00
armv9
6a0d998eaf AnyTLS GUI
thanks husi
2025-02-23 23:36:56 +09:00
armv9
4483a85df2 Subscription: parse sing-box outbound 2025-02-23 23:26:43 +09:00
armv9
89c1dab989 Fix native bugs 2025-02-23 21:58:58 +09:00
HystericalDragon
6f7b4a125f fix: fix reciver in SDK 34 2025-02-23 21:58:58 +09:00
armv9
c7ef42d9f1 Refactoring mux settings 2025-02-23 21:58:58 +09:00
armv9
607afa8ce3 Upgrade SDK 35 2025-02-23 21:58:58 +09:00
armv9
ea8e4aee60 Upgrade AGP 8.8.1 2025-02-23 21:58:58 +09:00
armv9
cc9ea9a81a Remove redundant ECH options 2025-02-23 21:58:58 +09:00
SodaWithoutSparkles
febb1e210e
fix: donate info (#873) 2025-02-10 00:28:15 +08:00
purofle
e241977572
Revert "refactor: set the default multiplex protocol to smux"
This reverts commit 3ee216700c.
2025-01-27 18:26:27 +08:00
purofle
e3fe755b25
fix: set default dns to 233.5.5.5
Closes: #861
Closes: #860
Closes: #859
Closes: #857
Closes: #856
Closes: #854
Closes: #853
Closes: #852
Closes: #850
Closes: #858
2025-01-26 22:22:29 +08:00
purofle
3ee216700c
refactor: set the default multiplex protocol to smux 2025-01-26 22:22:17 +08:00
zizzdog
8590d9284a
修复“域名规则默认使用 domain_suffix” (#848) 2025-01-18 22:18:04 +08:00
armv9
5a191fdc94 1.3.4 2024-12-15 18:55:00 +09:00
armv9
71a3cdd8b6 fix: Re-add the server domain for bypass 2024-12-14 15:49:56 +09:00
armv9
d69f607d72 1.3.3 2024-12-12 21:10:12 +09:00
armv9
fd825c5532 Add workaround for bulkBarrierPreWrite: unaligned arguments panic 2024-12-12 21:09:44 +09:00
73 changed files with 1875 additions and 508 deletions

View File

@ -33,7 +33,7 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
go-version: "1.22.10"
go-version: "1.23.6"
- name: Native Build
if: steps.cache.outputs.cache-hit != 'true'
run: ./run lib core

View File

@ -3,7 +3,7 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("com.google.devtools.ksp")
id("kotlin-parcelize")
}
@ -13,8 +13,8 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
kapt.arguments {
arg("room.incremental", true)
ksp {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas")
}
bundle {
@ -23,9 +23,19 @@ android {
}
}
buildFeatures {
buildConfig = true
viewBinding = true
aidl = true
}
namespace = "io.nekohasekai.sagernet"
packaging {
jniLibs {
useLegacyPackaging = true
}
}
androidResources {
generateLocaleConfig = true
}
}
dependencies {
@ -68,11 +78,11 @@ dependencies {
exclude(group = "androidx.appcompat")
}
implementation("androidx.room:room-runtime:2.5.1")
kapt("androidx.room:room-compiler:2.5.1")
implementation("androidx.room:room-ktx:2.5.1")
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
implementation("com.github.MatrixDev.Roomigrant:RoomigrantLib:0.3.4")
kapt("com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4")
ksp("com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
}

View File

@ -0,0 +1,360 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "cff00d0142d9e53d2ca24a6a55cd213c",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cff00d0142d9e53d2ca24a6a55cd213c')"
]
}
}

View File

@ -0,0 +1,366 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "1dbf667053726c13d139a4d83c41f895",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `anyTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "anyTLSBean",
"columnName": "anyTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dbf667053726c13d139a4d83c41f895')"
]
}
}

View File

@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@ -50,7 +51,6 @@
android:autoRevokePermissions="allowed"
android:banner="@mipmap/ic_launcher"
android:dataExtractionRules="@xml/backup_rules"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true"
android:hardwareAccelerated="true"
@ -188,6 +188,9 @@
<activity
android:name="moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="moe.matsuri.nb4a.proxy.neko.NekoSettingActivity"
android:configChanges="uiMode" />
@ -255,11 +258,17 @@
<service
android:name="io.nekohasekai.sagernet.bg.ProxyService"
android:exported="false"
android:process=":bg" />
android:foregroundServiceType="specialUse"
android:process=":bg">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="proxy" />
</service>
<service
android:name="io.nekohasekai.sagernet.bg.VpnService"
android:exported="false"
android:foregroundServiceType="specialUse"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":bg">
@ -267,6 +276,9 @@
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="vpn" />
</service>
<service

View File

@ -52,9 +52,6 @@ object Key {
const val ALWAYS_SHOW_ADDRESS = "alwaysShowAddress"
// Protocol Settings
const val MUX_TYPE = "muxType"
const val MUX_PROTOCOLS = "mux"
const val MUX_CONCURRENCY = "muxConcurrency"
const val GLOBAL_ALLOW_INSECURE = "globalAllowInsecure"
const val ACQUIRE_WAKE_LOCK = "acquireWakeLock"

View File

@ -328,7 +328,7 @@ class BaseService {
data.proxy = proxy
BootReceiver.enabled = DataStore.persistAcrossReboot
if (!data.closeReceiverRegistered) {
registerReceiver(data.receiver, IntentFilter().apply {
val filter = IntentFilter().apply {
addAction(Action.RELOAD)
addAction(Intent.ACTION_SHUTDOWN)
addAction(Action.CLOSE)
@ -337,7 +337,23 @@ class BaseService {
addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
}
addAction(Action.RESET_UPSTREAM_CONNECTIONS)
}, "$packageName.SERVICE", null)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(
data.receiver,
filter,
"$packageName.SERVICE",
null,
Context.RECEIVER_EXPORTED
)
} else {
registerReceiver(
data.receiver,
filter,
"$packageName.SERVICE",
null
)
}
data.closeReceiverRegistered = true
}

View File

@ -6,6 +6,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.os.Build
import android.text.format.Formatter
import androidx.core.app.NotificationCompat
@ -183,7 +184,17 @@ class ServiceNotification(
private suspend fun show() =
useBuilder { (service as Service).startForeground(notificationId, it.build()) }
useBuilder {
if (Build.VERSION.SDK_INT >= 34) {
(service as Service).startForeground(
notificationId,
it.build(),
FOREGROUND_SERVICE_TYPE_SPECIAL_USE
)
} else {
(service as Service).startForeground(notificationId, it.build())
}
}
private suspend fun update() = useBuilder {
NotificationManagerCompat.from(service as Service).notify(notificationId, it.build())
@ -191,7 +202,11 @@ class ServiceNotification(
fun destroy() {
listenPostSpeed = false
(service as Service).stopForeground(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
(service as Service).stopForeground(Service.STOP_FOREGROUND_REMOVE)
} else {
(service as Service).stopForeground(true)
}
service.unregisterReceiver(this)
}
}

View File

@ -47,7 +47,6 @@ class TestInstance(profile: ProxyEntity, val link: String, val timeout: Int) :
// don't call destroyAllJsi here
if (BuildConfig.DEBUG) Logs.d(config.config)
box = Libcore.newSingBoxInstance(config.config)
box.forTest = true
}
}

View File

@ -2,13 +2,24 @@ package io.nekohasekai.sagernet.database
import android.os.Binder
import androidx.preference.PreferenceDataStore
import io.nekohasekai.sagernet.*
import io.nekohasekai.sagernet.CONNECTION_TEST_URL
import io.nekohasekai.sagernet.GroupType
import io.nekohasekai.sagernet.IPv6Mode
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.TunImplementation
import io.nekohasekai.sagernet.bg.BaseService
import io.nekohasekai.sagernet.bg.VpnService
import io.nekohasekai.sagernet.database.preference.OnPreferenceDataStoreChangeListener
import io.nekohasekai.sagernet.database.preference.PublicDatabase
import io.nekohasekai.sagernet.database.preference.RoomPreferenceDataStore
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.ktx.boolean
import io.nekohasekai.sagernet.ktx.int
import io.nekohasekai.sagernet.ktx.long
import io.nekohasekai.sagernet.ktx.parsePort
import io.nekohasekai.sagernet.ktx.string
import io.nekohasekai.sagernet.ktx.stringSet
import io.nekohasekai.sagernet.ktx.stringToInt
import io.nekohasekai.sagernet.ktx.stringToIntIfExists
import moe.matsuri.nb4a.TempDatabase
object DataStore : OnPreferenceDataStoreChangeListener {
@ -98,7 +109,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var showGroupInNotification by configurationStore.boolean("showGroupInNotification")
var remoteDns by configurationStore.string(Key.REMOTE_DNS) { "https://dns.google/dns-query" }
var directDns by configurationStore.string(Key.DIRECT_DNS) { "https://120.53.53.53/dns-query" }
var directDns by configurationStore.string(Key.DIRECT_DNS) { "https://223.5.5.5/dns-query" }
var enableDnsRouting by configurationStore.boolean(Key.ENABLE_DNS_ROUTING) { true }
var enableFakeDns by configurationStore.boolean(Key.ENABLE_FAKEDNS)
@ -158,9 +169,6 @@ object DataStore : OnPreferenceDataStoreChangeListener {
// protocol
var muxType by configurationStore.stringToInt(Key.MUX_TYPE)
var muxProtocols by configurationStore.stringSet(Key.MUX_PROTOCOLS)
var muxConcurrency by configurationStore.stringToInt(Key.MUX_CONCURRENCY) { 8 }
var globalAllowInsecure by configurationStore.boolean(Key.GLOBAL_ALLOW_INSECURE) { false }
// old cache, DO NOT ADD

View File

@ -32,7 +32,9 @@ import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.*
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.*
@ -65,6 +67,7 @@ data class ProxyEntity(
var sshBean: SSHBean? = null,
var wgBean: WireGuardBean? = null,
var shadowTLSBean: ShadowTLSBean? = null,
var anyTLSBean: AnyTLSBean? = null,
var chainBean: ChainBean? = null,
var nekoBean: NekoBean? = null,
var configBean: ConfigBean? = null,
@ -77,16 +80,16 @@ data class ProxyEntity(
const val TYPE_VMESS = 4
const val TYPE_TROJAN = 6
const val TYPE_TROJAN_GO = 7
const val TYPE_MIERU = 21
const val TYPE_NAIVE = 9
const val TYPE_HYSTERIA = 15
const val TYPE_TUIC = 20
const val TYPE_SSH = 17
const val TYPE_WG = 18
const val TYPE_TROJAN_GO = 7
const val TYPE_NAIVE = 9
const val TYPE_HYSTERIA = 15
const val TYPE_SHADOWTLS = 19
const val TYPE_TUIC = 20
const val TYPE_MIERU = 21
const val TYPE_ANYTLS = 22
const val TYPE_CONFIG = 998
const val TYPE_NEKO = 999
@ -172,6 +175,7 @@ data class ProxyEntity(
TYPE_WG -> wgBean = KryoConverters.wireguardDeserialize(byteArray)
TYPE_TUIC -> tuicBean = KryoConverters.tuicDeserialize(byteArray)
TYPE_SHADOWTLS -> shadowTLSBean = KryoConverters.shadowTLSDeserialize(byteArray)
TYPE_ANYTLS -> anyTLSBean = KryoConverters.anyTLSDeserialize(byteArray)
TYPE_CHAIN -> chainBean = KryoConverters.chainDeserialize(byteArray)
TYPE_NEKO -> nekoBean = KryoConverters.nekoDeserialize(byteArray)
TYPE_CONFIG -> configBean = KryoConverters.configDeserialize(byteArray)
@ -192,6 +196,7 @@ data class ProxyEntity(
TYPE_WG -> "WireGuard"
TYPE_TUIC -> "TUIC"
TYPE_SHADOWTLS -> "ShadowTLS"
TYPE_ANYTLS -> "AnyTLS"
TYPE_CHAIN -> chainName
TYPE_NEKO -> nekoBean!!.displayType()
TYPE_CONFIG -> configBean!!.displayType()
@ -216,6 +221,7 @@ data class ProxyEntity(
TYPE_WG -> wgBean
TYPE_TUIC -> tuicBean
TYPE_SHADOWTLS -> shadowTLSBean
TYPE_ANYTLS -> anyTLSBean
TYPE_CHAIN -> chainBean
TYPE_NEKO -> nekoBean
TYPE_CONFIG -> configBean
@ -235,6 +241,7 @@ data class ProxyEntity(
is SSHBean -> false
is WireGuardBean -> false
is ShadowTLSBean -> false
is AnyTLSBean -> false
is NekoBean -> nekoBean!!.haveStandardLink()
is ConfigBean -> false
else -> true
@ -309,19 +316,31 @@ data class ProxyEntity(
}
}
fun needCoreMux(): Boolean {
fun singMux(): MultiplexOptions? {
return when (type) {
TYPE_VMESS -> if (vmessBean!!.isVLESS) {
Protocols.isProfileNeedMux(vmessBean!!) && Protocols.shouldEnableMux("vless")
} else {
Protocols.isProfileNeedMux(vmessBean!!) && Protocols.shouldEnableMux("vmess")
TYPE_VMESS -> MultiplexOptions().apply {
enabled = vmessBean!!.enableMux
padding = vmessBean!!.muxPadding
max_streams = vmessBean!!.muxConcurrency
protocol = when (vmessBean!!.muxType) {
1 -> "smux"
2 -> "yamux"
else -> "h2mux"
}
}
TYPE_TROJAN -> Protocols.isProfileNeedMux(trojanBean!!)
&& Protocols.shouldEnableMux("trojan")
TYPE_TROJAN -> MultiplexOptions().apply {
enabled = trojanBean!!.enableMux
padding = trojanBean!!.muxPadding
max_streams = trojanBean!!.muxConcurrency
protocol = when (trojanBean!!.muxType) {
1 -> "smux"
2 -> "yamux"
else -> "h2mux"
}
}
TYPE_SS -> !ssBean!!.sUoT && Protocols.shouldEnableMux("shadowsocks")
else -> false
else -> null
}
}
@ -339,6 +358,7 @@ data class ProxyEntity(
wgBean = null
tuicBean = null
shadowTLSBean = null
anyTLSBean = null
chainBean = null
configBean = null
nekoBean = null
@ -409,6 +429,11 @@ data class ProxyEntity(
shadowTLSBean = bean
}
is AnyTLSBean -> {
type = TYPE_ANYTLS
anyTLSBean = bean
}
is ChainBean -> {
type = TYPE_CHAIN
chainBean = bean
@ -445,6 +470,7 @@ data class ProxyEntity(
TYPE_WG -> WireGuardSettingsActivity::class.java
TYPE_TUIC -> TuicSettingsActivity::class.java
TYPE_SHADOWTLS -> ShadowTLSSettingsActivity::class.java
TYPE_ANYTLS -> AnyTLSSettingsActivity::class.java
TYPE_CHAIN -> ChainSettingsActivity::class.java
TYPE_NEKO -> NekoSettingActivity::class.java
TYPE_CONFIG -> ConfigSettingActivity::class.java

View File

@ -8,6 +8,7 @@ import kotlinx.parcelize.Parcelize
@Entity(tableName = "rules")
@Parcelize
@TypeConverters(StringCollectionConverter::class)
data class RuleEntity(
@PrimaryKey(autoGenerate = true) var id: Long = 0L,
var name: String = "",
@ -21,7 +22,7 @@ data class RuleEntity(
var source: String = "",
var protocol: String = "",
var outbound: Long = 0,
var packages: List<String> = listOf(),
var packages: Set<String> = emptySet(),
) : Parcelable {
fun displayName(): String {

View File

@ -1,5 +1,6 @@
package io.nekohasekai.sagernet.database
import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@ -15,7 +16,11 @@ import kotlinx.coroutines.launch
@Database(
entities = [ProxyGroup::class, ProxyEntity::class, RuleEntity::class],
version = 3
version = 5,
autoMigrations = [
AutoMigration(from = 3, to = 4),
AutoMigration(from = 4, to = 5)
]
)
@TypeConverters(value = [KryoConverters::class, GsonConverters::class])
@GenerateRoomMigrations
@ -27,7 +32,7 @@ abstract class SagerDatabase : RoomDatabase() {
val instance by lazy {
SagerNet.application.getDatabasePath(Key.DB_PROFILE).parentFile?.mkdirs()
Room.databaseBuilder(SagerNet.application, SagerDatabase::class.java, Key.DB_PROFILE)
.addMigrations(*SagerDatabase_Migrations.build())
// .addMigrations(*SagerDatabase_Migrations.build())
.allowMainThreadQueries()
.enableMultiInstanceInvalidation()
.fallbackToDestructiveMigration()

View File

@ -0,0 +1,44 @@
package io.nekohasekai.sagernet.database
import androidx.room.TypeConverter
class StringCollectionConverter {
companion object {
const val SPLIT_FLAG = ","
/*
@TypeConverter
@JvmStatic
fun fromList(list: List<String>): String = if (list.isEmpty()) {
""
} else {
list.joinToString(SPLIT_FLAG)
}
@TypeConverter
@JvmStatic
fun toList(str: String): List<String> = if (str.isBlank()) {
emptyList()
} else {
str.split(SPLIT_FLAG)
}
*/
@TypeConverter
@JvmStatic
fun fromSet(set: Set<String>): String = if (set.isEmpty()) {
""
} else {
set.joinToString(SPLIT_FLAG)
}
@TypeConverter
@JvmStatic
fun toSet(str: String): Set<String> = if (str.isBlank()) {
emptySet()
} else {
str.split(",").toSet()
}
}
}

View File

@ -1,11 +1,7 @@
package io.nekohasekai.sagernet.fmt
import android.widget.Toast
import io.nekohasekai.sagernet.IPv6Mode
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.TunImplementation
import io.nekohasekai.sagernet.*
import io.nekohasekai.sagernet.bg.VpnService
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
@ -27,39 +23,21 @@ import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
import io.nekohasekai.sagernet.fmt.v2ray.buildSingBoxOutboundStandardV2RayBean
import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.fmt.wireguard.buildSingBoxOutboundWireguardBean
import io.nekohasekai.sagernet.ktx.isIpAddress
import io.nekohasekai.sagernet.ktx.mkPort
import io.nekohasekai.sagernet.utils.PackageCache
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.SingBoxOptions.CacheFile
import moe.matsuri.nb4a.SingBoxOptions.ClashAPIOptions
import moe.matsuri.nb4a.SingBoxOptions.DNSFakeIPOptions
import moe.matsuri.nb4a.SingBoxOptions.DNSOptions
import moe.matsuri.nb4a.SingBoxOptions.DNSRule_DefaultOptions
import moe.matsuri.nb4a.SingBoxOptions.DNSServerOptions
import moe.matsuri.nb4a.SingBoxOptions.ExperimentalOptions
import moe.matsuri.nb4a.SingBoxOptions.Inbound_DirectOptions
import moe.matsuri.nb4a.SingBoxOptions.Inbound_MixedOptions
import moe.matsuri.nb4a.SingBoxOptions.Inbound_TunOptions
import moe.matsuri.nb4a.SingBoxOptions.LogOptions
import moe.matsuri.nb4a.SingBoxOptions.MultiplexOptions
import moe.matsuri.nb4a.SingBoxOptions.MyOptions
import moe.matsuri.nb4a.SingBoxOptions.Outbound
import moe.matsuri.nb4a.SingBoxOptions.Outbound_SelectorOptions
import moe.matsuri.nb4a.SingBoxOptions.Outbound_SocksOptions
import moe.matsuri.nb4a.SingBoxOptions.RouteOptions
import moe.matsuri.nb4a.SingBoxOptions.RuleSet
import moe.matsuri.nb4a.SingBoxOptions.Rule_DefaultOptions
import moe.matsuri.nb4a.SingBoxOptionsUtil
import moe.matsuri.nb4a.checkEmpty
import moe.matsuri.nb4a.generateRuleSet
import moe.matsuri.nb4a.makeSingBoxRule
import moe.matsuri.nb4a.*
import moe.matsuri.nb4a.SingBoxOptions.*
import moe.matsuri.nb4a.plugin.Plugins
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
import moe.matsuri.nb4a.proxy.anytls.buildSingBoxOutboundAnyTLSBean
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
import moe.matsuri.nb4a.proxy.shadowtls.buildSingBoxOutboundShadowTLSBean
import moe.matsuri.nb4a.utils.JavaUtil.gson
import moe.matsuri.nb4a.utils.Util
import moe.matsuri.nb4a.utils.listByLineOrComma
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
const val TAG_MIXED = "mixed-in"
@ -156,6 +134,7 @@ fun buildConfig(
}.toHashSet().toList()).associateBy { it.id }
val buildSelector = !forTest && group?.isSelector == true && !forExport
val userDNSRuleList = mutableListOf<DNSRule_DefaultOptions>()
val domainListDNSDirectForce = mutableListOf<String>()
val bypassDNSBeans = hashSetOf<AbstractBean>()
val isVPN = DataStore.serviceMode == Key.MODE_VPN
val bind = if (!forTest && DataStore.allowAccess) "0.0.0.0" else LOCALHOST
@ -398,6 +377,9 @@ fun buildConfig(
is SSHBean ->
buildSingBoxOutboundSSHBean(bean).asMap()
is AnyTLSBean ->
buildSingBoxOutboundAnyTLSBean(bean).asMap()
else -> throw IllegalStateException("can't reach")
}
@ -406,18 +388,12 @@ fun buildConfig(
// val keepAliveInterval = DataStore.tcpKeepAliveInterval
// val needKeepAliveInterval = keepAliveInterval !in intArrayOf(0, 15)
if (!muxApplied && proxyEntity.needCoreMux()) {
muxApplied = true
currentOutbound["multiplex"] = MultiplexOptions().apply {
enabled = true
padding = Protocols.shouldEnableMux("padding")
max_streams = DataStore.muxConcurrency
protocol = when (DataStore.muxType) {
1 -> "smux"
2 -> "yamux"
else -> "h2mux"
}
}.asMap()
if (!muxApplied) {
val muxObj = proxyEntity.singMux()
if (muxObj != null && muxObj.enabled) {
muxApplied = true
currentOutbound["multiplex"] = muxObj.asMap()
}
}
}
}
@ -434,7 +410,12 @@ fun buildConfig(
}
// domain_strategy
pastEntity?.requireBean()?.apply {
// don't loopback
if (defaultServerDomainStrategy != "" && !serverAddress.isIpAddress()) {
domainListDNSDirectForce.add("full:$serverAddress")
}
}
currentOutbound["domain_strategy"] =
if (forTest) "" else defaultServerDomainStrategy
@ -667,6 +648,35 @@ fun buildConfig(
}.asMap())
}
// Bypass Lookup for the first profile
bypassDNSBeans.forEach {
var serverAddr = it.serverAddress
if (it is ConfigBean) {
var config = mutableMapOf<String, Any>()
config = gson.fromJson(it.config, config.javaClass)
config["server"]?.apply {
serverAddr = toString()
}
}
if (!serverAddr.isIpAddress()) {
domainListDNSDirectForce.add("full:${serverAddr}")
}
}
remoteDns.forEach {
var address = it
if (address.contains("://")) {
address = address.substringAfter("://")
}
"https://$address".toHttpUrlOrNull()?.apply {
if (!host.isIpAddress()) {
domainListDNSDirectForce.add("full:$host")
}
}
}
// remote dns obj
remoteDns.firstOrNull().let {
dns.servers.add(DNSServerOptions().apply {
@ -748,11 +758,18 @@ fun buildConfig(
disable_cache = true
})
}
// avoid loopback (always top DNS rule)
// avoid loopback
dns.rules.add(0, DNSRule_DefaultOptions().apply {
outbound = mutableListOf("any")
server = "dns-direct"
})
// force bypass (always top DNS rule)
if (domainListDNSDirectForce.isNotEmpty()) {
dns.rules.add(0, DNSRule_DefaultOptions().apply {
makeSingBoxRule(domainListDNSDirectForce.toHashSet().toList())
server = "dns-direct"
})
}
}
}.let {
ConfigBuildResult(

View File

@ -16,6 +16,7 @@ import io.nekohasekai.sagernet.fmt.internal.ChainBean;
import io.nekohasekai.sagernet.fmt.mieru.MieruBean;
import io.nekohasekai.sagernet.fmt.naive.NaiveBean;
import io.nekohasekai.sagernet.fmt.shadowsocks.ShadowsocksBean;
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean;
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean;
import io.nekohasekai.sagernet.fmt.socks.SOCKSBean;
import io.nekohasekai.sagernet.fmt.ssh.SSHBean;
@ -142,6 +143,13 @@ public class KryoConverters {
return deserialize(new ShadowTLSBean(), bytes);
}
@TypeConverter
public static AnyTLSBean anyTLSDeserialize(byte[] bytes) {
if (JavaUtil.isEmpty(bytes)) return null;
return deserialize(new AnyTLSBean(), bytes);
}
@TypeConverter
public static ChainBean chainDeserialize(byte[] bytes) {
if (JavaUtil.isEmpty(bytes)) return null;

View File

@ -16,6 +16,7 @@ object TypeMap : HashMap<String, Int>() {
this["ssh"] = ProxyEntity.TYPE_SSH
this["wg"] = ProxyEntity.TYPE_WG
this["tuic"] = ProxyEntity.TYPE_TUIC
this["anytls"] = ProxyEntity.TYPE_ANYTLS
this["neko"] = ProxyEntity.TYPE_NEKO
this["config"] = ProxyEntity.TYPE_CONFIG
}

View File

@ -95,9 +95,9 @@ fun parseHysteria2(url: String): HysteriaBean {
link.queryParameter("obfs-password")?.also {
obfuscation = it
}
link.queryParameter("pinSHA256")?.also {
// TODO your box do not support it
}
// link.queryParameter("pinSHA256")?.also {
// // TODO your box do not support it
// }
}
}
@ -284,9 +284,9 @@ fun buildSingBoxOutboundHysteriaBean(bean: HysteriaBean): MutableMap<String, Any
if (port != null) {
server_port = port
} else {
hop_ports = bean.serverPorts
server_ports = bean.serverPorts
}
hop_interval = bean.hopInterval
hop_interval = "${bean.hopInterval}s"
up_mbps = bean.uploadMbps
down_mbps = bean.downloadMbps
obfs = bean.obfuscation
@ -323,9 +323,9 @@ fun buildSingBoxOutboundHysteriaBean(bean: HysteriaBean): MutableMap<String, Any
if (port != null) {
server_port = port
} else {
hop_ports = bean.serverPorts
server_ports = bean.serverPorts
}
hop_interval = bean.hopInterval
hop_interval = "${bean.hopInterval}s"
up_mbps = bean.uploadMbps
down_mbps = bean.downloadMbps
if (bean.obfuscation.isNotBlank()) {

View File

@ -92,10 +92,10 @@ fun TrojanGoBean.buildTrojanGoConfig(port: Int): String {
put(password)
})
put("log_level", if (DataStore.logLevel > 0) 0 else 2)
if (Protocols.shouldEnableMux("trojan-go")) put("mux", JSONObject().apply {
put("enabled", true)
put("concurrency", DataStore.muxConcurrency)
})
// if (Protocols.shouldEnableMux("trojan-go")) put("mux", JSONObject().apply {
// put("enabled", true)
// put("concurrency", DataStore.muxConcurrency)
// })
put("tcp", JSONObject().apply {
put("prefer_ipv4", DataStore.ipv6Mode <= IPv6Mode.ENABLE)
})

View File

@ -52,11 +52,20 @@ public abstract class StandardV2RayBean extends AbstractBean {
public Boolean enableECH;
public String echConfig;
// sing-box 不再使用
public Boolean enablePqSignature;
public Boolean disabledDRS;
public String echConfig;
// --------------------------------------- Mux
public Boolean enableMux;
public Boolean muxPadding;
public Integer muxType;
public Integer muxConcurrency;
// --------------------------------------- //
@ -101,11 +110,16 @@ public abstract class StandardV2RayBean extends AbstractBean {
if (JavaUtil.isNullOrBlank(echConfig)) echConfig = "";
if (enablePqSignature == null) enablePqSignature = false;
if (disabledDRS == null) disabledDRS = false;
if (enableMux == null) enableMux = false;
if (muxPadding == null) muxPadding = false;
if (muxType == null) muxType = 0;
if (muxConcurrency == null) muxConcurrency = 1;
}
@Override
public void serialize(ByteBufferOutput output) {
output.writeInt(1);
output.writeInt(2);
super.serialize(output);
output.writeString(uuid);
output.writeString(encryption);
@ -160,6 +174,11 @@ public abstract class StandardV2RayBean extends AbstractBean {
}
output.writeInt(packetEncoding);
output.writeBoolean(enableMux);
output.writeBoolean(muxPadding);
output.writeInt(muxType);
output.writeInt(muxConcurrency);
}
@Override
@ -239,6 +258,13 @@ public abstract class StandardV2RayBean extends AbstractBean {
}
packetEncoding = input.readInt();
if (version >= 2) {
enableMux = input.readBoolean();
muxPadding = input.readBoolean();
muxType = input.readInt();
muxConcurrency = input.readInt();
}
}
@Override
@ -251,6 +277,10 @@ public abstract class StandardV2RayBean extends AbstractBean {
bean.enableECH = enableECH;
bean.disabledDRS = disabledDRS;
bean.echConfig = echConfig;
bean.enableMux = enableMux;
bean.muxPadding = muxPadding;
bean.muxType = muxType;
bean.muxConcurrency = muxConcurrency;
}
public boolean isVLESS() {

View File

@ -622,8 +622,6 @@ fun buildSingBoxOutboundTLS(bean: StandardV2RayBean): OutboundTLSOptions? {
if (bean.enableECH) {
ech = OutboundECHOptions().apply {
enabled = true
pq_signature_schemes_enabled = bean.enablePqSignature
dynamic_record_sizing_disabled = bean.disabledDRS
if (bean.echConfig.isNotBlank()) {
config = bean.echConfig.lines()
}

View File

@ -22,7 +22,9 @@ import io.nekohasekai.sagernet.fmt.wireguard.WireGuardBean
import io.nekohasekai.sagernet.ktx.*
import libcore.Libcore
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.proxy.anytls.AnyTLSBean
import moe.matsuri.nb4a.proxy.config.ConfigBean
import moe.matsuri.nb4a.utils.Util
import org.ini4j.Ini
import org.json.JSONArray
import org.json.JSONObject
@ -66,10 +68,11 @@ object RawUpdater : GroupUpdater() {
setURL(subscription.link)
setUserAgent(subscription.customUserAgent.takeIf { it.isNotBlank() } ?: USER_AGENT)
}.execute()
proxies = parseRaw(response.contentString)
proxies = parseRaw(Util.getStringBox(response.contentString))
?: error(app.getString(R.string.no_proxies_found))
subscription.subscriptionUserinfo = response.getHeader("Subscription-Userinfo")
subscription.subscriptionUserinfo =
Util.getStringBox(response.getHeader("Subscription-Userinfo"))
}
val proxiesMap = LinkedHashMap<String, AbstractBean>()
@ -247,6 +250,7 @@ object RawUpdater : GroupUpdater() {
setTLS(proxy["tls"]?.toString() == "true")
sni = proxy["sni"]?.toString()
name = proxy["name"]?.toString()
allowInsecure = proxy["name"]?.toString() == "true"
})
}
@ -414,6 +418,20 @@ object RawUpdater : GroupUpdater() {
realityOpt.value.toString()
}
}
"smux" -> for (smuxOpt in (opt.value as Map<String, Any>)) {
when (smuxOpt.key.lowercase()) {
"enabled" -> bean.enableMux =
smuxOpt.value.toString() == "true"
"max-streams" -> bean.muxConcurrency =
smuxOpt.value.toString().toInt()
"padding" -> bean.muxPadding =
smuxOpt.value.toString() == "true"
}
}
}
}
if (isHttpUpgrade) {
@ -473,6 +491,19 @@ object RawUpdater : GroupUpdater() {
grpcOpt.value.toString()
}
}
"smux" -> for (smuxOpt in (opt.value as Map<String, Any>)) {
when (smuxOpt.key.lowercase()) {
"enabled" -> bean.enableMux =
smuxOpt.value.toString() == "true"
"max-streams" -> bean.muxConcurrency =
smuxOpt.value.toString().toInt()
"padding" -> bean.muxPadding =
smuxOpt.value.toString() == "true"
}
}
}
}
if (isHttpUpgrade) {
@ -481,6 +512,31 @@ object RawUpdater : GroupUpdater() {
proxies.add(bean)
}
"anytls" -> {
val bean = AnyTLSBean()
for (opt in proxy) {
if (opt.value == null) continue
when (opt.key.replace("_", "-")) {
"name" -> bean.name = opt.value.toString()
"server" -> bean.serverAddress = opt.value as String
"port" -> bean.serverPort = opt.value.toString().toInt()
"password" -> bean.password = opt.value.toString()
"client-fingerprint" -> bean.utlsFingerprint =
opt.value as String
"sni" -> bean.sni = opt.value.toString()
"skip-cert-verify" -> bean.allowInsecure =
opt.value.toString() == "true"
"alpn" -> {
val alpn = (opt.value as? (List<String>))
bean.alpn = alpn?.joinToString("\n")
}
}
}
proxies.add(bean)
}
"hysteria" -> {
val bean = HysteriaBean()
bean.protocolVersion = 1
@ -731,9 +787,25 @@ object RawUpdater : GroupUpdater() {
}
json.has("outbounds") -> {
return listOf(ConfigBean().applyDefaultValues().apply {
config = json.toStringPretty()
})
return json.getJSONArray("outbounds")
.filterIsInstance<JSONObject>()
.mapNotNull {
val ty = it.getStr("type")
if (ty == null || ty == "" ||
ty == "dns" || ty == "block" || ty == "direct" || ty == "selector" || ty == "urltest"
) {
null
} else {
it
}
}.map {
ConfigBean().apply {
applyDefaultValues()
type = 1
config = it.toStringPretty()
name = it.getStr("tag")
}
}
}
json.has("server") && json.has("server_port") -> {

View File

@ -5,8 +5,11 @@ package io.nekohasekai.sagernet.ktx
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.content.*
import android.content.pm.PackageInfo
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.os.Build
import android.system.Os
@ -33,10 +36,17 @@ import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.database.preference.PublicDatabase
import io.nekohasekai.sagernet.ui.MainActivity
import io.nekohasekai.sagernet.ui.ThemedActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import moe.matsuri.nb4a.utils.NGUtil
import java.io.FileDescriptor
import java.net.*
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.Socket
import java.net.URLEncoder
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
@ -48,6 +58,7 @@ import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
fun String?.blankAsNull(): String? = if (isNullOrBlank()) null else this
inline fun <T> Iterable<T>.forEachTry(action: (T) -> Unit) {
var result: Exception? = null
@ -113,9 +124,6 @@ fun Context.listenForPackageChanges(onetime: Boolean = true, callback: () -> Uni
})
}
val PackageInfo.signaturesCompat
get() = if (Build.VERSION.SDK_INT >= 28) signingInfo.apkContentsSigners else @Suppress("DEPRECATION") signatures
/**
* Based on: https://stackoverflow.com/a/26348729/2245107
*/

View File

@ -107,7 +107,7 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
PackageCache.awaitLoadSync()
for ((_, pkg) in PackageCache.installedPluginPackages) {
try {
val pluginId = pkg.providers[0].loadString(Plugins.METADATA_KEY_ID)
val pluginId = pkg.providers?.get(0)?.loadString(Plugins.METADATA_KEY_ID)
if (pluginId.isNullOrBlank() || pluginId.startsWith(Plugins.AUTHORITIES_PREFIX_NEKO_PLUGIN)) continue
addItem(MaterialAboutActionItem.Builder()
.icon(R.drawable.ic_baseline_nfc_24)

View File

@ -139,9 +139,9 @@ class AppListActivity : ThemedActivity() {
var filteredApps = apps
suspend fun reload() {
apps = getCachedApps().map { (packageName, packageInfo) ->
apps = getCachedApps().mapNotNull { (packageName, packageInfo) ->
coroutineContext[Job]!!.ensureActive()
ProxiedApp(packageManager, packageInfo.applicationInfo, packageName)
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
}.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
}
@ -200,8 +200,11 @@ class AppListActivity : ThemedActivity() {
private fun initProxiedUids(str: String = DataStore.routePackages) {
proxiedUids.clear()
val apps = getCachedApps()
for (line in str.lineSequence()) proxiedUids[(apps[line]
?: continue).applicationInfo.uid] = true
for (line in str.lineSequence()) {
val app = (apps[line] ?: continue)
val uid = app.applicationInfo?.uid ?: continue
proxiedUids[uid] = true
}
}
private fun isProxiedApp(app: ProxiedApp) = proxiedUids[app.uid]
@ -306,6 +309,7 @@ class AppListActivity : ThemedActivity() {
return true
}
R.id.action_clear_selections -> {
runOnDefaultDispatcher {
proxiedUids.clear()
@ -316,6 +320,7 @@ class AppListActivity : ThemedActivity() {
}
}
}
R.id.action_export_clipboard -> {
val success = SagerNet.trySetPrimaryClip("false\n${DataStore.routePackages}")
Snackbar.make(
@ -325,6 +330,7 @@ class AppListActivity : ThemedActivity() {
).show()
return true
}
R.id.action_import_clipboard -> {
val proxiedAppString =
SagerNet.clipboard.primaryClip?.getItemAt(0)?.text?.toString()
@ -344,6 +350,7 @@ class AppListActivity : ThemedActivity() {
}
Snackbar.make(binding.list, R.string.action_import_err, Snackbar.LENGTH_LONG).show()
}
R.id.uninstall_all -> {
runOnDefaultDispatcher {
proxiedUids.clear()

View File

@ -103,9 +103,9 @@ class AppManagerActivity : ThemedActivity() {
var filteredApps = apps
suspend fun reload() {
apps = cachedApps.map { (packageName, packageInfo) ->
apps = cachedApps.mapNotNull { (packageName, packageInfo) ->
coroutineContext[Job]!!.ensureActive()
ProxiedApp(packageManager, packageInfo.applicationInfo, packageName)
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
}.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
}
@ -164,8 +164,11 @@ class AppManagerActivity : ThemedActivity() {
private fun initProxiedUids(str: String = DataStore.individual) {
proxiedUids.clear()
val apps = cachedApps
for (line in str.lineSequence()) proxiedUids[(apps[line]
?: continue).applicationInfo.uid] = true
for (line in str.lineSequence()) {
val app = (apps[line] ?: continue)
val uid = app.applicationInfo?.uid ?: continue
proxiedUids[uid] = true
}
}
private fun isProxiedApp(app: ProxiedApp) = proxiedUids[app.uid]
@ -325,14 +328,19 @@ class AppManagerActivity : ThemedActivity() {
proxiedUids.clear()
for (app in cachedApps) {
val needProxy =
needProxyAppsList.contains(app.key) || app.value.applicationInfo.uid == 1000
needProxyAppsList.contains(app.key) || (app.value.applicationInfo?.uid
?: 0) == 1000
if (needProxy) {
if (!bypass) {
proxiedUids[app.value.applicationInfo.uid] = true
app.value.applicationInfo?.apply {
proxiedUids[uid] = true
}
}
} else {
if (bypass) {
proxiedUids[app.value.applicationInfo.uid] = true
app.value.applicationInfo?.apply {
proxiedUids[uid] = true
}
}
}
}

View File

@ -18,6 +18,7 @@ import io.nekohasekai.sagernet.databinding.LayoutAssetsBinding
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.widget.UndoSnackbarManager
import libcore.Libcore
import moe.matsuri.nb4a.utils.Util
import org.json.JSONObject
import java.io.File
import java.io.FileWriter
@ -283,7 +284,7 @@ class AssetsActivity : ThemedActivity() {
setURL("https://api.github.com/repos/$repo/releases/latest")
}.execute()
val release = JSONObject(response.contentString)
val release = JSONObject(Util.getStringBox(response.contentString))
val tagName = release.optString("tag_name")
if (tagName == localVersion) {

View File

@ -57,6 +57,7 @@ import kotlinx.coroutines.sync.withLock
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.Protocols.getProtocolColor
import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity
import moe.matsuri.nb4a.proxy.config.ConfigSettingActivity
import moe.matsuri.nb4a.proxy.neko.NekoJSInterface
import moe.matsuri.nb4a.proxy.neko.NekoSettingActivity
@ -148,7 +149,7 @@ class ConfigurationFragment @JvmOverloads constructor(
searchView.setOnQueryTextListener(this)
searchView.maxWidth = Int.MAX_VALUE
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
cancelSearch(searchView)
}
@ -390,6 +391,10 @@ class ConfigurationFragment @JvmOverloads constructor(
startActivity(Intent(requireActivity(), ShadowTLSSettingsActivity::class.java))
}
R.id.action_new_anytls -> {
startActivity(Intent(requireActivity(), AnyTLSSettingsActivity::class.java))
}
R.id.action_new_config -> {
startActivity(Intent(requireActivity(), ConfigSettingActivity::class.java))
}
@ -1711,9 +1716,9 @@ class ConfigurationFragment @JvmOverloads constructor(
}
}
private fun cancelSearch(searchView: SearchView) {
searchView.onActionViewCollapsed()
searchView.clearFocus()
}
private fun cancelSearch(searchView: SearchView) {
searchView.onActionViewCollapsed()
searchView.clearFocus()
}
}

View File

@ -49,7 +49,7 @@ class RouteSettingsActivity(
fun init(packageName: String?) {
RuleEntity().apply {
if (!packageName.isNullOrBlank()) {
packages = listOf(packageName)
packages = setOf(packageName)
name = app.getString(R.string.route_for, PackageCache.loadLabel(packageName))
}
}.init()
@ -89,7 +89,7 @@ class RouteSettingsActivity(
2 -> -2L
else -> DataStore.routeOutboundRule
}
packages = DataStore.routePackages.split("\n").filter { it.isNotBlank() }
packages = DataStore.routePackages.split("\n").filter { it.isNotBlank() }.toSet()
if (DataStore.editingId == 0L) {
enabled = true

View File

@ -17,7 +17,6 @@ import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.utils.Theme
import io.nekohasekai.sagernet.widget.AppListPreference
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.ui.*
class SettingsPreferenceFragment : PreferenceFragmentCompat() {
@ -81,7 +80,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
val ipv6Mode = findPreference<Preference>(Key.IPV6_MODE)!!
val trafficSniffing = findPreference<Preference>(Key.TRAFFIC_SNIFFING)!!
val muxConcurrency = findPreference<EditTextPreference>(Key.MUX_CONCURRENCY)!!
val tcpKeepAliveInterval = findPreference<EditTextPreference>(Key.TCP_KEEP_ALIVE_INTERVAL)!!
tcpKeepAliveInterval.isVisible = false
@ -123,16 +121,7 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
true
}
val muxProtocols = findPreference<MultiSelectListPreference>(Key.MUX_PROTOCOLS)!!
muxProtocols.apply {
val e = Protocols.getCanMuxList().toTypedArray()
entries = e
entryValues = e
}
portLocalDns.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
muxConcurrency.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
mixedPort.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
val metedNetwork = findPreference<Preference>(Key.METERED_NETWORK)!!
@ -175,7 +164,6 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
appendHttpProxy.onPreferenceChangeListener = reloadListener
showDirectSpeed.onPreferenceChangeListener = reloadListener
trafficSniffing.onPreferenceChangeListener = reloadListener
muxConcurrency.onPreferenceChangeListener = reloadListener
tcpKeepAliveInterval.onPreferenceChangeListener = reloadListener
bypassLan.onPreferenceChangeListener = reloadListener
bypassLanInCore.onPreferenceChangeListener = reloadListener

View File

@ -7,6 +7,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.VpnService
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContract
@ -26,7 +27,15 @@ class VpnRequestActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
receiver = broadcastReceiver { _, _ -> connect.launch(null) }
registerReceiver(receiver, IntentFilter(Intent.ACTION_USER_PRESENT))
if (SDK_INT >= 33) {
registerReceiver(
receiver,
IntentFilter(Intent.ACTION_USER_PRESENT),
Context.RECEIVER_EXPORTED
)
} else {
registerReceiver(receiver, IntentFilter(Intent.ACTION_USER_PRESENT))
}
} else connect.launch(null)
}

View File

@ -45,10 +45,13 @@ abstract class StandardV2RaySettingsActivity : ProfileSettingsActivity<StandardV
private val realityShortId = pbm.add(PreferenceBinding(Type.Text, "realityShortId"))
private val enableECH = pbm.add(PreferenceBinding(Type.Bool, "enableECH"))
private val enablePqSignature = pbm.add(PreferenceBinding(Type.Bool, "enablePqSignature"))
private val disabledDRS = pbm.add(PreferenceBinding(Type.Bool, "disabledDRS"))
private val echConfig = pbm.add(PreferenceBinding(Type.Text, "echConfig"))
private val enableMux = pbm.add(PreferenceBinding(Type.Bool, "enableMux"))
private val muxPadding = pbm.add(PreferenceBinding(Type.Bool, "muxPadding"))
private val muxType = pbm.add(PreferenceBinding(Type.TextToInt, "muxType"))
private val muxConcurrency = pbm.add(PreferenceBinding(Type.TextToInt, "muxConcurrency"))
override fun StandardV2RayBean.init() {
if (this is TrojanBean) {
this@StandardV2RaySettingsActivity.uuid.fieldName = "password"
@ -160,6 +163,7 @@ abstract class StandardV2RaySettingsActivity : ProfileSettingsActivity<StandardV
host.preference.isVisible = true
path.preference.isVisible = true
}
"ws" -> {
host.preference.setTitle(R.string.ws_host)
path.preference.setTitle(R.string.ws_path)

View File

@ -9,6 +9,7 @@ import io.nekohasekai.sagernet.utils.cf.RegisterRequest
import io.nekohasekai.sagernet.utils.cf.UpdateDeviceRequest
import libcore.Libcore
import moe.matsuri.nb4a.utils.JavaUtil.gson
import moe.matsuri.nb4a.utils.Util
// kang from wgcf
object Cloudflare {
@ -37,8 +38,9 @@ object Cloudflare {
setUserAgent("okhttp/3.12.1")
}.execute()
Logs.d(response.contentString)
val device = gson.fromJson(response.contentString, DeviceResponse::class.java)
Logs.d(Util.getStringBox(response.contentString))
val device =
gson.fromJson(Util.getStringBox(response.contentString), DeviceResponse::class.java)
val accessToken = device.token
client.newRequest().apply {

View File

@ -2,43 +2,14 @@ package moe.matsuri.nb4a
import android.content.Context
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity.Companion.TYPE_NEKO
import io.nekohasekai.sagernet.fmt.AbstractBean
import io.nekohasekai.sagernet.fmt.v2ray.StandardV2RayBean
import io.nekohasekai.sagernet.fmt.v2ray.isTLS
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.getColorAttr
import moe.matsuri.nb4a.plugin.NekoPluginManager
import moe.matsuri.nb4a.proxy.config.ConfigBean
// Settings for all protocols, built-in or plugin
object Protocols {
// Mux
fun isProfileNeedMux(bean: StandardV2RayBean): Boolean {
return when (bean.type) {
"tcp", "ws" -> true
"http" -> !bean.isTLS()
else -> false
}
}
fun shouldEnableMux(protocol: String): Boolean {
return DataStore.muxProtocols.contains(protocol)
}
fun getCanMuxList(): List<String> {
// built-in and support mux
val list = mutableListOf("vmess", "trojan", "trojan-go", "shadowsocks", "vless", "padding")
NekoPluginManager.getProtocols().forEach {
if (it.protocolConfig.optBoolean("canMux")) {
list.add(it.protocolId)
}
}
return list
}
// Deduplication
@ -47,6 +18,9 @@ object Protocols {
) {
fun hash(): String {
if (bean is ConfigBean) {
return bean.config
}
return bean.serverAddress + bean.serverPort + type
}

View File

@ -449,9 +449,9 @@ public class SingBoxOptions {
public OutboundTLSOptions tls;
public String hop_ports;
public String server_ports;
public Integer hop_interval;
public String hop_interval;
}
@ -575,9 +575,9 @@ public class SingBoxOptions {
public OutboundTLSOptions tls;
public String hop_ports;
public String server_ports;
public Integer hop_interval;
public String hop_interval;
}
@ -1879,10 +1879,6 @@ public class SingBoxOptions {
public Boolean enabled;
public Boolean pq_signature_schemes_enabled;
public Boolean dynamic_record_sizing_disabled;
// Generate note: Listable
public List<String> key;
@ -1894,10 +1890,6 @@ public class SingBoxOptions {
public Boolean enabled;
public Boolean pq_signature_schemes_enabled;
public Boolean dynamic_record_sizing_disabled;
// Generate note: Listable
public List<String> config;
@ -3910,9 +3902,9 @@ public class SingBoxOptions {
public OutboundTLSOptions tls;
public String hop_ports;
public String server_ports;
public Integer hop_interval;
public String hop_interval;
}
@ -4284,9 +4276,9 @@ public class SingBoxOptions {
public OutboundTLSOptions tls;
public String hop_ports;
public String server_ports;
public Integer hop_interval;
public String hop_interval;
}
@ -4343,7 +4335,6 @@ public class SingBoxOptions {
public Boolean source_ip_is_private;
public Boolean rule_set_ipcidr_match_source;
public Boolean ip_is_private;
// Generate note: Listable
@ -4517,4 +4508,57 @@ public class SingBoxOptions {
}
// sing-box Options 生成器已经坏了以下是从 husi 抄的
public static class Outbound_AnyTLSOptions extends Outbound {
// Generate note: nested type DialerOptions
public String detour;
public String bind_interface;
public String inet4_bind_address;
public String inet6_bind_address;
public String protect_path;
public Integer routing_mark;
public Boolean reuse_addr;
public String connect_timeout;
public Boolean tcp_fast_open;
public Boolean tcp_multi_path;
public Boolean udp_fragment;
public String domain_strategy;
public String network_strategy;
public List<String> network_type;
public List<String> fallback_network_type;
public String fallback_delay;
// Generate note: nested type ServerOptions
public String server;
public Integer server_port;
// Generate note: nested type OutboundTLSOptionsContainer
public OutboundTLSOptions tls;
public String password;
public String idle_session_check_interval;
public String idle_session_timeout;
}
}

View File

@ -113,7 +113,6 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP:
if (isIP) {
if (it.startsWith("geoip:")) {
rule_set.plusAssign(it)
rule_set_ipcidr_match_source = false
} else {
ip_cidr.plusAssign(it)
}
@ -130,8 +129,7 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP:
} else if (it.startsWith("keyword:")) {
domain_keyword.plusAssign(it.removePrefix("keyword:").lowercase())
} else {
// https://github.com/SagerNet/sing-box/commit/5d41e328d4a9f7549dd27f11b4ccc43710a73664
domain.plusAssign(it.lowercase())
domain_suffix.plusAssign(it.lowercase())
}
}
ip_cidr?.removeIf { it.isNullOrBlank() }

View File

@ -22,8 +22,8 @@ object Plugins {
const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plugin.executable_path"
fun isExeOrPlugin(pkg: PackageInfo): Boolean {
if (pkg.providers == null || pkg.providers.isEmpty()) return false
val provider = pkg.providers[0] ?: return false
if (pkg.providers?.isEmpty() == true) return false
val provider = pkg.providers?.get(0) ?: return false
val auth = provider.authority ?: return false
return auth.startsWith(AUTHORITIES_PREFIX_SEKAI_EXE)
|| auth.startsWith(AUTHORITIES_PREFIX_NEKO_EXE)
@ -92,8 +92,8 @@ object Plugins {
PackageCache.awaitLoadSync()
val pkgs = PackageCache.installedPluginPackages
.map { it.value }
.filter { it.providers[0].loadString(METADATA_KEY_ID) == pluginId }
return pkgs.map { it.providers[0] }
.filter { it.providers?.get(0)?.loadString(METADATA_KEY_ID) == pluginId }
return pkgs.mapNotNull { it.providers?.get(0) }
}
private fun buildUri(id: String, auth: String) = Uri.Builder()

View File

@ -0,0 +1,82 @@
package moe.matsuri.nb4a.proxy.anytls;
import androidx.annotation.NonNull;
import com.esotericsoftware.kryo.io.ByteBufferInput;
import com.esotericsoftware.kryo.io.ByteBufferOutput;
import org.jetbrains.annotations.NotNull;
import io.nekohasekai.sagernet.fmt.AbstractBean;
import io.nekohasekai.sagernet.fmt.KryoConverters;
public class AnyTLSBean extends AbstractBean {
public static final Creator<AnyTLSBean> CREATOR = new CREATOR<AnyTLSBean>() {
@NonNull
@Override
public AnyTLSBean newInstance() {
return new AnyTLSBean();
}
@Override
public AnyTLSBean[] newArray(int size) {
return new AnyTLSBean[size];
}
};
public String password;
public String sni;
public String alpn;
public String certificates;
public String utlsFingerprint;
public Boolean allowInsecure;
// In sing-box, this seemed can be used with REALITY.
// But even mihomo appended many options, it still not provide REALITY.
// https://github.com/anytls/anytls-go/blob/4636d90462fa21a510420512d7706a9acf69c7b9/docs/faq.md?plain=1#L25-L37
public String echConfig;
@Override
public void initializeDefaultValues() {
super.initializeDefaultValues();
if (password == null) password = "";
if (sni == null) sni = "";
if (alpn == null) alpn = "";
if (certificates == null) certificates = "";
if (utlsFingerprint == null) utlsFingerprint = "";
if (allowInsecure == null) allowInsecure = false;
if (echConfig == null) echConfig = "";
}
@Override
public void serialize(ByteBufferOutput output) {
output.writeInt(0);
super.serialize(output);
output.writeString(password);
output.writeString(sni);
output.writeString(alpn);
output.writeString(certificates);
output.writeString(utlsFingerprint);
output.writeBoolean(allowInsecure);
output.writeString(echConfig);
}
@Override
public void deserialize(ByteBufferInput input) {
int version = input.readInt();
super.deserialize(input);
password = input.readString();
sni = input.readString();
alpn = input.readString();
certificates = input.readString();
utlsFingerprint = input.readString();
allowInsecure = input.readBoolean();
echConfig = input.readString();
}
@NotNull
@Override
public AnyTLSBean clone() {
return KryoConverters.deserialize(new AnyTLSBean(), KryoConverters.serialize(this));
}
}

View File

@ -0,0 +1,37 @@
package moe.matsuri.nb4a.proxy.anytls
import io.nekohasekai.sagernet.ktx.blankAsNull
import moe.matsuri.nb4a.SingBoxOptions
import moe.matsuri.nb4a.utils.listByLineOrComma
fun buildSingBoxOutboundAnyTLSBean(bean: AnyTLSBean): SingBoxOptions.Outbound_AnyTLSOptions {
return SingBoxOptions.Outbound_AnyTLSOptions().apply {
type = "anytls"
server = bean.serverAddress
server_port = bean.serverPort
password = bean.password
tls = SingBoxOptions.OutboundTLSOptions().apply {
enabled = true
server_name = bean.sni.blankAsNull()
if (bean.allowInsecure) insecure = true
alpn = bean.alpn.blankAsNull()?.listByLineOrComma()
bean.certificates.blankAsNull()?.let {
certificate = it
}
bean.utlsFingerprint.blankAsNull()?.let {
utls = SingBoxOptions.OutboundUTLSOptions().apply {
enabled = true
fingerprint = it
}
}
bean.echConfig.blankAsNull()?.let {
// In new version, some complex options will be deprecated, so we just do this.
ech = SingBoxOptions.OutboundECHOptions().apply {
enabled = true
config = listOf(it)
}
}
}
}
}

View File

@ -0,0 +1,51 @@
package moe.matsuri.nb4a.proxy.anytls
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.preference.EditTextPreferenceModifiers
import io.nekohasekai.sagernet.ktx.applyDefaultValues
import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity
import moe.matsuri.nb4a.proxy.PreferenceBinding
import moe.matsuri.nb4a.proxy.PreferenceBindingManager
import moe.matsuri.nb4a.proxy.Type
class AnyTLSSettingsActivity : ProfileSettingsActivity<AnyTLSBean>() {
override fun createEntity() = AnyTLSBean().applyDefaultValues()
private val pbm = PreferenceBindingManager()
private val name = pbm.add(PreferenceBinding(Type.Text, "name"))
private val serverAddress = pbm.add(PreferenceBinding(Type.Text, "serverAddress"))
private val serverPort = pbm.add(PreferenceBinding(Type.TextToInt, "serverPort"))
private val password = pbm.add(PreferenceBinding(Type.Text, "password"))
private val sni = pbm.add(PreferenceBinding(Type.Text, "sni"))
private val alpn = pbm.add(PreferenceBinding(Type.Text, "alpn"))
private val certificates = pbm.add(PreferenceBinding(Type.Text, "certificates"))
private val allowInsecure = pbm.add(PreferenceBinding(Type.Bool, "allowInsecure"))
private val utlsFingerprint = pbm.add(PreferenceBinding(Type.Text, "utlsFingerprint"))
override fun AnyTLSBean.init() {
pbm.writeToCacheAll(this)
}
override fun AnyTLSBean.serialize() {
pbm.fromCacheAll(this)
}
override fun PreferenceFragmentCompat.createPreferences(
savedInstanceState: Bundle?,
rootKey: String?
) {
addPreferencesFromResource(R.xml.anytls_preferences)
findPreference<EditTextPreference>(Key.SERVER_PORT)!!.apply {
setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
}
findPreference<EditTextPreference>("password")!!.apply {
summaryProvider = PasswordSummaryProvider
}
}
}

View File

@ -65,8 +65,8 @@ suspend fun NekoBean.updateAllConfig(port: Int) = suspendCoroutine<Unit> {
val otherArgs = mutableMapOf<String, Any>()
otherArgs["finalAddress"] = finalAddress
otherArgs["finalPort"] = finalPort
otherArgs["muxEnabled"] = Protocols.shouldEnableMux(protocolId)
otherArgs["muxConcurrency"] = DataStore.muxConcurrency
// otherArgs["muxEnabled"] = Protocols.shouldEnableMux(protocolId)
// otherArgs["muxConcurrency"] = DataStore.muxConcurrency
val ret = jsip.buildAllConfig(port, this@updateAllConfig, otherArgs)

View File

@ -3,6 +3,7 @@ package moe.matsuri.nb4a.utils
import android.annotation.SuppressLint
import android.content.Context
import android.util.Base64
import libcore.StringBox
import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.*
@ -181,4 +182,10 @@ object Util {
}
}
fun getStringBox(b: StringBox?): String {
if (b != null && b.value != null) {
return b.value
}
return ""
}
}

View File

@ -57,15 +57,18 @@
<item
android:id="@+id/action_new_tuic"
android:title="@string/action_tuic" />
<item
android:id="@+id/action_new_shadowtls"
android:title="@string/action_shadowtls" />
<item
android:id="@+id/action_new_anytls"
android:title="@string/action_anytls" />
<item
android:id="@+id/action_new_ssh"
android:title="@string/action_ssh" />
<item
android:id="@+id/action_new_wg"
android:title="@string/action_wireguard" />
<item
android:id="@+id/action_new_shadowtls"
android:title="@string/action_shadowtls" />
<item
android:id="@+id/action_new_config"
android:title="@string/custom_config" />

View File

@ -0,0 +1 @@
unqualifiedResLocale=en-US

View File

@ -482,9 +482,6 @@
<string name="enable_ech">启用 ECH 技术支持</string>
<string name="enable_ech_sum">启用 ECH</string>
<string name="ech_settings">ECH 设置</string>
<string name="pq_signature_schemes_enabled">启用对后量子对等证书签名方案支持</string>
<string name="dynamic_record_sizing_disabled">禁用TLS记录的自适应调整大小</string>
<string name="dynamic_record_sizing_sum">如果启用将始终使用最大可能的TLS记录大小。当禁用时TLS记录的大小可能会进行调整以尝试改善延迟。</string>
<string name="ech_config">ECH 配置</string>
<string name="http_upgrade_host">HTTPUpgrade 主机</string>
<string name="http_upgrade_path">HTTPUpgrade 路径</string>

View File

@ -45,7 +45,7 @@
<string name="auto_connect_summary">在開機或本程式更新後將自動重新啓用代理</string>
<string name="delete">移除</string>
<string name="enable">啟用</string>
<string name="donate_info">世界好得意 請比世界</string>
<string name="donate_info">貓貓好得意 請比貓貓</string>
<string name="route_bypass">繞過</string>
<string name="route_block">封鎖</string>
<string name="add_profile_methods_scan_qr_code">掃描二維碼</string>

View File

@ -284,7 +284,7 @@
<string name="connection_test_icmp_ping_unavailable">ICMPing 無法使用</string>
<string name="connection_test_domain_not_found">網域不存在</string>
<string name="connection_test_refused">連線被拒</string>
<string name="donate_info">世界很可愛 請給世界</string>
<string name="donate_info">貓貓很可愛 請給貓貓</string>
<string name="connection_test_unreachable">無法連線</string>
<string name="connection_test_timeout">逾時</string>
<string name="append_http_proxy">為 VPN 附加 HTTP 代理</string>

View File

@ -216,7 +216,7 @@
<string name="action_trojan_go" translatable="false">Trojan Go</string>
<string name="action_mieru" translatable="false">Mieru</string>
<string name="action_naive" translatable="false">Naïve</string>
<string name="action_ping_tunnel" translatable="false">Ping Tunnel</string>
<string name="action_anytls" translatable="false">AnyTLS</string>
<string name="action_hysteria" translatable="false">Hysteria</string>
<string name="action_ssh" translatable="false">SSH</string>
<string name="action_wireguard" translatable="false">WireGuard</string>
@ -560,13 +560,7 @@
<string name="enable_ech">Enable ECH</string>
<string name="enable_ech_sum">Enable Encrypted Client Hello</string>
<string name="ech_settings">ECH Settings</string>
<string name="pq_signature_schemes_enabled">Enable post-quantum peer certificate signature
supports</string>
<string name="dynamic_record_sizing_disabled">Disables adaptive sizing of TLS records</string>
<string name="ech_config">ECH Config</string>
<string name="dynamic_record_sizing_sum">If enable, the largest possible TLS record size is
always used. When disable, the size of TLS records may be adjusted in an attempt to improve
latency.</string>
<string name="http_upgrade_host">HTTPUpgrade Host</string>
<string name="http_upgrade_path">HTTPUpgrade Path</string>
<string name="update_current_subscription">Update current Group\'s subscription</string>
@ -574,4 +568,6 @@
<string name="allow_insecure_on_request_sum">Disable certificate checking when updating
subscriptions</string>
<string name="global_allow_insecure">Always allow insecure</string>
<string name="mux_preference">Mulitplex</string>
<string name="padding">Padding</string>
</resources>

View File

@ -0,0 +1,57 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
app:icon="@drawable/ic_social_emoji_symbols"
app:key="profileName"
app:title="@string/profile_name"
app:useSimpleSummaryProvider="true" />
<PreferenceCategory app:title="@string/proxy_cat">
<EditTextPreference
app:icon="@drawable/ic_hardware_router"
app:key="serverAddress"
app:title="@string/server_address"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:icon="@drawable/ic_maps_directions_boat"
app:key="serverPort"
app:title="@string/server_port"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:icon="@drawable/ic_settings_password"
app:key="password"
app:title="@string/password" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/security_settings">
<EditTextPreference
app:icon="@drawable/ic_action_copyright"
app:key="sni"
app:title="@string/sni"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
app:icon="@drawable/ic_notification_enhanced_encryption"
app:key="allowInsecure"
app:title="@string/allow_insecure" />
<EditTextPreference
app:icon="@drawable/ic_baseline_legend_toggle_24"
app:key="alpn"
app:title="@string/alpn"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:icon="@drawable/ic_baseline_vpn_key_24"
app:key="certificates"
app:title="@string/certificates"
app:useSimpleSummaryProvider="true" />
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue=""
app:entries="@array/utls_fingerprint_entry"
app:entryValues="@array/utls_fingerprint_entry"
app:icon="@drawable/ic_baseline_fingerprint_24"
app:key="utlsFingerprint"
app:title="@string/utls_fingerprint"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -2,10 +2,6 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="@string/general_settings">
<io.nekohasekai.sagernet.widget.AppListPreference
app:icon="@drawable/ic_baseline_android_24"
app:key="nekoPlugins"
app:title="@string/neko_plugin" />
<SwitchPreference
app:defaultValue="false"
app:icon="@drawable/ic_communication_phonelink_ring"
@ -88,6 +84,10 @@
app:key="logLevel"
app:title="@string/log_level"
app:useSimpleSummaryProvider="true" />
<io.nekohasekai.sagernet.widget.AppListPreference
app:icon="@drawable/ic_baseline_android_24"
app:key="nekoPlugins"
app:title="@string/neko_plugin" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/cag_route">
@ -134,34 +134,6 @@
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/protocol_settings">
<MultiSelectListPreference
app:defaultValue="@array/mux_select_init"
app:entries="@array/mux_select_init"
app:entryValues="@array/mux_select_init"
app:icon="@drawable/ic_baseline_compare_arrows_24"
app:key="mux"
app:summary="@string/mux_sum"
app:title="@string/enable_mux" />
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/mux_type"
app:entryValues="@array/int_array_3"
app:key="muxType"
app:title="@string/mux_type"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:defaultValue="8"
app:icon="@drawable/ic_baseline_low_priority_24"
app:key="muxConcurrency"
app:title="@string/mux_concurrency"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
app:key="globalAllowInsecure"
app:icon="@drawable/ic_action_lock_open"
app:title="@string/global_allow_insecure" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/cag_dns">
<EditTextPreference
app:defaultValue="https://dns.google/dns-query"
@ -177,7 +149,7 @@
app:title="@string/domain_strategy_for_remote"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:defaultValue="https://120.53.53.53/dns-query"
app:defaultValue="https://223.5.5.5/dns-query"
app:icon="@drawable/ic_action_dns"
app:key="directDns"
app:title="@string/direct_dns"
@ -262,10 +234,14 @@
app:title="@string/app_tls_version"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
app:key="showBottomBar"
app:title="@string/show_bottom_bar" />
app:icon="@drawable/ic_action_lock_open"
app:key="globalAllowInsecure"
app:title="@string/global_allow_insecure" />
<SwitchPreference
app:key="allowInsecureOnRequest"
app:title="@string/allow_insecure_on_request_sum" />
<SwitchPreference
app:key="showBottomBar"
app:title="@string/show_bottom_bar" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -118,11 +118,6 @@
app:key="allowInsecure"
app:summary="@string/allow_insecure_sum"
app:title="@string/allow_insecure" />
<SwitchPreference
app:icon="@drawable/ic_baseline_security_24"
app:key="enableECH"
app:summary="@string/enable_ech_sum"
app:title="@string/enable_ech" />
</PreferenceCategory>
<PreferenceCategory
@ -149,19 +144,41 @@
</PreferenceCategory>
<PreferenceCategory
app:key="serverECHCategory"
app:title="@string/ech_settings">
app:key="serverMuxCategory"
app:title="@string/mux_preference">
<SwitchPreference
app:icon="@drawable/ic_baseline_security_24"
app:key="enablePqSignature"
app:title="@string/pq_signature_schemes_enabled" />
<SwitchPreference
app:icon="@drawable/ic_baseline_security_24"
app:key="disabledDRS"
app:summary="@string/dynamic_record_sizing_sum"
app:title="@string/dynamic_record_sizing_disabled" />
app:icon="@drawable/ic_baseline_compare_arrows_24"
app:key="enableMux"
app:summary="@string/mux_sum"
app:title="@string/enable_mux" />
<moe.matsuri.nb4a.ui.SimpleMenuPreference
app:defaultValue="0"
app:entries="@array/mux_type"
app:entryValues="@array/int_array_3"
app:key="muxType"
app:title="@string/mux_type"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:icon="@drawable/ic_baseline_texture_24"
app:defaultValue="1"
app:icon="@drawable/ic_baseline_low_priority_24"
app:key="muxConcurrency"
app:title="@string/mux_concurrency"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
app:icon="@drawable/baseline_developer_board_24"
app:key="muxPadding"
app:title="@string/padding" />
</PreferenceCategory>
<PreferenceCategory
app:key="serverECHCategory"
app:title="ECH">
<SwitchPreference
app:icon="@drawable/ic_baseline_security_24"
app:key="enableECH"
app:title="@string/enable" />
<EditTextPreference
app:icon="@drawable/ic_baseline_nfc_24"
app:key="echConfig"
app:title="@string/ech_config"
app:useSimpleSummaryProvider="true" />

View File

@ -6,3 +6,7 @@ allprojects {
tasks.register<Delete>("clean") {
delete(rootProject.buildDir)
}
plugins {
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
}

View File

@ -6,10 +6,6 @@ ENV_NB4A=1
source "buildScript/lib/core/get_source_env.sh"
pushd ..
######
## From nekoray/libs/get_source.sh
######
####
if [ ! -d "sing-box" ]; then
@ -17,17 +13,6 @@ if [ ! -d "sing-box" ]; then
fi
pushd sing-box
git checkout "$COMMIT_SING_BOX"
popd
####
if [ ! -d "sing-quic" ]; then
git clone --no-checkout https://github.com/MatsuriDayo/sing-quic.git
fi
pushd sing-quic
git checkout "$COMMIT_SING_QUIC"
popd
####
@ -37,7 +22,6 @@ if [ ! -d "libneko" ]; then
fi
pushd libneko
git checkout "$COMMIT_LIBNEKO"
popd
####

View File

@ -1,3 +1,2 @@
export COMMIT_SING_BOX="06557f6cef23160668122a17a818b378b5a216b5"
export COMMIT_SING_QUIC="b49ce60d9b3622d5238fee96bfd3c5f6e3915b42"
export COMMIT_SING_BOX="76802b787333ad72bc1db2de79ea69993b79d892"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"

View File

@ -7,6 +7,6 @@ apply(from = "../repositories.gradle.kts")
dependencies {
// Gradle Plugins
implementation("com.android.tools.build:gradle:7.4.2")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
implementation("com.android.tools.build:gradle:8.8.1")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21")
}

View File

@ -1,25 +1,15 @@
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.dsl.Lint
import com.android.build.gradle.AbstractAppExtension
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.getByName
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import java.security.MessageDigest
import java.util.*
import java.util.Base64
import java.util.Properties
import kotlin.system.exitProcess
fun sha256Hex(bytes: ByteArray): String {
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(bytes)
return digest.fold("") { str, it -> str + "%02x".format(it) }
}
private val Project.android get() = extensions.getByName<ApplicationExtension>("android")
private lateinit var metadata: Properties
@ -76,29 +66,13 @@ fun Project.requireLocalProperties(): Properties {
return localProperties
}
fun Project.requireTargetAbi(): String {
var targetAbi = ""
if (gradle.startParameter.taskNames.isNotEmpty()) {
if (gradle.startParameter.taskNames.size == 1) {
val targetTask = gradle.startParameter.taskNames[0].toLowerCase(Locale.ROOT).trim()
when {
targetTask.contains("arm64") -> targetAbi = "arm64-v8a"
targetTask.contains("arm") -> targetAbi = "armeabi-v7a"
targetTask.contains("x64") -> targetAbi = "x86_64"
targetTask.contains("x86") -> targetAbi = "x86"
}
}
}
return targetAbi
}
fun Project.setupCommon() {
android.apply {
buildToolsVersion = "30.0.3"
compileSdk = 33
buildToolsVersion = "35.0.1"
compileSdk = 35
defaultConfig {
minSdk = 21
targetSdk = 33
targetSdk = 35
}
buildTypes {
getByName("release") {
@ -120,7 +94,7 @@ fun Project.setupCommon() {
textOutput = project.file("build/lint.txt")
htmlOutput = project.file("build/lint.html")
}
packagingOptions {
packaging {
resources.excludes.addAll(
listOf(
"**/*.kotlin_*",
@ -137,9 +111,6 @@ fun Project.setupCommon() {
)
)
}
packagingOptions {
jniLibs.useLegacyPackaging = true
}
(this as? AbstractAppExtension)?.apply {
buildTypes {
getByName("release") {
@ -191,9 +162,7 @@ fun Project.setupAppCommon() {
buildTypes {
val key = signingConfigs.findByName("release")
if (key != null) {
if (requireTargetAbi().isBlank()) {
getByName("release").signingConfig = key
}
getByName("release").signingConfig = key
getByName("debug").signingConfig = key
}
}
@ -213,8 +182,6 @@ fun Project.setupApp() {
}
setupAppCommon()
val targetAbi = requireTargetAbi()
android.apply {
this as AbstractAppExtension
@ -228,12 +195,13 @@ fun Project.setupApp() {
}
splits.abi {
reset()
isEnable = true
isUniversalApk = false
if (targetAbi.isNotBlank()) {
reset()
include(targetAbi)
}
include("armeabi-v7a")
include("arm64-v8a")
include("x86")
include("x86_64")
}
flavorDimensions += "vendor"

View File

@ -21,3 +21,5 @@ android.enableJetifier=true
kotlin.code.style=official
# Gradle parallel build
org.gradle.parallel=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

1
libcore/.gitignore vendored
View File

@ -5,3 +5,4 @@ binary*.go
/debug.go
build
.build
env_*.sh

View File

@ -10,21 +10,27 @@ import (
"runtime"
"runtime/debug"
"strings"
"sync"
"github.com/matsuridayo/libneko/protect_server"
"github.com/matsuridayo/libneko/speedtest"
"github.com/sagernet/sing-box/boxapi"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/protocol/group"
box "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause"
)
func init() {
dialer.DoNotSelectInterface = true
}
var mainInstance *BoxInstance
func VersionBox() string {
@ -59,36 +65,37 @@ func ResetAllConnections(system bool) {
}
type BoxInstance struct {
access sync.Mutex
*box.Box
cancel context.CancelFunc
state int
v2api *boxapi.SbV2rayServer
selector *outbound.Selector
selector *group.Selector
pauseManager pause.Manager
ForTest bool
}
func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
defer device.DeferPanicToError("NewSingBoxInstance", func(err_ error) { err = err_ })
// create box context
ctx, cancel := context.WithCancel(context.Background())
ctx = box.Context(ctx, nekoboxAndroidInboundRegistry(), nekoboxAndroidOutboundRegistry(), nekoboxAndroidEndpointRegistry())
ctx = service.ContextWithDefaultRegistry(ctx)
service.MustRegister[platform.Interface](ctx, boxPlatformInterfaceInstance)
// parse options
var options option.Options
err = options.UnmarshalJSON([]byte(config))
err = options.UnmarshalJSONContext(ctx, []byte(config))
if err != nil {
return nil, fmt.Errorf("decode config: %v", err)
}
// create box context
ctx, cancel := context.WithCancel(context.Background())
ctx = service.ContextWithDefaultRegistry(ctx)
// create box
instance, err := box.New(box.Options{
Options: options,
Context: ctx,
PlatformInterface: boxPlatformInterfaceInstance,
PlatformLogWriter: boxPlatformLogWriter,
})
if err != nil {
@ -103,8 +110,8 @@ func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
}
// selector
if proxy, ok := b.Router().Outbound("proxy"); ok {
if selector, ok := proxy.(*outbound.Selector); ok {
if proxy, ok := b.Outbound().Outbound("proxy"); ok {
if selector, ok := proxy.(*group.Selector); ok {
b.selector = selector
}
}
@ -113,6 +120,9 @@ func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
}
func (b *BoxInstance) Start() (err error) {
b.access.Lock()
defer b.access.Unlock()
defer device.DeferPanicToError("box.Start", func(err_ error) { err = err_ })
if b.state == 0 {
@ -123,6 +133,9 @@ func (b *BoxInstance) Start() (err error) {
}
func (b *BoxInstance) Close() (err error) {
b.access.Lock()
defer b.access.Unlock()
defer device.DeferPanicToError("box.Close", func(err_ error) { err = err_ })
// no double close
@ -138,14 +151,19 @@ func (b *BoxInstance) Close() (err error) {
}
// close box
common.Close(b.Box)
if b.cancel != nil {
b.cancel()
}
if b.Box != nil {
b.Box.Close()
}
return nil
}
func (b *BoxInstance) Sleep() {
b.pauseManager.DevicePause()
_ = b.Box.Router().ResetNetwork()
// _ = b.Box.Router().ResetNetwork()
}
func (b *BoxInstance) Wake() {
@ -162,7 +180,7 @@ func (b *BoxInstance) SetV2rayStats(outbounds string) {
Enabled: true,
Outbounds: strings.Split(outbounds, "\n"),
})
b.Box.Router().SetV2RayServer(b.v2api)
b.Box.Router().SetTracker(b.v2api.StatsService())
}
func (b *BoxInstance) QueryStats(tag, direct string) int64 {

101
libcore/box_include.go Normal file
View File

@ -0,0 +1,101 @@
package libcore
import (
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/adapter/inbound"
"github.com/sagernet/sing-box/adapter/outbound"
"github.com/sagernet/sing-box/protocol/anytls"
"github.com/sagernet/sing-box/protocol/block"
"github.com/sagernet/sing-box/protocol/direct"
"github.com/sagernet/sing-box/protocol/dns"
"github.com/sagernet/sing-box/protocol/group"
"github.com/sagernet/sing-box/protocol/http"
"github.com/sagernet/sing-box/protocol/hysteria"
"github.com/sagernet/sing-box/protocol/hysteria2"
"github.com/sagernet/sing-box/protocol/mixed"
"github.com/sagernet/sing-box/protocol/redirect"
"github.com/sagernet/sing-box/protocol/shadowsocks"
"github.com/sagernet/sing-box/protocol/shadowtls"
"github.com/sagernet/sing-box/protocol/socks"
"github.com/sagernet/sing-box/protocol/ssh"
"github.com/sagernet/sing-box/protocol/tor"
"github.com/sagernet/sing-box/protocol/trojan"
"github.com/sagernet/sing-box/protocol/tuic"
"github.com/sagernet/sing-box/protocol/tun"
"github.com/sagernet/sing-box/protocol/vless"
"github.com/sagernet/sing-box/protocol/vmess"
"github.com/sagernet/sing-box/protocol/wireguard"
_ "github.com/sagernet/sing-box/experimental/clashapi"
)
func nekoboxAndroidInboundRegistry() *inbound.Registry {
registry := inbound.NewRegistry()
tun.RegisterInbound(registry)
redirect.RegisterRedirect(registry)
redirect.RegisterTProxy(registry)
direct.RegisterInbound(registry)
socks.RegisterInbound(registry)
http.RegisterInbound(registry)
mixed.RegisterInbound(registry)
return registry
}
func nekoboxAndroidOutboundRegistry() *outbound.Registry {
registry := outbound.NewRegistry()
direct.RegisterOutbound(registry)
block.RegisterOutbound(registry)
dns.RegisterOutbound(registry)
group.RegisterSelector(registry)
group.RegisterURLTest(registry)
socks.RegisterOutbound(registry)
http.RegisterOutbound(registry)
shadowsocks.RegisterOutbound(registry)
vmess.RegisterOutbound(registry)
trojan.RegisterOutbound(registry)
tor.RegisterOutbound(registry)
ssh.RegisterOutbound(registry)
shadowtls.RegisterOutbound(registry)
vless.RegisterOutbound(registry)
anytls.RegisterOutbound(registry)
registerQUICOutbounds(registry)
registerWireGuardOutbound(registry)
return registry
}
func nekoboxAndroidEndpointRegistry() *endpoint.Registry {
registry := endpoint.NewRegistry()
registerWireGuardEndpoint(registry)
return registry
}
func registerQUICInbounds(registry *inbound.Registry) {
hysteria.RegisterInbound(registry)
tuic.RegisterInbound(registry)
hysteria2.RegisterInbound(registry)
}
func registerQUICOutbounds(registry *outbound.Registry) {
hysteria.RegisterOutbound(registry)
tuic.RegisterOutbound(registry)
hysteria2.RegisterOutbound(registry)
}
func registerWireGuardOutbound(registry *outbound.Registry) {
wireguard.RegisterOutbound(registry)
}
func registerWireGuardEndpoint(registry *endpoint.Registry) {
wireguard.RegisterEndpoint(registry)
}

View File

@ -1,5 +1,6 @@
#!/bin/bash
source ./env_java.sh || true
source ../buildScript/init/env_ndk.sh
BUILD=".build"
@ -9,7 +10,11 @@ rm -rf $BUILD/android \
$BUILD/javac-output \
$BUILD/src
gomobile bind -v -androidapi 21 -cache $(realpath $BUILD) -trimpath -ldflags='-s -w' -tags='with_conntrack,with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_ech' . || exit 1
if [ -z "$GOPATH" ]; then
GOPATH=$(go env GOPATH)
fi
"$GOPATH"/bin/gomobile-matsuri bind -v -androidapi 21 -cache "$(realpath $BUILD)" -trimpath -ldflags='-s -w' -tags='with_conntrack,with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_ech' . || exit 1
rm -r libcore-sources.jar
proj=../app/libs

12
libcore/fix.go Normal file
View File

@ -0,0 +1,12 @@
package libcore
// https://github.com/golang/go/issues/46893
// TODO: remove after `bulkBarrierPreWrite: unaligned arguments` fixed
type StringBox struct {
Value string
}
func wrapString(value string) *StringBox {
return &StringBox{Value: value}
}

View File

@ -1,14 +1,15 @@
package libcore
import (
"github.com/oschwald/maxminddb-golang"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"log"
"net"
"os"
"strings"
"github.com/oschwald/maxminddb-golang"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
type Geoip struct {
@ -66,7 +67,8 @@ func (g *Geoip) ConvertGeoip(countryCode, outputPath string) {
}
outputFile, err := os.Create(outputPath)
err = srs.Write(outputFile, plainRuleSet.Upgrade())
rs, _ := plainRuleSet.Upgrade()
err = srs.Write(outputFile, rs, plainRuleSet.Version)
if err != nil {
log.Println("failed to write geosite file:", err)
return

View File

@ -1,13 +1,13 @@
package libcore
import (
"log"
"os"
"github.com/sagernet/sing-box/common/geosite"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"os"
"log"
)
type Geosite struct {
@ -58,7 +58,8 @@ func (g *Geosite) ConvertGeosite(code string, outputPath string) {
}
outputFile, err := os.Create(outputPath)
err = srs.Write(outputFile, plainRuleSet.Upgrade())
rs, _ := plainRuleSet.Upgrade()
err = srs.Write(outputFile, rs, plainRuleSet.Version)
if err != nil {
log.Println("failed to write geosite file:", err)
return

View File

@ -1,42 +1,41 @@
module libcore
go 1.20
go 1.23.1
toolchain go1.23.6
require (
github.com/matsuridayo/libneko v1.0.0 // replaced
github.com/miekg/dns v1.1.59
github.com/sagernet/sing v0.4.3
github.com/miekg/dns v1.1.63
github.com/oschwald/maxminddb-golang v1.12.0
github.com/sagernet/sing v0.6.1
github.com/sagernet/sing-box v1.0.0 // replaced
github.com/sagernet/sing-dns v0.2.3
github.com/sagernet/sing-tun v0.3.3
github.com/sagernet/sing-dns v0.4.0
github.com/sagernet/sing-tun v0.6.1
github.com/ulikunitz/xz v0.5.11
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
)
require github.com/oschwald/maxminddb-golang v1.12.0
require (
berty.tech/go-libtor v1.0.385 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/anytls/sing-anytls v0.0.2 // indirect
github.com/caddyserver/certmagic v0.20.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cretz/bine v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.0.12 // indirect
github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gofrs/uuid/v5 v5.2.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/gofrs/uuid/v5 v5.3.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
@ -44,47 +43,49 @@ require (
github.com/libdns/cloudflare v0.1.1 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
github.com/mholt/acmez v1.2.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
github.com/ooni/go-libtor v1.1.8 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
github.com/sagernet/quic-go v0.47.0-beta.2 // indirect
github.com/sagernet/cors v1.2.1 // indirect
github.com/sagernet/fswatch v0.1.1 // indirect
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/quic-go v0.49.0-beta.1 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-mux v0.2.0 // indirect
github.com/sagernet/sing-quic v0.2.2 // indirect
github.com/sagernet/sing-mux v0.3.1 // indirect
github.com/sagernet/sing-quic v0.4.0 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/sing-vmess v0.1.12 // indirect
github.com/sagernet/sing-shadowtls v0.2.0 // indirect
github.com/sagernet/sing-vmess v0.2.0 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/utls v1.5.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
github.com/sagernet/utls v1.6.7 // indirect
github.com/sagernet/wireguard-go v0.0.1-beta.5 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
@ -95,7 +96,7 @@ replace github.com/matsuridayo/libneko => ../../libneko
replace github.com/sagernet/sing-box => ../../sing-box
replace github.com/sagernet/sing-quic => ../../sing-quic
// replace github.com/sagernet/sing-quic => ../../sing-quic
// replace github.com/sagernet/sing => ../../sing

View File

@ -1,15 +1,13 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/anytls/sing-anytls v0.0.2 h1:25azSh0o/LMcIkhS4ZutgRTIGwh8O3wuOhsThVM9K9o=
github.com/anytls/sing-anytls v0.0.2/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -17,15 +15,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
@ -34,20 +29,20 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
@ -65,17 +60,20 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U=
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w=
github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
@ -86,59 +84,62 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.47.0-beta.2 h1:1tCGWFOSaXIeuQaHrwOMJIYvlupjTcaVInGQw5ArULU=
github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYigzQ5lQu3Nh4wNh8Q=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.49.0-beta.1 h1:3LdoCzVVfYRibZns1tYWSIoB65fpTmrwy+yfK8DQ8Jk=
github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8WHNsRs71b3Lt1+p/U=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing v0.6.1 h1:mJ6e7Ir2wtCoGLbdnnXWBsNJu5YHtbXmv66inoE0zFA=
github.com/sagernet/sing v0.6.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.0 h1:+mNoOuR3nljjouCH+qMg4zHI1+R9T2ReblGFkZPEndc=
github.com/sagernet/sing-dns v0.4.0/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
github.com/sagernet/sing-quic v0.4.0 h1:E4geazHk/UrJTXMlT+CBCKmn8V86RhtNeczWtfeoEFc=
github.com/sagernet/sing-quic v0.4.0/go.mod h1:c+CytOEyeN20KCTFIP8YQUkNDVFLSzjrEPqP7Hlnxys=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.3.3 h1:LZnQNmfGcNG2KPTPkLgc+Lo7k606QJVkPp2DnjriwUk=
github.com/sagernet/sing-tun v0.3.3/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo=
github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=
github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM=
github.com/sagernet/wireguard-go v0.0.1-beta.5 h1:aBEsxJUMEONwOZqKPIkuAcv4zJV5p6XlzEN04CF0FXc=
github.com/sagernet/wireguard-go v0.0.1-beta.5/go.mod h1:jGXij2Gn2wbrWuYNUmmNhf1dwcZtvyAvQoe8Xd8MbUo=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
@ -146,30 +147,28 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da h1:gS9sVMAeHM+gVBmM9bTM6vUi/NHv58O3QzJ3vjjN84M=
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da/go.mod h1:IEceR0jfVklLJXrbUe90rfdAFAYDW0SQwKl4qvO1GBQ=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -177,21 +176,22 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=

View File

@ -45,9 +45,9 @@ type HTTPRequest interface {
}
type HTTPResponse interface {
GetHeader(string) string
GetHeader(string) *StringBox
GetContent() ([]byte, error)
GetContentString() (string, error)
GetContentString() (*StringBox, error)
WriteTo(path string) error
}
@ -204,7 +204,7 @@ type httpResponse struct {
}
func (h *httpResponse) errorString() string {
content, err := h.GetContentString()
content, err := h.getContentString()
if err != nil {
return fmt.Sprint("HTTP ", h.Status)
}
@ -214,8 +214,8 @@ func (h *httpResponse) errorString() string {
return fmt.Sprint("HTTP ", h.Status, ": ", content)
}
func (h *httpResponse) GetHeader(key string) string {
return h.Header.Get(key)
func (h *httpResponse) GetHeader(key string) *StringBox {
return wrapString(h.Header.Get(key))
}
func (h *httpResponse) GetContent() ([]byte, error) {
@ -226,7 +226,15 @@ func (h *httpResponse) GetContent() ([]byte, error) {
return h.content, h.contentError
}
func (h *httpResponse) GetContentString() (string, error) {
func (h *httpResponse) GetContentString() (*StringBox, error) {
content, err := h.getContentString()
if err != nil {
return nil, err
}
return wrapString(content), nil
}
func (h *httpResponse) getContentString() (string, error) {
content, err := h.GetContent()
if err != nil {
return "", err

View File

@ -3,8 +3,12 @@
chmod -R 777 .build 2>/dev/null
rm -rf .build 2>/dev/null
if [ -z "$GOPATH" ]; then
GOPATH=$(go env GOPATH)
fi
# Install gomobile
if [ ! -f "$GOPATH/bin/gomobile" ]; then
if [ ! -f "$GOPATH/bin/gomobile-matsuri" ]; then
git clone https://github.com/MatsuriDayo/gomobile.git
pushd gomobile/cmd
pushd gomobile
@ -15,6 +19,8 @@ if [ ! -f "$GOPATH/bin/gomobile" ]; then
popd
popd
rm -rf gomobile
mv "$GOPATH/bin/gomobile" "$GOPATH/bin/gomobile-matsuri"
mv "$GOPATH/bin/gobind" "$GOPATH/bin/gobind-matsuri"
fi
gomobile init
gomobile-matsuri init

View File

@ -4,6 +4,7 @@ import (
"net/netip"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
"github.com/sagernet/sing/common/x/list"
)
@ -28,8 +29,8 @@ func (i *interfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
return 0
}
func (i *interfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
return "", 0
func (i *interfaceMonitor) DefaultInterface() *control.Interface {
return nil
}
func (i *interfaceMonitor) OverrideAndroidVPN() bool {

View File

@ -12,7 +12,6 @@ import (
"github.com/matsuridayo/libneko/neko_common"
"github.com/matsuridayo/libneko/neko_log"
boxmain "github.com/sagernet/sing-box/cmd/sing-box"
"github.com/sagernet/sing-box/nekoutils"
)
@ -58,7 +57,6 @@ func InitCore(process, cachePath, internalAssets, externalAssets string,
neko_log.LogWriterDisable = !logEnable
neko_log.TruncateOnStart = isBgProcess
neko_log.SetupLog(int(maxLogSizeKb)*1024, filepath.Join(cachePath, "neko.log"))
boxmain.SetDisableColor(true)
// nekoutils
nekoutils.Selector_OnProxySelected = intfNB4A.Selector_OnProxySelected

View File

@ -18,7 +18,6 @@ import (
sblog "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
@ -36,7 +35,7 @@ func (w *boxPlatformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
}
}
func (w *boxPlatformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
func (w *boxPlatformInterfaceWrapper) Initialize(n adapter.NetworkManager) error {
return nil
}
@ -44,13 +43,9 @@ func (w *boxPlatformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bo
return true
}
func (w *boxPlatformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
func (w *boxPlatformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error {
// "protect"
return func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
return intfBox.AutoDetectInterfaceControl(int32(fd))
})
}
return intfBox.AutoDetectInterfaceControl(int32(fd))
}
func (w *boxPlatformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
@ -92,7 +87,7 @@ func (w *boxPlatformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
return false
}
func (w *boxPlatformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
func (w *boxPlatformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, error) {
return nil, errors.New("wtf")
}
@ -100,6 +95,10 @@ func (w *boxPlatformInterfaceWrapper) IncludeAllNetworks() bool {
return false
}
func (w *boxPlatformInterfaceWrapper) SendNotification(notification *platform.Notification) error {
return nil
}
// Android not using
func (w *boxPlatformInterfaceWrapper) UnderNetworkExtension() bool {

View File

@ -1,3 +1,3 @@
PACKAGE_NAME=moe.nb4a
VERSION_NAME=1.3.2
VERSION_CODE=36
VERSION_NAME=1.3.5
VERSION_CODE=39