Resolve remote host names in the configuration file to IP addresses
This commit is contained in:
@@ -4,6 +4,8 @@ import com.v2ray.ang.AppConfig
|
|||||||
import com.v2ray.ang.dto.NetworkType
|
import com.v2ray.ang.dto.NetworkType
|
||||||
import com.v2ray.ang.dto.ProfileItem
|
import com.v2ray.ang.dto.ProfileItem
|
||||||
import com.v2ray.ang.extension.isNotNullEmpty
|
import com.v2ray.ang.extension.isNotNullEmpty
|
||||||
|
import com.v2ray.ang.handler.MmkvManager
|
||||||
|
import com.v2ray.ang.util.HttpUtil
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
@@ -148,4 +150,9 @@ open class FmtBase {
|
|||||||
|
|
||||||
return dicQuery
|
return dicQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resolveHostToIP(server: String?): String {
|
||||||
|
return HttpUtil.resolveHostToIP(server.orEmpty(), MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6) == true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ object HttpFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.HTTP)
|
val outboundBean = OutboundBean.create(EConfigType.HTTP)
|
||||||
|
|
||||||
outboundBean?.settings?.servers?.first()?.let { server ->
|
outboundBean?.settings?.servers?.first()?.let { server ->
|
||||||
server.address = profileItem.server.orEmpty()
|
server.address = resolveHostToIP(profileItem.server)
|
||||||
server.port = profileItem.serverPort.orEmpty().toInt()
|
server.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
if (profileItem.username.isNotNullEmpty()) {
|
if (profileItem.username.isNotNullEmpty()) {
|
||||||
val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ object ShadowsocksFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS)
|
val outboundBean = OutboundBean.create(EConfigType.SHADOWSOCKS)
|
||||||
|
|
||||||
outboundBean?.settings?.servers?.first()?.let { server ->
|
outboundBean?.settings?.servers?.first()?.let { server ->
|
||||||
server.address = profileItem.server.orEmpty()
|
server.address = resolveHostToIP(profileItem.server)
|
||||||
server.port = profileItem.serverPort.orEmpty().toInt()
|
server.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
server.password = profileItem.password
|
server.password = profileItem.password
|
||||||
server.method = profileItem.method
|
server.method = profileItem.method
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ object SocksFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.SOCKS)
|
val outboundBean = OutboundBean.create(EConfigType.SOCKS)
|
||||||
|
|
||||||
outboundBean?.settings?.servers?.first()?.let { server ->
|
outboundBean?.settings?.servers?.first()?.let { server ->
|
||||||
server.address = profileItem.server.orEmpty()
|
server.address = resolveHostToIP(profileItem.server)
|
||||||
server.port = profileItem.serverPort.orEmpty().toInt()
|
server.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
if (profileItem.username.isNotNullEmpty()) {
|
if (profileItem.username.isNotNullEmpty()) {
|
||||||
val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ object TrojanFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.TROJAN)
|
val outboundBean = OutboundBean.create(EConfigType.TROJAN)
|
||||||
|
|
||||||
outboundBean?.settings?.servers?.first()?.let { server ->
|
outboundBean?.settings?.servers?.first()?.let { server ->
|
||||||
server.address = profileItem.server.orEmpty()
|
server.address = resolveHostToIP(profileItem.server)
|
||||||
server.port = profileItem.serverPort.orEmpty().toInt()
|
server.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
server.password = profileItem.password
|
server.password = profileItem.password
|
||||||
server.flow = profileItem.flow
|
server.flow = profileItem.flow
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ object VlessFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.VLESS)
|
val outboundBean = OutboundBean.create(EConfigType.VLESS)
|
||||||
|
|
||||||
outboundBean?.settings?.vnext?.first()?.let { vnext ->
|
outboundBean?.settings?.vnext?.first()?.let { vnext ->
|
||||||
vnext.address = profileItem.server.orEmpty()
|
vnext.address = resolveHostToIP(profileItem.server)
|
||||||
vnext.port = profileItem.serverPort.orEmpty().toInt()
|
vnext.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
vnext.users[0].id = profileItem.password.orEmpty()
|
vnext.users[0].id = profileItem.password.orEmpty()
|
||||||
vnext.users[0].encryption = profileItem.method
|
vnext.users[0].encryption = profileItem.method
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ object VmessFmt : FmtBase() {
|
|||||||
val outboundBean = OutboundBean.create(EConfigType.VMESS)
|
val outboundBean = OutboundBean.create(EConfigType.VMESS)
|
||||||
|
|
||||||
outboundBean?.settings?.vnext?.first()?.let { vnext ->
|
outboundBean?.settings?.vnext?.first()?.let { vnext ->
|
||||||
vnext.address = profileItem.server.orEmpty()
|
vnext.address = resolveHostToIP(profileItem.server)
|
||||||
vnext.port = profileItem.serverPort.orEmpty().toInt()
|
vnext.port = profileItem.serverPort.orEmpty().toInt()
|
||||||
vnext.users[0].id = profileItem.password.orEmpty()
|
vnext.users[0].id = profileItem.password.orEmpty()
|
||||||
vnext.users[0].security = profileItem.method
|
vnext.users[0].security = profileItem.method
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ object WireguardFmt : FmtBase() {
|
|||||||
wireguard.peers?.firstOrNull()?.let { peer ->
|
wireguard.peers?.firstOrNull()?.let { peer ->
|
||||||
peer.publicKey = profileItem.publicKey.orEmpty()
|
peer.publicKey = profileItem.publicKey.orEmpty()
|
||||||
peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() }
|
peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() }
|
||||||
peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
|
peer.endpoint = Utils.getIpv6Address(resolveHostToIP(profileItem.server)) + ":${profileItem.serverPort}"
|
||||||
}
|
}
|
||||||
wireguard.mtu = profileItem.mtu
|
wireguard.mtu = profileItem.mtu
|
||||||
wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() }
|
wireguard.reserved = profileItem.reserved?.takeIf { it.isNotBlank() }?.split(",")?.filter { it.isNotBlank() }?.map { it.trim().toInt() }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.v2ray.ang.handler.MmkvManager
|
|||||||
import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
||||||
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
||||||
import com.v2ray.ang.service.V2RayServiceManager
|
import com.v2ray.ang.service.V2RayServiceManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -220,10 +221,15 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
* @param guid The server unique identifier
|
* @param guid The server unique identifier
|
||||||
*/
|
*/
|
||||||
private fun shareFullContent(guid: String) {
|
private fun shareFullContent(guid: String) {
|
||||||
if (AngConfigManager.shareFullContent2Clipboard(mActivity, guid) == 0) {
|
mActivity.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
mActivity.toastSuccess(R.string.toast_success)
|
val result = AngConfigManager.shareFullContent2Clipboard(mActivity, guid)
|
||||||
} else {
|
launch(Dispatchers.Main) {
|
||||||
mActivity.toastError(R.string.toast_failure)
|
if (result == 0) {
|
||||||
|
mActivity.toastSuccess(R.string.toast_success)
|
||||||
|
} else {
|
||||||
|
mActivity.toastError(R.string.toast_failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import com.v2ray.ang.util.Utils.urlDecode
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.IDN
|
import java.net.IDN
|
||||||
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
object HttpUtil {
|
object HttpUtil {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a URL string to its ASCII representation.
|
* Converts a URL string to its ASCII representation.
|
||||||
*
|
*
|
||||||
@@ -33,6 +33,44 @@ object HttpUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a hostname to an IP address, returns original input if it's already an IP
|
||||||
|
*
|
||||||
|
* @param host The hostname or IP address to resolve
|
||||||
|
* @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 {
|
||||||
|
try {
|
||||||
|
// If it's already an IP address, return it directly
|
||||||
|
if (Utils.isPureIpAddress(host)) {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all IP addresses
|
||||||
|
val addresses = InetAddress.getAllByName(host)
|
||||||
|
if (addresses.isEmpty()) {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort addresses based on preference
|
||||||
|
val sortedAddresses = if (ipv6Preferred) {
|
||||||
|
// IPv6 preferred (size 16 first, then size 4)
|
||||||
|
addresses.sortedByDescending { it.address.size }
|
||||||
|
} else {
|
||||||
|
// IPv4 preferred (size 4 first, then size 16)
|
||||||
|
addresses.sortedBy { it.address.size }
|
||||||
|
}
|
||||||
|
Log.i(AppConfig.TAG, "Resolved IPs for $host: ${sortedAddresses.joinToString { it.hostAddress ?: "unknown" }}")
|
||||||
|
|
||||||
|
// Return the first address after sorting
|
||||||
|
return sortedAddresses.first().hostAddress ?: host
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to resolve host to IP", e)
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the content of a URL as a string.
|
* Retrieves the content of a URL as a string.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user