diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index cc8c713e..aa749c31 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -583,4 +583,9 @@ data class V2rayConfig( return null } + fun getAllProxyOutbound(): List { + return outbounds.filter { outbound -> + EConfigType.entries.any { it.name.equals(outbound.protocol, ignoreCase = true) } + } + } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index bc3229fe..9cea5544 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -302,8 +302,4 @@ open class FmtBase { } } - fun resolveHostToIP(server: String?): String { - return HttpUtil.resolveHostToIP(server.orEmpty(), MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true) - } - } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt index 2ff28d8c..faac0230 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/HttpFmt.kt @@ -16,7 +16,7 @@ object HttpFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.HTTP) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt index d0acc28d..e3c0edc5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/ShadowsocksFmt.kt @@ -134,7 +134,7 @@ object ShadowsocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.method = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt index fad0bb4c..f37030c2 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt @@ -63,7 +63,7 @@ object SocksFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.SOCKS) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() if (profileItem.username.isNotNullEmpty()) { val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt index 48dd8f4e..f9c06299 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/TrojanFmt.kt @@ -63,7 +63,7 @@ object TrojanFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.TROJAN) outboundBean?.settings?.servers?.first()?.let { server -> - server.address = resolveHostToIP(profileItem.server) + server.address = profileItem.server.orEmpty() server.port = profileItem.serverPort.orEmpty().toInt() server.password = profileItem.password server.flow = profileItem.flow diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt index 1438cd7e..ffa8f28e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VlessFmt.kt @@ -59,7 +59,7 @@ object VlessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VLESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = resolveHostToIP(profileItem.server) + vnext.address = profileItem.server.orEmpty() vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].encryption = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt index cb157e1e..3e56b84e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/VmessFmt.kt @@ -171,7 +171,7 @@ object VmessFmt : FmtBase() { val outboundBean = OutboundBean.create(EConfigType.VMESS) outboundBean?.settings?.vnext?.first()?.let { vnext -> - vnext.address = resolveHostToIP(profileItem.server) + vnext.address = profileItem.server.orEmpty() vnext.port = profileItem.serverPort.orEmpty().toInt() vnext.users[0].id = profileItem.password.orEmpty() vnext.users[0].security = profileItem.method diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt index 44ce1a04..e2bc5f90 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/WireguardFmt.kt @@ -113,7 +113,7 @@ object WireguardFmt : FmtBase() { wireguard.peers?.firstOrNull()?.let { peer -> peer.publicKey = profileItem.publicKey.orEmpty() peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() } - peer.endpoint = Utils.getIpv6Address(resolveHostToIP(profileItem.server)) + ":${profileItem.serverPort}" + peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}" } wireguard.mtu = profileItem.mtu wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index 51c66523..18b130df 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -46,6 +46,7 @@ import com.v2ray.ang.fmt.TrojanFmt import com.v2ray.ang.fmt.VlessFmt import com.v2ray.ang.fmt.VmessFmt import com.v2ray.ang.fmt.WireguardFmt +import com.v2ray.ang.util.HttpUtil import com.v2ray.ang.util.JsonUtil import com.v2ray.ang.util.Utils @@ -150,6 +151,8 @@ object V2rayConfigManager { v2rayConfig.policy = null } + resolveProxyDomainsToHosts(v2rayConfig) + result.status = true result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: "" result.domainPort = if (retMore.first) retMore.second else retOut.second @@ -763,4 +766,32 @@ object V2rayConfigManager { } + private fun resolveProxyDomainsToHosts(v2rayConfig: V2rayConfig) { + val proxyOutboundList = v2rayConfig.getAllProxyOutbound() + val dns = v2rayConfig.dns ?: return + + val newHosts = dns.hosts?.toMutableMap() ?: mutableMapOf() + + for (item in proxyOutboundList) { + val domain = item.getServerAddress() + if (domain.isNullOrEmpty()) continue + + if (newHosts.containsKey(domain)) continue + + val resolvedIps = HttpUtil.resolveHostToIP( + domain, + MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true + ) + + if (resolvedIps.isEmpty()) continue + + newHosts[domain] = if (resolvedIps.size == 1) { + resolvedIps[0] + } else { + resolvedIps + } + } + + dns.hosts = newHosts + } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt index b7caa4e4..d9c3d86b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/util/HttpUtil.kt @@ -10,6 +10,7 @@ import java.io.IOException import java.net.HttpURLConnection import java.net.IDN import java.net.InetAddress +import java.net.Inet6Address import java.net.InetSocketAddress import java.net.Proxy import java.net.URL @@ -40,37 +41,38 @@ object HttpUtil { * @param ipv6Preferred Whether to prefer IPv6 addresses, defaults to false * @return The resolved IP address or the original input (if it's already an IP or resolution fails) */ - fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): String { + fun resolveHostToIP(host: String, ipv6Preferred: Boolean = false): List { try { - // If it's already an IP address, return it directly + // If it's already an IP address, return it as a list if (Utils.isPureIpAddress(host)) { - return host + return listOf(host) } // Get all IP addresses val addresses = InetAddress.getAllByName(host) if (addresses.isEmpty()) { - return host + return emptyList() } // Sort addresses based on preference val sortedAddresses = if (ipv6Preferred) { - // IPv6 preferred (size 16 first, then size 4) - addresses.sortedByDescending { it.address.size } + addresses.sortedWith(compareByDescending { it is Inet6Address }) } else { - // IPv4 preferred (size 4 first, then size 16) - addresses.sortedBy { it.address.size } + addresses.sortedWith(compareBy { it is Inet6Address }) } - Log.i(AppConfig.TAG, "Resolved IPs for $host: ${sortedAddresses.joinToString { it.hostAddress ?: "unknown" }}") - // Return the first address after sorting - return sortedAddresses.first().hostAddress ?: host + val ipList = sortedAddresses.mapNotNull { it.hostAddress } + + Log.i(AppConfig.TAG, "Resolved IPs for $host: ${ipList.joinToString()}") + + return ipList } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to resolve host to IP", e) - return host + return emptyList() } } + /** * Retrieves the content of a URL as a string. *