Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6632ba3b6e | ||
|
|
bd582f1d90 | ||
|
|
f5f1e12565 | ||
|
|
207d6f4f8c | ||
|
|
52e0a19826 | ||
|
|
fc0e60a097 | ||
|
|
65d6b4aaa8 | ||
|
|
7b11755e7f | ||
|
|
2d0de4860c | ||
|
|
7406ef16ff | ||
|
|
a084b21d50 | ||
|
|
bf01fe2bdb | ||
|
|
519cc2a4b5 | ||
|
|
c21653d40f | ||
|
|
1919c5e05f |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4.2.2
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: '0'
|
||||
|
||||
@@ -12,8 +12,8 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 667
|
||||
versionName = "1.10.17"
|
||||
versionCode = 669
|
||||
versionName = "1.10.19"
|
||||
multiDexEnabled = true
|
||||
|
||||
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
||||
|
||||
@@ -91,6 +91,8 @@ object AppConfig {
|
||||
const val TAG_DIRECT = "direct"
|
||||
const val TAG_BLOCKED = "block"
|
||||
const val TAG_FRAGMENT = "fragment"
|
||||
const val TAG_DNS = "dns-module"
|
||||
const val TAG_DOMESTIC_DNS = "domestic-dns"
|
||||
|
||||
/** Network-related constants. */
|
||||
const val UPLINK = "uplink"
|
||||
|
||||
@@ -245,7 +245,14 @@ data class V2rayConfig(
|
||||
var tproxy: String? = null,
|
||||
var mark: Int? = null,
|
||||
var dialerProxy: String? = null,
|
||||
var domainStrategy: String? = null
|
||||
var domainStrategy: String? = null,
|
||||
var happyEyeballs: happyEyeballsBean? = null,
|
||||
)
|
||||
data class happyEyeballsBean(
|
||||
var prioritizeIPv6: Boolean? = null,
|
||||
var maxConcurrentTry: Int? = 4,
|
||||
var tryDelayMs: Int? = 250, // ms
|
||||
var interleave: Int? = null,
|
||||
)
|
||||
|
||||
data class TlsSettingsBean(
|
||||
@@ -490,6 +497,7 @@ data class V2rayConfig(
|
||||
var expectIPs: List<String>? = null,
|
||||
val clientIp: String? = null,
|
||||
val skipFallback: Boolean? = null,
|
||||
val tag: String? = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -509,9 +509,10 @@ object V2rayConfigManager {
|
||||
//hev-socks5-tunnel dns routing
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
type = "field",
|
||||
inboundTag = arrayListOf("socks"),
|
||||
outboundTag = "dns-out",
|
||||
port = "53",
|
||||
outboundTag = "dns-out"
|
||||
type = "field"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -574,18 +575,8 @@ object V2rayConfigManager {
|
||||
address = domesticDns.first(),
|
||||
domains = directDomain,
|
||||
expectIPs = if (isCnRoutingMode) geoipCn else null,
|
||||
skipFallback = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (Utils.isPureIpAddress(domesticDns.first())) {
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
port = "53",
|
||||
ip = arrayListOf(domesticDns.first()),
|
||||
domain = null
|
||||
skipFallback = true,
|
||||
tag = AppConfig.TAG_DOMESTIC_DNS
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -626,20 +617,26 @@ object V2rayConfigManager {
|
||||
// DNS dns
|
||||
v2rayConfig.dns = V2rayConfig.DnsBean(
|
||||
servers = servers,
|
||||
hosts = hosts
|
||||
hosts = hosts,
|
||||
tag = AppConfig.TAG_DNS
|
||||
)
|
||||
|
||||
// DNS routing
|
||||
if (Utils.isPureIpAddress(remoteDns.first())) {
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_PROXY,
|
||||
port = "53",
|
||||
ip = arrayListOf(remoteDns.first()),
|
||||
domain = null
|
||||
)
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_PROXY,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DNS),
|
||||
domain = null
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DOMESTIC_DNS),
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to configure DNS", e)
|
||||
return false
|
||||
@@ -1014,14 +1011,22 @@ object V2rayConfigManager {
|
||||
if (domain.isNullOrEmpty()) continue
|
||||
|
||||
if (newHosts.containsKey(domain)) {
|
||||
item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6"
|
||||
item.ensureSockopt().domainStrategy = "UseIP"
|
||||
item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
|
||||
prioritizeIPv6 = preferIpv6,
|
||||
interleave = 2
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
val resolvedIps = HttpUtil.resolveHostToIP(domain, preferIpv6)
|
||||
if (resolvedIps.isNullOrEmpty()) continue
|
||||
|
||||
item.ensureSockopt().domainStrategy = if (preferIpv6) "UseIPv6v4" else "UseIPv4v6"
|
||||
item.ensureSockopt().domainStrategy = "UseIP"
|
||||
item.ensureSockopt().happyEyeballs = StreamSettingsBean.happyEyeballsBean(
|
||||
prioritizeIPv6 = preferIpv6,
|
||||
interleave = 2
|
||||
)
|
||||
newHosts[domain] = if (resolvedIps.size == 1) {
|
||||
resolvedIps[0]
|
||||
} else {
|
||||
|
||||
@@ -386,6 +386,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
||||
}
|
||||
|
||||
R.id.intelligent_selection_all -> {
|
||||
if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") != "0") {
|
||||
toast(getString(R.string.pre_resolving_domain))
|
||||
}
|
||||
mainViewModel.createIntelligentSelectionAll()
|
||||
true
|
||||
}
|
||||
|
||||
@@ -56,8 +56,14 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
appsList.sortedWith { p1, p2 ->
|
||||
when {
|
||||
p1.isSelected > p2.isSelected -> -1
|
||||
p1.isSelected == p2.isSelected -> 0
|
||||
else -> 1
|
||||
p1.isSelected < p2.isSelected -> 1
|
||||
p1.isSystemApp > p2.isSystemApp -> 1
|
||||
p1.isSystemApp < p2.isSystemApp -> -1
|
||||
p1.appName.lowercase() > p2.appName.lowercase() -> 1
|
||||
p1.appName.lowercase() < p2.appName.lowercase() -> -1
|
||||
p1.packageName > p2.packageName -> 1
|
||||
p1.packageName < p2.packageName -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -12,7 +12,9 @@ import java.net.IDN
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.MalformedURLException
|
||||
import java.net.Proxy
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
|
||||
object HttpUtil {
|
||||
@@ -140,7 +142,7 @@ object HttpUtil {
|
||||
val responseCode = conn.responseCode
|
||||
when (responseCode) {
|
||||
in 300..399 -> {
|
||||
val location = conn.getHeaderField("Location")
|
||||
val location = resolveLocation(conn)
|
||||
conn.disconnect()
|
||||
if (location.isNullOrEmpty()) {
|
||||
throw IOException("Redirect location not found")
|
||||
@@ -219,5 +221,29 @@ object HttpUtil {
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
// Returns absolute URL string location header sets
|
||||
fun resolveLocation(conn: HttpURLConnection): String? {
|
||||
val raw = conn.getHeaderField("Location")?.trim()?.takeIf { it.isNotEmpty() } ?: return null
|
||||
|
||||
// Try check url is relative or absolute
|
||||
return try {
|
||||
val locUri = URI(raw)
|
||||
val baseUri = conn.url.toURI()
|
||||
val resolved = if (locUri.isAbsolute) locUri else baseUri.resolve(locUri)
|
||||
resolved.toURL().toString()
|
||||
} catch (_: Exception) {
|
||||
// Fallback: url resolver, also should handles //host/...
|
||||
try {
|
||||
URL(raw).toString() // absolute with protocol
|
||||
} catch (_: MalformedURLException) {
|
||||
try {
|
||||
URL(conn.url, raw).toString()
|
||||
} catch (_: MalformedURLException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
AppConfig.PREF_VPN_DNS,
|
||||
AppConfig.PREF_VPN_BYPASS_LAN,
|
||||
AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX,
|
||||
AppConfig.PREF_VPN_MTU,
|
||||
AppConfig.PREF_REMOTE_DNS,
|
||||
AppConfig.PREF_DOMESTIC_DNS,
|
||||
AppConfig.PREF_DNS_HOSTS,
|
||||
|
||||
@@ -377,5 +377,6 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -382,5 +382,6 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
@@ -392,5 +392,6 @@
|
||||
<item>کم ترین پینگ</item>
|
||||
<item>کم ترین بار(لود)</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -391,5 +391,6 @@
|
||||
<item>کمترین پینگ</item>
|
||||
<item>کمترین بار(لود)</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
<string name="title_pref_vpn_bypass_lan">VPN обходит LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">Адрес интерфейса VPN</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (по умолчанию 1500)</string>
|
||||
|
||||
<string name="title_pref_domestic_dns">Внутренняя DNS (необязательно)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
@@ -198,7 +198,7 @@
|
||||
<string name="summary_pref_proxy_sharing_enabled">Другие устройства могут подключаться, используя ваш IP-адрес, чтобы использовать локальный прокси. Используйте только в доверенной сети, чтобы избежать несанкционированного подключения.</string>
|
||||
<string name="toast_warning_pref_proxysharing_short">Доступ из LAN разрешён, убедитесь, что вы находитесь в доверенной сети</string>
|
||||
|
||||
<string name="title_pref_allow_insecure">Разрешать небезопасные</string>
|
||||
<string name="title_pref_allow_insecure">Разрешать небезопасные соединения</string>
|
||||
<string name="summary_pref_allow_insecure">Для TLS по умолчанию разрешены небезопасные соединения</string>
|
||||
|
||||
<string name="title_pref_socks_port">Порт локального прокси</string>
|
||||
@@ -391,5 +391,6 @@
|
||||
<item>Наименьшая задержка</item>
|
||||
<item>Наименьшая нагрузка</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Предварительное определение домена…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -379,5 +379,6 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -383,5 +383,6 @@
|
||||
<item>最低延迟</item>
|
||||
<item>最稳定</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">预解析域名中…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -383,5 +383,6 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -185,8 +185,10 @@
|
||||
<string name="title_pref_vpn_bypass_lan">Does VPN bypass LAN</string>
|
||||
|
||||
<string name="title_pref_vpn_interface_address">VPN Interface Address</string>
|
||||
|
||||
<string name="title_pref_vpn_mtu">VPN MTU (default 1500)</string>
|
||||
|
||||
|
||||
<string name="title_pref_domestic_dns">Domestic DNS (Optional)</string>
|
||||
<string name="summary_pref_domestic_dns">DNS</string>
|
||||
|
||||
@@ -393,5 +395,6 @@
|
||||
<item>Least Ping</item>
|
||||
<item>Least Load</item>
|
||||
</string-array>
|
||||
<string name="pre_resolving_domain">Pre-resolving domain…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[versions]
|
||||
agp = "8.12.0"
|
||||
agp = "8.12.1"
|
||||
desugarJdkLibs = "2.1.5"
|
||||
gradleLicensePlugin = "0.9.8"
|
||||
kotlin = "2.2.0"
|
||||
kotlin = "2.2.10"
|
||||
coreKtx = "1.16.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
appcompat = "1.7.1"
|
||||
material = "1.12.0"
|
||||
activity = "1.10.1"
|
||||
@@ -20,7 +20,7 @@ swiperefreshlayout = "1.1.0"
|
||||
toasty = "1.5.2"
|
||||
editorkit = "2.9.0"
|
||||
core = "3.5.3"
|
||||
workRuntimeKtx = "2.10.2"
|
||||
workRuntimeKtx = "2.10.3"
|
||||
lifecycleViewmodelKtx = "2.9.2"
|
||||
multidex = "2.0.1"
|
||||
mockitoMockitoInline = "5.2.0"
|
||||
|
||||
Reference in New Issue
Block a user