Compare commits

...

9 Commits

Author SHA1 Message Date
2dust
447e712a9d up 1.8.31 2024-07-16 14:05:34 +08:00
2dust
8bb03f189d up 1.8.30 2024-07-13 10:43:46 +08:00
2dust
3b0554cd9b up 1.8.29 2024-07-12 13:30:38 +08:00
2dust
858101b0d9 Bug fix
https://github.com/2dust/v2rayNG/issues/3234
https://github.com/2dust/v2rayNG/issues/3291
2024-07-11 16:41:38 +08:00
2dust
a7cf8bee28 Code clean
Add allowInsecure to Share
2024-07-11 16:40:25 +08:00
2dust
af1ec7bea9 Bug fix 2024-07-09 12:29:04 +08:00
2dust
002bf7ef22 up 1.8.28 2024-07-07 18:14:36 +08:00
2dust
6919e2336d Bug fix
https://github.com/2dust/v2rayNG/issues/3278
2024-07-01 18:09:57 +08:00
2dust
5a5bd22073 Bug fix
https://github.com/2dust/v2rayNG/issues/3232
2024-07-01 10:34:45 +08:00
13 changed files with 209 additions and 218 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId = "com.v2ray.ang" applicationId = "com.v2ray.ang"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 570 versionCode = 575
versionName = "1.8.27" versionName = "1.8.31"
multiDexEnabled = true multiDexEnabled = true
splits.abi { splits.abi {
reset() reset()
@@ -60,7 +60,7 @@ android {
applicationVariants.all { applicationVariants.all {
val variant = this val variant = this
val versionCodes = val versionCodes =
mapOf("armeabi-v7a" to 4, "arm64-v8a" to 4, "x86" to 4, "x86_64" to 4, "all" to 4) mapOf("armeabi-v7a" to 4, "arm64-v8a" to 4, "x86" to 4, "x86_64" to 4, "universal" to 4)
variant.outputs variant.outputs
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl } .map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
@@ -68,7 +68,7 @@ android {
val abi = if (output.getFilter("ABI") != null) val abi = if (output.getFilter("ABI") != null)
output.getFilter("ABI") output.getFilter("ABI")
else else
"all" "universal"
output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk" output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk"
if(versionCodes.containsKey(abi)) if(versionCodes.containsKey(abi))
@@ -98,6 +98,7 @@ dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar")))) implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar"))))
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
implementation("com.google.android.flexbox:flexbox:3.0.0")
// Androidx // Androidx
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.legacy:legacy-support-v4:1.0.0")
@@ -112,9 +113,9 @@ dependencies {
// Androidx ktx // Androidx ktx
implementation("androidx.activity:activity-ktx:1.9.0") implementation("androidx.activity:activity-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.2") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.2") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3")
//kotlin //kotlin
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.23") implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.23")

View File

@@ -387,22 +387,26 @@ object AngConfigManager {
// } // }
fun importBatchConfig(server: String?, subid: String, append: Boolean): Int { fun importBatchConfig(server: String?, subid: String, append: Boolean): Int {
var count = parseBatchConfig(server, subid, append) var count = parseBatchConfig(Utils.decode(server), subid, append)
if (count <= 0) { if (count <= 0) {
count = parseBatchConfig(Utils.decode(server), subid, append) count = parseBatchConfig(server, subid, append)
} }
if (count <= 0) { if (count <= 0) {
count = parseCustomConfigServer(server, subid) count = parseCustomConfigServer(server, subid)
} }
if (parseBatchSubscription(server, subid) > 0) { var countSub = parseBatchSubscription(server)
updateConfigViaSubAll() if (countSub <= 0) {
return 1 countSub = parseBatchSubscription(Utils.decode(server))
} }
return count if (countSub > 0) {
updateConfigViaSubAll()
}
return count + countSub
} }
fun parseBatchSubscription(servers: String?, subid: String): Int { fun parseBatchSubscription(servers: String?): Int {
try { try {
if (servers == null) { if (servers == null) {
return 0 return 0
@@ -410,7 +414,6 @@ object AngConfigManager {
var count = 0 var count = 0
servers.lines() servers.lines()
.reversed()
.forEach { str -> .forEach { str ->
if (str.startsWith(AppConfig.PROTOCOL_HTTP) || str.startsWith(AppConfig.PROTOCOL_HTTPS)) { if (str.startsWith(AppConfig.PROTOCOL_HTTP) || str.startsWith(AppConfig.PROTOCOL_HTTPS)) {
count += MmkvManager.importUrlAsSubscription(str) count += MmkvManager.importUrlAsSubscription(str)
@@ -488,7 +491,7 @@ object AngConfigManager {
if (serverList.isNotEmpty()) { if (serverList.isNotEmpty()) {
var count = 0 var count = 0
for (srv in serverList) { for (srv in serverList.reversed()) {
val config = ServerConfig.create(EConfigType.CUSTOM) val config = ServerConfig.create(EConfigType.CUSTOM)
config.fullConfig = config.fullConfig =
Gson().fromJson(Gson().toJson(srv), V2rayConfig::class.java) Gson().fromJson(Gson().toJson(srv), V2rayConfig::class.java)
@@ -560,7 +563,7 @@ object AngConfigManager {
settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT),
AppConfig.PORT_HTTP.toInt() AppConfig.PORT_HTTP.toInt()
) )
Utils.getUrlContentWithCustomUserAgent(url, httpPort) Utils.getUrlContentWithCustomUserAgent(url, 30000, httpPort)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
"" ""
@@ -577,9 +580,9 @@ object AngConfigManager {
} }
private fun parseConfigViaSub(server: String?, subid: String, append: Boolean): Int { private fun parseConfigViaSub(server: String?, subid: String, append: Boolean): Int {
var count = parseBatchConfig(server, subid, append) var count = parseBatchConfig(Utils.decode(server), subid, append)
if (count <= 0) { if (count <= 0) {
count = parseBatchConfig(Utils.decode(server), subid, append) count = parseBatchConfig(server, subid, append)
} }
if (count <= 0) { if (count <= 0) {
count = parseCustomConfigServer(server, subid) count = parseCustomConfigServer(server, subid)

View File

@@ -7,7 +7,6 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.v2ray.ang.dto.AppInfo import com.v2ray.ang.dto.AppInfo
import rx.Observable import rx.Observable
import java.util.*
object AppManagerUtil { object AppManagerUtil {
fun loadNetworkAppList(ctx: Context): ArrayList<AppInfo> { fun loadNetworkAppList(ctx: Context): ArrayList<AppInfo> {
@@ -31,9 +30,10 @@ object AppManagerUtil {
return apps return apps
} }
fun rxLoadNetworkAppList(ctx: Context): Observable<ArrayList<AppInfo>> = Observable.unsafeCreate { fun rxLoadNetworkAppList(ctx: Context): Observable<ArrayList<AppInfo>> =
it.onNext(loadNetworkAppList(ctx)) Observable.unsafeCreate {
} it.onNext(loadNetworkAppList(ctx))
}
val PackageInfo.hasInternetPermission: Boolean val PackageInfo.hasInternetPermission: Boolean
get() { get() {

View File

@@ -179,7 +179,7 @@ object MmkvManager {
} }
} }
fun sortByTestResults( ) { fun sortByTestResults() {
data class ServerDelay(var guid: String, var testDelayMillis: Long) data class ServerDelay(var guid: String, var testDelayMillis: Long)
val serverDelays = mutableListOf<ServerDelay>() val serverDelays = mutableListOf<ServerDelay>()

View File

@@ -7,7 +7,7 @@ import android.content.res.Resources
import android.os.Build import android.os.Build
import android.os.LocaleList import android.os.LocaleList
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import java.util.* import java.util.Locale
open class MyContextWrapper(base: Context?) : ContextWrapper(base) { open class MyContextWrapper(base: Context?) : ContextWrapper(base) {
companion object { companion object {

View File

@@ -52,7 +52,8 @@ object SpeedtestUtil {
val allText = process.inputStream.bufferedReader().use { it.readText() } val allText = process.inputStream.bufferedReader().use { it.readText() }
if (!TextUtils.isEmpty(allText)) { if (!TextUtils.isEmpty(allText)) {
val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19) val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19)
val temps = tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val temps =
tempInfo.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (temps.count() > 0 && temps[0].length < 10) { if (temps.count() > 0 && temps[0].length < 10) {
return temps[0].toFloat().toInt().toString() + "ms" return temps[0].toFloat().toInt().toString() + "ms"
} }
@@ -70,7 +71,7 @@ object SpeedtestUtil {
tcpTestingSockets.add(socket) tcpTestingSockets.add(socket)
} }
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
socket.connect(InetSocketAddress(url, port),3000) socket.connect(InetSocketAddress(url, port), 3000)
val time = System.currentTimeMillis() - start val time = System.currentTimeMillis() - start
synchronized(this) { synchronized(this) {
tcpTestingSockets.remove(socket) tcpTestingSockets.remove(socket)
@@ -105,8 +106,11 @@ object SpeedtestUtil {
val url = URL(Utils.getDelayTestUrl()) val url = URL(Utils.getDelayTestUrl())
conn = url.openConnection( conn = url.openConnection(
Proxy(Proxy.Type.HTTP, Proxy(
InetSocketAddress("127.0.0.1", port))) as HttpURLConnection Proxy.Type.HTTP,
InetSocketAddress("127.0.0.1", port)
)
) as HttpURLConnection
conn.connectTimeout = 30000 conn.connectTimeout = 30000
conn.readTimeout = 30000 conn.readTimeout = 30000
conn.setRequestProperty("Connection", "close") conn.setRequestProperty("Connection", "close")
@@ -120,11 +124,19 @@ object SpeedtestUtil {
if (code == 204 || code == 200 && conn.responseLength == 0L) { if (code == 204 || code == 200 && conn.responseLength == 0L) {
result = context.getString(R.string.connection_test_available, elapsed) result = context.getString(R.string.connection_test_available, elapsed)
} else { } else {
throw IOException(context.getString(R.string.connection_test_error_status_code, code)) throw IOException(
context.getString(
R.string.connection_test_error_status_code,
code
)
)
} }
} catch (e: IOException) { } catch (e: IOException) {
// network exception // network exception
Log.d(AppConfig.ANG_PACKAGE, "testConnection IOException: " + Log.getStackTraceString(e)) Log.d(
AppConfig.ANG_PACKAGE,
"testConnection IOException: " + Log.getStackTraceString(e)
)
result = context.getString(R.string.connection_test_error, e.message) result = context.getString(R.string.connection_test_error, e.message)
} catch (e: Exception) { } catch (e: Exception) {
// library exception, eg sumsung // library exception, eg sumsung

View File

@@ -356,7 +356,7 @@ object Utils {
} }
@Throws(IOException::class) @Throws(IOException::class)
fun getUrlContentWithCustomUserAgent(urlStr: String?, httpPort: Int = 0): String { fun getUrlContentWithCustomUserAgent(urlStr: String?, timeout: Int = 30000, httpPort: Int = 0): String {
val url = URL(urlStr) val url = URL(urlStr)
val conn = if (httpPort == 0) { val conn = if (httpPort == 0) {
url.openConnection() url.openConnection()
@@ -368,6 +368,8 @@ object Utils {
) )
) )
} }
conn.connectTimeout = timeout
conn.readTimeout = timeout
conn.setRequestProperty("Connection", "close") conn.setRequestProperty("Connection", "close")
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}") conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
url.userInfo?.let { url.userInfo?.let {

View File

@@ -142,7 +142,8 @@ object V2rayConfigUtil {
settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true) settingsStorage?.decodeBool(AppConfig.PREF_SNIFFING_ENABLED, true)
?: true ?: true
v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp v2rayConfig.inbounds[0].sniffing?.enabled = fakedns || sniffAllTlsAndHttp
v2rayConfig.inbounds[0].sniffing?.routeOnly = settingsStorage?.decodeBool(AppConfig.PREF_ROUTE_ONLY_ENABLED, false) v2rayConfig.inbounds[0].sniffing?.routeOnly =
settingsStorage?.decodeBool(AppConfig.PREF_ROUTE_ONLY_ENABLED, false)
if (!sniffAllTlsAndHttp) { if (!sniffAllTlsAndHttp) {
v2rayConfig.inbounds[0].sniffing?.destOverride?.clear() v2rayConfig.inbounds[0].sniffing?.destOverride?.clear()
} }
@@ -188,7 +189,7 @@ object V2rayConfigUtil {
) )
routingUserRule( routingUserRule(
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT)
?: "", AppConfig.TAG_DIRECT, v2rayConfig ?: "", TAG_DIRECT, v2rayConfig
) )
routingUserRule( routingUserRule(
settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED)
@@ -210,37 +211,38 @@ object V2rayConfigUtil {
when (routingMode) { when (routingMode) {
ERoutingMode.BYPASS_LAN.value -> { ERoutingMode.BYPASS_LAN.value -> {
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("ip", "private", TAG_DIRECT, v2rayConfig)
} }
ERoutingMode.BYPASS_MAINLAND.value -> { ERoutingMode.BYPASS_MAINLAND.value -> {
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("", "cn", TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("domain", "geolocation-cn", TAG_DIRECT, v2rayConfig)
v2rayConfig.routing.rules.add(0, googleapisRoute) v2rayConfig.routing.rules.add(0, googleapisRoute)
} }
ERoutingMode.BYPASS_LAN_MAINLAND.value -> { ERoutingMode.BYPASS_LAN_MAINLAND.value -> {
routingGeo("ip", "private", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("ip", "private", TAG_DIRECT, v2rayConfig)
routingGeo("", "cn", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("", "cn", TAG_DIRECT, v2rayConfig)
routingGeo("domain", "geolocation-cn", AppConfig.TAG_DIRECT, v2rayConfig) routingGeo("domain", "geolocation-cn", TAG_DIRECT, v2rayConfig)
v2rayConfig.routing.rules.add(0, googleapisRoute) v2rayConfig.routing.rules.add(0, googleapisRoute)
} }
ERoutingMode.GLOBAL_DIRECT.value -> { ERoutingMode.GLOBAL_DIRECT.value -> {
val globalDirect = V2rayConfig.RoutingBean.RulesBean( val globalDirect = V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_DIRECT, outboundTag = TAG_DIRECT,
port = "0-65535" port = "0-65535"
) )
v2rayConfig.routing.rules.add(globalDirect) v2rayConfig.routing.rules.add(globalDirect)
} }
} }
if(routingMode != ERoutingMode.GLOBAL_DIRECT.value) { if (routingMode != ERoutingMode.GLOBAL_DIRECT.value) {
v2rayConfig.routing.rules.add( v2rayConfig.routing.rules.add(
V2rayConfig.RoutingBean.RulesBean( V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_PROXY, outboundTag = AppConfig.TAG_PROXY,
port = "0-65535" port = "0-65535"
)) )
)
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -465,7 +467,7 @@ object V2rayConfigUtil {
if (Utils.isPureIpAddress(domesticDns.first())) { if (Utils.isPureIpAddress(domesticDns.first())) {
v2rayConfig.routing.rules.add( v2rayConfig.routing.rules.add(
0, V2rayConfig.RoutingBean.RulesBean( 0, V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_DIRECT, outboundTag = TAG_DIRECT,
port = "53", port = "53",
ip = arrayListOf(domesticDns.first()), ip = arrayListOf(domesticDns.first()),
domain = null domain = null
@@ -597,7 +599,8 @@ object V2rayConfigUtil {
mux = null mux = null
) )
var packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello" var packets =
settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello"
if (v2rayConfig.outbounds[0].streamSettings?.security == V2rayConfig.REALITY if (v2rayConfig.outbounds[0].streamSettings?.security == V2rayConfig.REALITY
&& packets == "tlshello" && packets == "tlshello"
) { ) {

View File

@@ -96,7 +96,7 @@ object ShadowsocksFmt {
for (pair in pairs) { for (pair in pairs) {
val idx = pair.indexOf("=") val idx = pair.indexOf("=")
if (idx == -1) { if (idx == -1) {
queryPairs[Utils.urlDecode(pair)] = ""; queryPairs[Utils.urlDecode(pair)] = ""
} else { } else {
queryPairs[Utils.urlDecode(pair.substring(0, idx))] = queryPairs[Utils.urlDecode(pair.substring(0, idx))] =
Utils.urlDecode(pair.substring(idx + 1)) Utils.urlDecode(pair.substring(idx + 1))
@@ -118,9 +118,9 @@ object ShadowsocksFmt {
null null
) )
} else if (queryPairs["plugin"] == "v2ray-plugin") { } else if (queryPairs["plugin"] == "v2ray-plugin") {
var network = "ws"; var network = "ws"
if (queryPairs["mode"] == "quic") { if (queryPairs["mode"] == "quic") {
network = "quic"; network = "quic"
} }
sni = config.outboundBean?.streamSettings?.populateTransportSettings( sni = config.outboundBean?.streamSettings?.populateTransportSettings(
network, network,

View File

@@ -20,7 +20,7 @@ object TrojanFmt {
} }
fun parseTrojan(str: String): ServerConfig? { fun parseTrojan(str: String): ServerConfig? {
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.TROJAN) val config = ServerConfig.create(EConfigType.TROJAN)
val uri = URI(Utils.fixIllegalUrl(str)) val uri = URI(Utils.fixIllegalUrl(str))
@@ -28,35 +28,37 @@ object TrojanFmt {
var flow = "" var flow = ""
var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint
if (uri.rawQuery.isNullOrEmpty()) { if (uri.rawQuery.isNullOrEmpty()) return null
config.outboundBean?.streamSettings?.populateTlsSettings(
V2rayConfig.TLS, allowInsecure, "",
fingerprint, null, null, null, null
)
} else {
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
val sni = config.outboundBean?.streamSettings?.populateTransportSettings(
queryParam["type"] ?: "tcp", val queryParam = uri.rawQuery.split("&")
queryParam["headerType"], .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
queryParam["host"],
queryParam["path"], val sni = config.outboundBean?.streamSettings?.populateTransportSettings(
queryParam["seed"], queryParam["type"] ?: "tcp",
queryParam["quicSecurity"], queryParam["headerType"],
queryParam["key"], queryParam["host"],
queryParam["mode"], queryParam["path"],
queryParam["serviceName"], queryParam["seed"],
queryParam["authority"] queryParam["quicSecurity"],
) queryParam["key"],
fingerprint = queryParam["fp"] ?: "" queryParam["mode"],
config.outboundBean?.streamSettings?.populateTlsSettings( queryParam["serviceName"],
queryParam["security"] ?: V2rayConfig.TLS, queryParam["authority"]
allowInsecure, queryParam["sni"] ?: sni?:"", fingerprint, queryParam["alpn"], )
null, null, null fingerprint = queryParam["fp"] ?: ""
) allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
flow = queryParam["flow"] ?: "" config.outboundBean?.streamSettings?.populateTlsSettings(
} queryParam["security"] ?: V2rayConfig.TLS,
allowInsecure,
queryParam["sni"] ?: sni ?: "",
fingerprint,
queryParam["alpn"],
null,
null,
null
)
flow = queryParam["flow"] ?: ""
config.outboundBean?.settings?.servers?.get(0)?.let { server -> config.outboundBean?.settings?.servers?.get(0)?.let { server ->
server.address = uri.idnHost server.address = uri.idnHost
@@ -78,7 +80,6 @@ object TrojanFmt {
if (!TextUtils.isEmpty(it)) { if (!TextUtils.isEmpty(it)) {
dicQuery["flow"] = it dicQuery["flow"] = it
} }
} }
dicQuery["security"] = streamSetting.security.ifEmpty { "none" } dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
@@ -92,16 +93,16 @@ object TrojanFmt {
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
} }
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint?:"" dicQuery["fp"] = tlsSetting.fingerprint ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.publicKey)) { if (!TextUtils.isEmpty(tlsSetting.publicKey)) {
dicQuery["pbk"] = tlsSetting.publicKey?:"" dicQuery["pbk"] = tlsSetting.publicKey ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.shortId)) { if (!TextUtils.isEmpty(tlsSetting.shortId)) {
dicQuery["sid"] = tlsSetting.shortId?:"" dicQuery["sid"] = tlsSetting.shortId ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.spiderX)) { if (!TextUtils.isEmpty(tlsSetting.spiderX)) {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX?:"") dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "")
} }
} }
dicQuery["type"] = dicQuery["type"] =

View File

@@ -20,7 +20,7 @@ object VlessFmt {
} }
fun parseVless(str: String): ServerConfig? { fun parseVless(str: String): ServerConfig? {
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.VLESS) val config = ServerConfig.create(EConfigType.VLESS)
val uri = URI(Utils.fixIllegalUrl(str)) val uri = URI(Utils.fixIllegalUrl(str))
@@ -31,7 +31,7 @@ object VlessFmt {
val streamSetting = config.outboundBean?.streamSettings ?: return null val streamSetting = config.outboundBean?.streamSettings ?: return null
config.remarks = Utils.urlDecode(uri.fragment ?: "") config.remarks = Utils.urlDecode(uri.fragment ?: "")
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = uri.idnHost vnext.address = uri.idnHost
vnext.port = uri.port vnext.port = uri.port
vnext.users[0].id = uri.userInfo vnext.users[0].id = uri.userInfo
@@ -51,6 +51,7 @@ object VlessFmt {
queryParam["serviceName"], queryParam["serviceName"],
queryParam["authority"] queryParam["authority"]
) )
allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
streamSetting.populateTlsSettings( streamSetting.populateTlsSettings(
queryParam["security"] ?: "", queryParam["security"] ?: "",
allowInsecure, allowInsecure,
@@ -92,16 +93,16 @@ object VlessFmt {
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
} }
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint?:"" dicQuery["fp"] = tlsSetting.fingerprint ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.publicKey)) { if (!TextUtils.isEmpty(tlsSetting.publicKey)) {
dicQuery["pbk"] = tlsSetting.publicKey?:"" dicQuery["pbk"] = tlsSetting.publicKey ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.shortId)) { if (!TextUtils.isEmpty(tlsSetting.shortId)) {
dicQuery["sid"] = tlsSetting.shortId?:"" dicQuery["sid"] = tlsSetting.shortId ?: ""
} }
if (!TextUtils.isEmpty(tlsSetting.spiderX)) { if (!TextUtils.isEmpty(tlsSetting.spiderX)) {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX?:"") dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "")
} }
} }
dicQuery["type"] = dicQuery["type"] =

View File

@@ -22,68 +22,64 @@ object VmessFmt {
} }
fun parseVmess(str: String): ServerConfig? { fun parseVmess(str: String): ServerConfig? {
if (str.indexOf('?') > 0 && str.indexOf('&') > 0) {
return parseVmessStd(str)
}
val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false val allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
val config = ServerConfig.create(EConfigType.VMESS) val config = ServerConfig.create(EConfigType.VMESS)
val streamSetting = config.outboundBean?.streamSettings ?: return null val streamSetting = config.outboundBean?.streamSettings ?: return null
var result = str.replace(EConfigType.VMESS.protocolScheme, "")
if (!tryParseNewVmess(str, config, allowInsecure)) { result = Utils.decode(result)
if (str.indexOf("?") > 0) { if (TextUtils.isEmpty(result)) {
if (!tryResolveVmess4Kitsunebi(str, config)) { Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_decoding_failed")
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol") return null
return null
}
} else {
var result = str.replace(EConfigType.VMESS.protocolScheme, "")
result = Utils.decode(result)
if (TextUtils.isEmpty(result)) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_decoding_failed")
return null
}
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
if (TextUtils.isEmpty(vmessQRCode.add) || TextUtils.isEmpty(vmessQRCode.port) || TextUtils.isEmpty(
vmessQRCode.id
) || TextUtils.isEmpty(vmessQRCode.net)
) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
return null
}
config.remarks = vmessQRCode.ps
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = vmessQRCode.add
vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id
vnext.users[0].security =
if (TextUtils.isEmpty(vmessQRCode.scy)) V2rayConfig.DEFAULT_SECURITY else vmessQRCode.scy
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(
vmessQRCode.net,
vmessQRCode.type,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.path,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.type,
vmessQRCode.path,
vmessQRCode.host
)
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
streamSetting.populateTlsSettings(
vmessQRCode.tls,
allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
fingerprint,
vmessQRCode.alpn,
null,
null,
null
)
}
} }
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
if (TextUtils.isEmpty(vmessQRCode.add)
|| TextUtils.isEmpty(vmessQRCode.port)
|| TextUtils.isEmpty(vmessQRCode.id)
|| TextUtils.isEmpty(vmessQRCode.net)
) {
Log.d(AppConfig.ANG_PACKAGE, "R.string.toast_incorrect_protocol")
return null
}
config.remarks = vmessQRCode.ps
config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = vmessQRCode.add
vnext.port = Utils.parseInt(vmessQRCode.port)
vnext.users[0].id = vmessQRCode.id
vnext.users[0].security =
if (TextUtils.isEmpty(vmessQRCode.scy)) V2rayConfig.DEFAULT_SECURITY else vmessQRCode.scy
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
}
val sni = streamSetting.populateTransportSettings(
vmessQRCode.net,
vmessQRCode.type,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.path,
vmessQRCode.host,
vmessQRCode.path,
vmessQRCode.type,
vmessQRCode.path,
vmessQRCode.host
)
val fingerprint = vmessQRCode.fp
streamSetting.populateTlsSettings(
vmessQRCode.tls,
allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
fingerprint,
vmessQRCode.alpn,
null,
null,
null
)
return config return config
} }
@@ -114,80 +110,51 @@ object VmessFmt {
return Utils.encode(json) return Utils.encode(json)
} }
private fun tryParseNewVmess( fun parseVmessStd(str: String): ServerConfig? {
uriString: String, config: ServerConfig, allowInsecure: Boolean var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
): Boolean { val config = ServerConfig.create(EConfigType.VMESS)
return runCatching {
val uri = URI(Utils.fixIllegalUrl(uriString))
check(uri.scheme == "vmess")
val (_, protocol, tlsStr, uuid, alterId) = Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})").matchEntire(
uri.userInfo
)?.groupValues ?: error("parse user info fail.")
val tls = tlsStr.isNotBlank()
val queryParam = uri.rawQuery.split("&")
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
val streamSetting = config.outboundBean?.streamSettings ?: return false val uri = URI(Utils.fixIllegalUrl(str))
config.remarks = Utils.urlDecode(uri.fragment ?: "") if (uri.rawQuery.isNullOrEmpty()) return null
config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> val queryParam = uri.rawQuery.split("&")
vnext.address = uri.idnHost .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
vnext.port = uri.port
vnext.users[0].id = uuid
vnext.users[0].security = V2rayConfig.DEFAULT_SECURITY
vnext.users[0].alterId = alterId.toInt()
}
var fingerprint = streamSetting.tlsSettings?.fingerprint
val sni = streamSetting.populateTransportSettings(protocol,
queryParam["type"],
queryParam["host"]?.split("|")?.get(0) ?: "",
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "",
queryParam["seed"],
queryParam["security"],
queryParam["key"],
queryParam["mode"],
queryParam["serviceName"],
queryParam["authority"])
streamSetting.populateTlsSettings(
if (tls) V2rayConfig.TLS else "",
allowInsecure,
sni,
fingerprint,
null,
null,
null,
null
)
true
}.getOrElse { false }
}
private fun tryResolveVmess4Kitsunebi(server: String, config: ServerConfig): Boolean { val streamSetting = config.outboundBean?.streamSettings ?: return null
var result = server.replace(EConfigType.VMESS.protocolScheme, "") config.remarks = Utils.urlDecode(uri.fragment ?: "")
val indexSplit = result.indexOf("?") config.outboundBean.settings?.vnext?.get(0)?.let { vnext ->
if (indexSplit > 0) { vnext.address = uri.idnHost
result = result.substring(0, indexSplit) vnext.port = uri.port
} vnext.users[0].id = uri.userInfo
result = Utils.decode(result) vnext.users[0].security = V2rayConfig.DEFAULT_SECURITY
val arr1 = result.split('@')
if (arr1.count() != 2) {
return false
}
val arr21 = arr1[0].split(':')
val arr22 = arr1[1].split(':')
if (arr21.count() != 2) {
return false
}
config.remarks = "Alien"
config.outboundBean?.settings?.vnext?.get(0)?.let { vnext ->
vnext.address = arr22[0]
vnext.port = Utils.parseInt(arr22[1])
vnext.users[0].id = arr21[1]
vnext.users[0].security = arr21[0]
vnext.users[0].alterId = 0 vnext.users[0].alterId = 0
} }
return true
val sni = streamSetting.populateTransportSettings(
queryParam["type"] ?: "tcp",
queryParam["headerType"],
queryParam["host"],
queryParam["path"],
queryParam["seed"],
queryParam["quicSecurity"],
queryParam["key"],
queryParam["mode"],
queryParam["serviceName"],
queryParam["authority"]
)
allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure
streamSetting.populateTlsSettings(
queryParam["security"] ?: "",
allowInsecure,
queryParam["sni"] ?: sni,
queryParam["fp"] ?: "",
queryParam["alpn"],
null,
null,
null
)
return config
} }
} }

View File

@@ -25,14 +25,15 @@ object WireguardFmt {
?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace() ?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace()
.split(",") .split(",")
wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: "" wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: ""
wireguard.peers?.get(0)?.endpoint = "${uri.idnHost}:${uri.port}" wireguard.peers?.get(0)?.endpoint =
Utils.getIpv6Address(uri.idnHost) + ":${uri.port}"
wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU) wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
wireguard.reserved = wireguard.reserved =
(queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",") (queryParam["reserved"] ?: "0,0,0").removeWhiteSpace().split(",")
.map { it.toInt() } .map { it.toInt() }
} }
return config return config
}else { } else {
return null return null
} }
} }