Generate Android autosockets per selected protocol and retain ports
This commit is contained in:
1 parent
ae85423e39
commit
eda65ece52
2 files changed
+50
-22
No files matched your search
@@ -21,7 +21,6 @@ import java.net.NetworkInterface
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
private val Context.dataStore by preferencesDataStore("utun_config")
|
||||
|
||||
@@ -82,6 +81,7 @@ class ConfigProvider(private val context: Context) {
|
||||
private val chatLocalBroadcastsEnabled = intPreferencesKey("chat_local_broadcasts_enabled")
|
||||
private val firstLaunchDone = booleanPreferencesKey("first_launch_done")
|
||||
private val connectionModeKey = stringPreferencesKey("connection_mode")
|
||||
private val autoSocketProtocolsKey = stringPreferencesKey("auto_socket_protocols")
|
||||
private val serviceEnabledKey = booleanPreferencesKey("service_enabled")
|
||||
private val startOnBootKey = booleanPreferencesKey("start_on_boot")
|
||||
private val soundEnabledKey = booleanPreferencesKey("sound_enabled")
|
||||
@@ -430,6 +430,19 @@ class ConfigProvider(private val context: Context) {
|
||||
context.dataStore.edit { prefs -> prefs[connectionModeKey] = mode }
|
||||
}
|
||||
|
||||
fun getAutoSocketProtocols(): AutoSocketProtocols = runBlocking {
|
||||
val value = context.dataStore.data.first()[autoSocketProtocolsKey] ?: return@runBlocking AutoSocketProtocols.BOTH
|
||||
AutoSocketProtocols.values().firstOrNull { it.configValue == value } ?: run {
|
||||
LogManager.addLog("WARN", "CFG", "invalid auto_socket_protocols='$value', using tcp+udp")
|
||||
AutoSocketProtocols.BOTH
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setAutoSocketProtocols(protocols: AutoSocketProtocols) {
|
||||
context.dataStore.edit { prefs -> prefs[autoSocketProtocolsKey] = protocols.configValue }
|
||||
LogManager.addLog("INFO", "CFG", "auto_socket_protocols=${protocols.configValue}")
|
||||
}
|
||||
|
||||
/* ── Background service enabled/disabled ── */
|
||||
|
||||
val serviceEnabledFlow: Flow<Boolean> = context.dataStore.data.map { it[serviceEnabledKey] ?: true }
|
||||
@@ -584,6 +597,7 @@ class ConfigProvider(private val context: Context) {
|
||||
suspend fun detectInterfaces(saved: List<ServerEntry> = emptyList()): List<ServerEntry> {
|
||||
val servers = mutableListOf<ServerEntry>()
|
||||
val seen = mutableSetOf<String>()
|
||||
val protocols = getAutoSocketProtocols()
|
||||
try {
|
||||
val nis = NetworkInterface.getNetworkInterfaces()
|
||||
while (nis.hasMoreElements()) {
|
||||
@@ -599,16 +613,7 @@ class ConfigProvider(private val context: Context) {
|
||||
val isV6 = ip.contains(":")
|
||||
val key = "${ni.name}/${if (isV6) "v6" else "v4"}"
|
||||
if (!seen.add(key)) continue
|
||||
val savedPort = saved.firstOrNull {
|
||||
it.ifName == ni.name && it.bindIp.contains(":") == isV6
|
||||
}?.port
|
||||
servers.add(ServerEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
ifName = ni.name,
|
||||
bindIp = ip,
|
||||
protocol = "tcp",
|
||||
port = savedPort ?: ThreadLocalRandom.current().nextInt(2000, 10000)
|
||||
))
|
||||
servers.addAll(protocols.createServers(ni.name, ip, saved))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -636,12 +641,13 @@ class ConfigProvider(private val context: Context) {
|
||||
val trimmed = line.trim()
|
||||
val commented = trimmed.startsWith("#")
|
||||
val body = if (commented) trimmed.removePrefix("#").trim() else trimmed
|
||||
if (body.startsWith("[server:") && body.endsWith("]")) {
|
||||
if (body.startsWith("[") && body.endsWith("]")) {
|
||||
if (sectionOpen && ifName.isNotEmpty() && addrPort > 0 && (!sectionCommented || includeCommented))
|
||||
raw.add(RawSrv(ifName, family, addrPort, proto))
|
||||
sectionOpen = false; sectionCommented = commented; addrPort = 0; proto = "tcp"; ifName = ""; family = ""
|
||||
if (!body.startsWith("[server:")) continue
|
||||
if (commented && !includeCommented) continue
|
||||
val section = body.removeSurrounding("[", "]").removePrefix("server:")
|
||||
val section = body.removeSurrounding("[", "]").removePrefix("server:").removeSuffix("_tcp").removeSuffix("_udp")
|
||||
val lastUnderscore = section.lastIndexOf('_')
|
||||
if (lastUnderscore > 0) {
|
||||
ifName = section.substring(0, lastUnderscore)
|
||||
@@ -658,7 +664,10 @@ class ConfigProvider(private val context: Context) {
|
||||
}
|
||||
if (sectionOpen && ifName.isNotEmpty() && addrPort > 0 && (!sectionCommented || includeCommented))
|
||||
raw.add(RawSrv(ifName, family, addrPort, proto))
|
||||
} catch (_: Exception) { return emptyList() }
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "CFG", "loadServersFromConfig failed path=${file.path}: ${e.message}")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val liveIps = getAvailableInterfaces()
|
||||
return raw.map { rs ->
|
||||
@@ -694,6 +703,7 @@ class ConfigProvider(private val context: Context) {
|
||||
appendLine("db_sync_enabled=1")
|
||||
appendLine("chatserver_enabled=1")
|
||||
appendLine("auto_sockets=android")
|
||||
appendLine("auto_socket_protocols=${getAutoSocketProtocols().configValue}")
|
||||
appendLine()
|
||||
for (srv in servers) appendServerSection(this, srv, false)
|
||||
for (srv in commented) appendServerSection(this, srv, true)
|
||||
@@ -777,7 +787,7 @@ class ConfigProvider(private val context: Context) {
|
||||
val family = if (srv.bindIp.contains(":")) "v6" else "v4"
|
||||
val p = if (comment) "#" else ""
|
||||
val addr = if (family == "v6") "[::]" else srv.bindIp.ifBlank { "0.0.0.0" }
|
||||
sb.appendLine("${p}[server:${srv.ifName}_$family]")
|
||||
sb.appendLine("${p}[server:${srv.sectionName}]")
|
||||
sb.appendLine("${p}addr=$addr:${srv.port}")
|
||||
sb.appendLine("${p}type=public")
|
||||
sb.appendLine("${p}transport=${srv.protocol}")
|
||||
@@ -814,13 +824,13 @@ class ConfigProvider(private val context: Context) {
|
||||
val cfgFile = File(configFilePath())
|
||||
val saved = if (cfgFile.exists()) loadServersFromConfig(includeCommented = true) else emptyList()
|
||||
val detected = runBlocking { detectInterfaces(saved) }
|
||||
LogManager.addLog("INFO", "CFG", "buildConfigText: auto mode, detected ${detected.size} interfaces" +
|
||||
LogManager.addLog("INFO", "CFG", "buildConfigText: auto mode protocols=${getAutoSocketProtocols().configValue}, ${detected.size} sockets: " +
|
||||
detected.joinToString(", ") { "${it.ifName}=${it.protocol}:${it.port}" })
|
||||
val upKeys = detected.map { it.ifName to it.bindIp.contains(":") }.toSet()
|
||||
val down = saved.filter { (it.ifName to it.bindIp.contains(":")) !in upKeys }
|
||||
val upKeys = detected.map { it.socketKey }.toSet()
|
||||
val down = saved.filter { it.socketKey !in upKeys }.distinctBy { it.socketKey }
|
||||
if (down.isNotEmpty()) {
|
||||
LogManager.addLog("INFO", "CFG", "buildConfigText: ${down.size} interfaces down, port preserved" +
|
||||
down.joinToString(", ") { "${it.ifName}=${it.port}" })
|
||||
LogManager.addLog("INFO", "CFG", "buildConfigText: ${down.size} sockets inactive, ports preserved: " +
|
||||
down.joinToString(", ") { "${it.ifName}=${it.protocol}:${it.port}" })
|
||||
}
|
||||
saveServersToConfig(detected, down)
|
||||
return buildConfigFromServers(detected)
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/** Протоколы автосокетов. Порты сохраняются отдельно для интерфейса, семейства IP и протокола. */
|
||||
enum class AutoSocketProtocols(val configValue: String, val protocols: List<String>) {
|
||||
TCP("tcp", listOf("tcp")),
|
||||
UDP("udp", listOf("udp")),
|
||||
BOTH("tcp+udp", listOf("tcp", "udp"));
|
||||
|
||||
fun createServers(ifName: String, bindIp: String, saved: List<ServerEntry>): List<ServerEntry> = protocols.map { protocol ->
|
||||
val key = Triple(ifName, bindIp.contains(":"), protocol)
|
||||
val port = saved.firstOrNull { it.socketKey == key }?.port ?: (2000..9999).random()
|
||||
ServerEntry(UUID.randomUUID().toString(), ifName, bindIp, protocol, port)
|
||||
}
|
||||
}
|
||||
|
||||
data class ServerEntry(
|
||||
val id: String, // UUID
|
||||
val ifName: String, // wlan0, rmnet0, ...
|
||||
val bindIp: String, // IPv4 address
|
||||
val bindIp: String, // IPv4 или IPv6 адрес
|
||||
val protocol: String, // "tcp" or "udp"
|
||||
val port: Int
|
||||
)
|
||||
) {
|
||||
val socketKey: Triple<String, Boolean, String> get() = Triple(ifName, bindIp.contains(":"), protocol)
|
||||
val sectionName: String get() = "${ifName}_${if (bindIp.contains(":")) "v6" else "v4"}_$protocol"
|
||||
}
|
||||
Reference in new issue
Block a user