diff --git a/android/app/src/main/java/com/masterdns/vpn/service/MasterDnsVpnService.kt b/android/app/src/main/java/com/masterdns/vpn/service/MasterDnsVpnService.kt index 8f2200f..7554c35 100644 --- a/android/app/src/main/java/com/masterdns/vpn/service/MasterDnsVpnService.kt +++ b/android/app/src/main/java/com/masterdns/vpn/service/MasterDnsVpnService.kt @@ -60,7 +60,25 @@ class MasterDnsVpnService : VpnService() { "com.android.chrome" // system Chrome on some OEMs ) - + internal fun parseConnectTarget(url: String): Pair { + if (url.startsWith("[")) { + val close = url.indexOf("]") + if (close > 0) { + val host = url.substring(1, close) + val portPart = url.substring(close + 1).removePrefix(":") + val port = portPart.toIntOrNull()?.coerceIn(1, 65535) ?: 80 + return host to port + } + } + val lastColon = url.lastIndexOf(':') + return if (lastColon > 0) { + val host = url.substring(0, lastColon) + val port = url.substring(lastColon + 1).toIntOrNull()?.coerceIn(1, 65535) ?: 80 + host to port + } else { + url to 80 + } + } } private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) @@ -122,6 +140,19 @@ class MasterDnsVpnService : VpnService() { val profileId = intent.getLongExtra(EXTRA_PROFILE_ID, -1) if (profileId > 0) { startVpn(profileId) + } else { + val msg = "Invalid profile id: $profileId" + VpnManager.appendLog(msg) + VpnManager.setError(msg) + runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_REMOVE) + } else { + @Suppress("DEPRECATION") + stopForeground(true) + } + } + runCatching { stopSelf() } } } ACTION_DISCONNECT -> { @@ -979,9 +1010,7 @@ class MasterDnsVpnService : VpnService() { } if (method == "CONNECT") { - val hostPort = url.split(":") - val host = hostPort[0] - val port = hostPort.getOrElse(1) { "80" }.toIntOrNull() ?: 80 + val (host, port) = parseConnectTarget(url) output.write("HTTP/1.1 200 Connection Established\r\n\r\n") output.flush() diff --git a/android/app/src/main/java/com/masterdns/vpn/util/GlobalSettingsStore.kt b/android/app/src/main/java/com/masterdns/vpn/util/GlobalSettingsStore.kt index 204f1a0..245a1ce 100644 --- a/android/app/src/main/java/com/masterdns/vpn/util/GlobalSettingsStore.kt +++ b/android/app/src/main/java/com/masterdns/vpn/util/GlobalSettingsStore.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow @@ -38,8 +39,8 @@ object GlobalSettingsStore { private val KEY_CUSTOM_DNS_SERVERS = stringPreferencesKey("custom_dns_servers") private val KEY_FAKE_DNS_ENABLED = booleanPreferencesKey("fake_dns_enabled") private val KEY_INTERNET_SHARING_ENABLED = booleanPreferencesKey("internet_sharing_enabled") - private val KEY_INTERNET_SHARING_SOCKS_PORT = stringPreferencesKey("internet_sharing_socks_port") - private val KEY_INTERNET_SHARING_HTTP_PORT = stringPreferencesKey("internet_sharing_http_port") + private val KEY_INTERNET_SHARING_SOCKS_PORT = intPreferencesKey("internet_sharing_socks_port_v2") + private val KEY_INTERNET_SHARING_HTTP_PORT = intPreferencesKey("internet_sharing_http_port_v2") private val KEY_INTERNET_SHARING_USER = stringPreferencesKey("internet_sharing_user") private val KEY_INTERNET_SHARING_PASS = stringPreferencesKey("internet_sharing_pass") @@ -63,8 +64,8 @@ object GlobalSettingsStore { prefs[KEY_CUSTOM_DNS_SERVERS] = settings.customDnsServers prefs[KEY_FAKE_DNS_ENABLED] = settings.fakeDnsEnabled prefs[KEY_INTERNET_SHARING_ENABLED] = settings.internetSharingEnabled - prefs[KEY_INTERNET_SHARING_SOCKS_PORT] = settings.internetSharingSocksPort.toString() - prefs[KEY_INTERNET_SHARING_HTTP_PORT] = settings.internetSharingHttpPort.toString() + prefs[KEY_INTERNET_SHARING_SOCKS_PORT] = settings.internetSharingSocksPort.coerceIn(1, 65535) + prefs[KEY_INTERNET_SHARING_HTTP_PORT] = settings.internetSharingHttpPort.coerceIn(1, 65535) prefs[KEY_INTERNET_SHARING_USER] = settings.internetSharingUser prefs[KEY_INTERNET_SHARING_PASS] = settings.internetSharingPass } @@ -80,8 +81,8 @@ object GlobalSettingsStore { customDnsServers = this[KEY_CUSTOM_DNS_SERVERS] ?: "", fakeDnsEnabled = this[KEY_FAKE_DNS_ENABLED] ?: true, internetSharingEnabled = this[KEY_INTERNET_SHARING_ENABLED] ?: false, - internetSharingSocksPort = this[KEY_INTERNET_SHARING_SOCKS_PORT]?.toIntOrNull() ?: 8090, - internetSharingHttpPort = this[KEY_INTERNET_SHARING_HTTP_PORT]?.toIntOrNull() ?: 8091, + internetSharingSocksPort = this[KEY_INTERNET_SHARING_SOCKS_PORT] ?: 8090, + internetSharingHttpPort = this[KEY_INTERNET_SHARING_HTTP_PORT] ?: 8091, internetSharingUser = this[KEY_INTERNET_SHARING_USER] ?: "", internetSharingPass = this[KEY_INTERNET_SHARING_PASS] ?: "" ) diff --git a/android/app/src/test/java/com/masterdns/vpn/service/MasterDnsVpnServiceConnectParseTest.kt b/android/app/src/test/java/com/masterdns/vpn/service/MasterDnsVpnServiceConnectParseTest.kt new file mode 100644 index 0000000..63a96d9 --- /dev/null +++ b/android/app/src/test/java/com/masterdns/vpn/service/MasterDnsVpnServiceConnectParseTest.kt @@ -0,0 +1,41 @@ +package com.masterdns.vpn.service + +import org.junit.Assert.assertEquals +import org.junit.Test + +class MasterDnsVpnServiceConnectParseTest { + @Test + fun parsesIpv6LiteralWithPort() { + val (host, port) = MasterDnsVpnService.parseConnectTarget("[::1]:443") + assertEquals("::1", host) + assertEquals(443, port) + } + + @Test + fun parsesIpv6LiteralWithoutPort() { + val (host, port) = MasterDnsVpnService.parseConnectTarget("[::1]") + assertEquals("::1", host) + assertEquals(80, port) + } + + @Test + fun parsesIpv4WithPort() { + val (host, port) = MasterDnsVpnService.parseConnectTarget("127.0.0.1:8080") + assertEquals("127.0.0.1", host) + assertEquals(8080, port) + } + + @Test + fun parsesDomainWithoutPort() { + val (host, port) = MasterDnsVpnService.parseConnectTarget("example.com") + assertEquals("example.com", host) + assertEquals(80, port) + } + + @Test + fun coercesOutOfRangePortToClampedValue() { + val (host, port) = MasterDnsVpnService.parseConnectTarget("example.com:99999") + assertEquals("example.com", host) + assertEquals(65535, port) + } +}