From 55a6157af041c34d998b1854219c088ab9b2cdb7 Mon Sep 17 00:00:00 2001 From: Barracuda72 Date: Mon, 2 Mar 2026 18:55:31 +0500 Subject: [PATCH] Implement IPv4-only mode for RPC connection --- .../ServerEditFragment.kt | 13 +++++++++++++ app/src/main/res/values/strings.xml | 2 ++ .../tremotesf/rpc/ConnectionConfiguration.kt | 1 + .../org/equeim/tremotesf/rpc/DnsFilter.kt | 17 +++++++++++++++++ .../kotlin/org/equeim/tremotesf/rpc/Server.kt | 2 ++ 5 files changed, 35 insertions(+) create mode 100644 rpc/src/main/kotlin/org/equeim/tremotesf/rpc/DnsFilter.kt diff --git a/app/src/main/kotlin/org/equeim/tremotesf/ui/connectionsettingsfragment/ServerEditFragment.kt b/app/src/main/kotlin/org/equeim/tremotesf/ui/connectionsettingsfragment/ServerEditFragment.kt index 702b13573..ca5b5ea49 100644 --- a/app/src/main/kotlin/org/equeim/tremotesf/ui/connectionsettingsfragment/ServerEditFragment.kt +++ b/app/src/main/kotlin/org/equeim/tremotesf/ui/connectionsettingsfragment/ServerEditFragment.kt @@ -130,6 +130,7 @@ class ServerEditFragment : ComposeFragment() { name = model.name, address = model.address, port = model.port, + useIPv4Only = model.useIPv4Only, httpsEnabled = model.httpsEnabled, apiPath = model.apiPath, authentication = model.authentication, @@ -158,6 +159,7 @@ private fun ServerEditScreen( name: MutableState, address: MutableState, port: TremotesfIntegerNumberInputFieldState, + useIPv4Only: MutableState, httpsEnabled: MutableState, apiPath: MutableState, authentication: MutableState, @@ -329,6 +331,14 @@ private fun ServerEditScreen( .padding(horizontal = horizontalPadding) ) + TremotesfSwitchWithText( + checked = useIPv4Only.value, + text = R.string.use_ipv4_only, + onCheckedChange = useIPv4Only::value::set, + modifier = Modifier.fillMaxWidth(), + horizontalContentPadding = horizontalPadding + ) + TremotesfSwitchWithText( checked = httpsEnabled.value, text = R.string.use_https_protocol, @@ -642,6 +652,7 @@ private fun ServerEditScreenPreview() = ScreenPreview { name = remember { mutableStateOf("hmm") }, address = remember { mutableStateOf("4.2.4.2") }, port = rememberTremotesfIntegerNumberInputFieldState(42), + useIPv4Only = remember { mutableStateOf(false) }, httpsEnabled = remember { mutableStateOf(false) }, apiPath = remember { mutableStateOf("/lol") }, authentication = remember { mutableStateOf(false) }, @@ -679,6 +690,7 @@ class ServerEditFragmentViewModel( val port by savedStateHandle.saveable(saver = TremotesfIntegerNumberInputFieldState.Saver()) { TremotesfIntegerNumberInputFieldState((editingServer.port).toLong()) } + val useIPv4Only by savedStateHandle.saveable> { mutableStateOf(editingServer.useIPv4Only) } val httpsEnabled by savedStateHandle.saveable> { mutableStateOf(editingServer.httpsEnabled) } val apiPath by savedStateHandle.saveable> { mutableStateOf(editingServer.apiPath) } val authentication by savedStateHandle.saveable> { mutableStateOf(editingServer.authentication) } @@ -782,6 +794,7 @@ class ServerEditFragmentViewModel( proxyUser = proxyUser.value.trim(), proxyPassword = proxyPassword.value.trim(), + useIPv4Only = useIPv4Only.value, httpsEnabled = httpsEnabled.value, selfSignedCertificateEnabled = selfSignedCertificateEnabled.value, selfSignedCertificate = selfSignedCertificate.value.trim(), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7fff89bf0..951acb864 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -565,4 +565,6 @@ SPDX-License-Identifier: GPL-3.0-or-later Sort order: Ascending Descending + + Use IPv4 only diff --git a/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/ConnectionConfiguration.kt b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/ConnectionConfiguration.kt index 0d77d8527..09586cb14 100644 --- a/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/ConnectionConfiguration.kt +++ b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/ConnectionConfiguration.kt @@ -40,6 +40,7 @@ internal fun createConnectionConfiguration(server: Server, retryOnConnectionFail InetSocketAddress.createUnresolved(server.proxyHostname, server.proxyPort) ) }) + .dns(DnsFilter(server.useIPv4Only)) var clientCertificates: List = emptyList() val clientCertificate = if (server.clientCertificateEnabled) server.clientCertificate.takeIf { it.isNotBlank() } else null val serverCertificate = if (server.selfSignedCertificateEnabled) server.selfSignedCertificate.takeIf { it.isNotBlank() } else null diff --git a/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/DnsFilter.kt b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/DnsFilter.kt new file mode 100644 index 000000000..62b0b36c7 --- /dev/null +++ b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/DnsFilter.kt @@ -0,0 +1,17 @@ +package org.equeim.tremotesf.rpc + +import okhttp3.Dns +import java.net.InetAddress +import java.net.Inet4Address + +class DnsFilter(private val useIPv4Only: Boolean) : Dns { + override fun lookup(hostname: String): List { + var addresses = Dns.SYSTEM.lookup(hostname) + + if (useIPv4Only) { + addresses = addresses.filter { Inet4Address::class.java.isInstance(it) } + } + + return addresses + } +} diff --git a/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/Server.kt b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/Server.kt index e6f3504da..f30b390f9 100644 --- a/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/Server.kt +++ b/rpc/src/main/kotlin/org/equeim/tremotesf/rpc/Server.kt @@ -52,6 +52,8 @@ data class Server( @SerialName("proxyPassword") val proxyPassword: String = "", + @SerialName("useIPv4Only") + val useIPv4Only: Boolean = false, @SerialName("httpsEnabled") val httpsEnabled: Boolean = false, @SerialName("selfSignedCertificateEnabled")