Skip to content

Commit acb7c43

Browse files
committed
fix(android): auto-fallback local dns port to 5353 for non-root devices and add UI warning
1 parent fc196dc commit acb7c43

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

android/app/src/main/java/com/masterdns/vpn/service/MasterDnsVpnService.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,27 @@ class MasterDnsVpnService : VpnService() {
133133
}
134134
}
135135

136+
// Detect LOCAL_DNS_ENABLED with a privileged port (<=1024) — requires root on Android.
137+
// Automatically fall back to port 5353 to avoid a bind permission error on non-rooted devices.
138+
val advancedForDns = parseAdvanced(runtimeProfile.advancedJson)
139+
val localDnsEnabled = advancedForDns["LOCAL_DNS_ENABLED"].equals("true", ignoreCase = true)
140+
val localDnsPort = advancedForDns["LOCAL_DNS_PORT"]?.toIntOrNull() ?: 53
141+
val safeDnsPort: Int? = if (!proxyMode && localDnsEnabled && localDnsPort <= 1024) {
142+
VpnManager.appendLog(
143+
"WARNING: LOCAL_DNS_PORT=$localDnsPort requires root on Android. " +
144+
"Automatically using port 5353 instead."
145+
)
146+
5353
147+
} else null
148+
136149
configFile.writeText(
137150
ConfigGenerator.generateConfig(
138151
profile = runtimeProfile,
139152
listenPort = socksPort,
140153
listenIpOverride = listenIpOverride,
141154
protocolOverride = protocolOverride,
142-
localDnsEnabledOverride = if (proxyMode) false else null
155+
localDnsEnabledOverride = if (proxyMode) false else null,
156+
localDnsPortOverride = if (proxyMode) null else safeDnsPort
143157
)
144158
)
145159
if (runtimeProfile.resolvers.isNotBlank()) {

android/app/src/main/java/com/masterdns/vpn/ui/settings/SettingsScreen.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,31 @@ fun SettingsScreen(
399399
if (!expanded) return@items
400400

401401
Spacer(modifier = Modifier.height(6.dp))
402+
if (section == "DNS") {
403+
val dnsEnabled = fieldsState["LOCAL_DNS_ENABLED"].equals("true", ignoreCase = true)
404+
val dnsPort = fieldsState["LOCAL_DNS_PORT"]?.toIntOrNull() ?: 53
405+
if (dnsEnabled && dnsPort <= 1024) {
406+
Card(
407+
colors = CardDefaults.cardColors(
408+
containerColor = MaterialTheme.colorScheme.errorContainer
409+
),
410+
modifier = Modifier.fillMaxWidth()
411+
) {
412+
Row(
413+
modifier = Modifier.padding(12.dp),
414+
verticalAlignment = Alignment.CenterVertically
415+
) {
416+
Text(
417+
text = "⚠ Port $dnsPort requires root access on Android. " +
418+
"The app will automatically use port 5353 instead.",
419+
style = MaterialTheme.typography.bodySmall,
420+
color = MaterialTheme.colorScheme.onErrorContainer
421+
)
422+
}
423+
}
424+
Spacer(modifier = Modifier.height(8.dp))
425+
}
426+
}
402427
sections[section].orEmpty().forEach { field ->
403428
if ((field.key == "SOCKS5_USER" || field.key == "SOCKS5_PASS") && !socksAuthEnabled) {
404429
return@forEach

bugs/photo_2026-04-05_04-25-39.jpg

71.2 KB
Loading

bugs/photo_2026-04-05_14-09-25.jpg

16.8 KB
Loading

0 commit comments

Comments
 (0)