Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import androidx.compose.runtime.LaunchedEffect
import java.net.InetAddress

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down Expand Up @@ -125,13 +126,35 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
}
return
}
val customDnsServers = parseCsv(draft.customDnsServers)
val invalidDnsServers = customDnsServers.filterNot(::isValidIpLiteral)
if (invalidDnsServers.isNotEmpty()) {
scope.launch {
snackbarHostState.showSnackbar(
context.getString(R.string.global_custom_dns_invalid_msg, invalidDnsServers.joinToString(", "))
)
}
return
}
if (draft.internetSharingEnabled &&
(draft.internetSharingUser.isBlank() || draft.internetSharingPass.isBlank())
) {
scope.launch {
snackbarHostState.showSnackbar(context.getString(R.string.global_sharing_credentials_required_msg))
}
return
}
val safeSocksPort = socksPortValue ?: return
val safeHttpPort = httpPortValue ?: return
draft = draft.copy(
val sanitized = draft.copy(
internetSharingSocksPort = safeSocksPort,
internetSharingHttpPort = safeHttpPort
internetSharingHttpPort = safeHttpPort,
customDnsServers = customDnsServers.joinToString(","),
internetSharingUser = draft.internetSharingUser.trim(),
internetSharingPass = draft.internetSharingPass.trim()
)
vm.save(normalize(draft))
draft = sanitized
vm.save(normalize(sanitized))
scope.launch { snackbarHostState.showSnackbar(context.getString(R.string.global_settings_saved_msg)) }
}

Expand Down Expand Up @@ -319,6 +342,11 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
color = MdvColor.PrimaryContainer
)
}
Text(
stringResource(R.string.global_sharing_lan_warning),
style = MaterialTheme.typography.bodySmall,
color = MdvColor.Error
)

Row(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -676,6 +704,9 @@ private fun parseCsv(value: String): Set<String> {
private fun normalize(settings: GlobalSettings): GlobalSettings {
return settings.copy(
connectionMode = settings.connectionMode.uppercase(),
customDnsServers = parseCsv(settings.customDnsServers).joinToString(","),
internetSharingUser = settings.internetSharingUser.trim(),
internetSharingPass = settings.internetSharingPass.trim(),
splitPackagesCsv = settings.splitPackagesCsv
.split(",")
.map { it.trim() }
Expand All @@ -685,6 +716,18 @@ private fun normalize(settings: GlobalSettings): GlobalSettings {
)
}

private fun isValidIpLiteral(value: String): Boolean {
val text = value.trim()
if (text.isBlank()) return false
val numericCandidate = when {
"." in text && ":" !in text -> text.matches(Regex("\\d{1,3}(\\.\\d{1,3}){3}"))
":" in text -> text.matches(Regex("[0-9A-Fa-f:.]+"))
else -> false
}
if (!numericCandidate) return false
return runCatching { InetAddress.getByName(text) }.isSuccess
}

private fun getSystemLocalIp(): String? {
return try {
val interfaces = java.net.NetworkInterface.getNetworkInterfaces()
Expand Down
3 changes: 3 additions & 0 deletions android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
<string name="global_settings_saved_msg">Global settings saved and applied</string>
<string name="global_settings_ports_required_msg">Please enter both SOCKS5 and HTTP ports.</string>
<string name="global_settings_ports_range_msg">Ports must be between 1025 and 65535.</string>
<string name="global_custom_dns_invalid_msg">Invalid custom DNS server: %1$s</string>
<string name="global_sharing_credentials_required_msg">Internet sharing requires both username and password.</string>
<string name="global_settings_save_button">Save Global Settings</string>
<string name="split_tunnel_apps_title">Split Tunnel Apps</string>
<string name="split_tunnel_apps_selected_count">%1$d selected apps - tap to choose</string>
Expand Down Expand Up @@ -123,6 +125,7 @@
<string name="global_http_port_required">HTTP port is required.</string>
<string name="global_username">Username</string>
<string name="global_password">Password</string>
<string name="global_sharing_lan_warning">Sharing listens on your local network. Use a username and password before enabling it.</string>
<string name="global_sharing_help">Use these endpoints to share your VPN connection with other devices or apps on the same network.</string>
<string name="info_app_name_title">MasterDnsVPN</string>
<string name="info_overview_subtitle">Project overview and build details</string>
Expand Down
Loading