Obtain DNS server automatically from VPN service
Demote DNS settings to fallback usages only.
This commit is contained in:
@@ -28,7 +28,6 @@ class App : Application() {
|
|||||||
|
|
||||||
val handler = Handler()
|
val handler = Handler()
|
||||||
val pref: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
val pref: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||||
val dns: String get() = pref.getString("service.dns", "8.8.8.8")
|
|
||||||
|
|
||||||
fun toast(@StringRes resId: Int) = handler.post { Toast.makeText(this, resId, Toast.LENGTH_SHORT).show() }
|
fun toast(@StringRes resId: Int) = handler.post { Toast.makeText(this, resId, Toast.LENGTH_SHORT).show() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
|
|||||||
App.ACTION_CLEAN_ROUTINGS -> {
|
App.ACTION_CLEAN_ROUTINGS -> {
|
||||||
val routing = routing
|
val routing = routing
|
||||||
routing!!.started = false
|
routing!!.started = false
|
||||||
if (status == Status.ACTIVE) resetup(routing, upstream)
|
if (status == Status.ACTIVE) resetup(routing, upstream, dns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,6 +147,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
|
|||||||
val password get() = if (status == Status.ACTIVE) group?.passphrase else null
|
val password get() = if (status == Status.ACTIVE) group?.passphrase else null
|
||||||
|
|
||||||
private var upstream: String? = null
|
private var upstream: String? = null
|
||||||
|
private var dns: List<InetAddress> = emptyList()
|
||||||
private var routing: Routing? = null
|
private var routing: Routing? = null
|
||||||
|
|
||||||
var status = Status.IDLE
|
var status = Status.IDLE
|
||||||
@@ -194,13 +195,14 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
|
|||||||
/**
|
/**
|
||||||
* startService 2nd stop, also called when VPN re-established
|
* startService 2nd stop, also called when VPN re-established
|
||||||
*/
|
*/
|
||||||
private fun setup(ifname: String? = null) {
|
private fun setup(ifname: String? = null, dns: List<InetAddress> = emptyList()) {
|
||||||
val matcher = patternNetworkInfo.matcher(loggerSu("dumpsys ${Context.WIFI_P2P_SERVICE}") ?: "")
|
val matcher = patternNetworkInfo.matcher(loggerSu("dumpsys ${Context.WIFI_P2P_SERVICE}") ?: "")
|
||||||
when {
|
when {
|
||||||
!matcher.find() -> startFailure(getString(R.string.root_unavailable))
|
!matcher.find() -> startFailure(getString(R.string.root_unavailable))
|
||||||
matcher.group(2) == "true" -> {
|
matcher.group(2) == "true" -> {
|
||||||
unregisterReceiver()
|
unregisterReceiver()
|
||||||
upstream = ifname
|
upstream = ifname
|
||||||
|
this.dns = dns
|
||||||
registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
|
||||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))
|
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION))
|
||||||
LocalBroadcastManager.getInstance(this)
|
LocalBroadcastManager.getInstance(this)
|
||||||
@@ -228,18 +230,18 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetup(routing: Routing, ifname: String? = null) =
|
private fun resetup(routing: Routing, ifname: String? = null, dns: List<InetAddress> = emptyList()) =
|
||||||
initRouting(ifname, routing.downstream, routing.hostAddress)
|
initRouting(ifname, routing.downstream, routing.hostAddress, dns)
|
||||||
|
|
||||||
override fun onAvailable(ifname: String) = when (status) {
|
override fun onAvailable(ifname: String, dns: List<InetAddress>) = when (status) {
|
||||||
Status.STARTING -> setup(ifname)
|
Status.STARTING -> setup(ifname, dns)
|
||||||
Status.ACTIVE -> {
|
Status.ACTIVE -> {
|
||||||
val routing = routing!!
|
val routing = routing!!
|
||||||
if (routing.started) {
|
if (routing.started) {
|
||||||
routing.stop()
|
routing.stop()
|
||||||
check(routing.upstream == null)
|
check(routing.upstream == null)
|
||||||
}
|
}
|
||||||
resetup(routing, ifname)
|
resetup(routing, ifname, dns)
|
||||||
while (false) { }
|
while (false) { }
|
||||||
}
|
}
|
||||||
else -> throw IllegalStateException("RepeaterService is in unexpected state when receiving onAvailable")
|
else -> throw IllegalStateException("RepeaterService is in unexpected state when receiving onAvailable")
|
||||||
@@ -280,23 +282,21 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
|
|||||||
val downstream = group.`interface` ?: return
|
val downstream = group.`interface` ?: return
|
||||||
receiverRegistered = true
|
receiverRegistered = true
|
||||||
try {
|
try {
|
||||||
if (initRouting(upstream, downstream, owner)) doStart(group)
|
if (initRouting(upstream, downstream, owner, dns)) doStart(group)
|
||||||
} catch (e: Routing.InterfaceNotFoundException) {
|
} catch (e: Routing.InterfaceNotFoundException) {
|
||||||
startFailure(e.message, group)
|
startFailure(e.message, group)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun initRouting(upstream: String?, downstream: String, owner: InetAddress): Boolean {
|
private fun initRouting(upstream: String?, downstream: String,
|
||||||
|
owner: InetAddress, dns: List<InetAddress>): Boolean {
|
||||||
val routing = Routing(upstream, downstream, owner)
|
val routing = Routing(upstream, downstream, owner)
|
||||||
this.routing = routing
|
this.routing = routing
|
||||||
|
this.dns = dns
|
||||||
val strict = app.pref.getBoolean("service.repeater.strict", false)
|
val strict = app.pref.getBoolean("service.repeater.strict", false)
|
||||||
if (strict && upstream == null) return true // in this case, nothing to be done
|
return if (strict && upstream == null || // in this case, nothing to be done
|
||||||
return if (routing
|
routing.ipForward() // Wi-Fi direct doesn't enable ip_forward
|
||||||
.ipForward() // Wi-Fi direct doesn't enable ip_forward
|
.rule().forward(strict).dnsRedirect(dns).start()) true else {
|
||||||
.rule()
|
|
||||||
.forward(strict)
|
|
||||||
.dnsRedirect(app.dns)
|
|
||||||
.start()) true else {
|
|
||||||
routing.stop()
|
routing.stop()
|
||||||
Toast.makeText(this, getText(R.string.noisy_su_failure), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getText(R.string.noisy_su_failure), Toast.LENGTH_SHORT).show()
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.support.v4.content.LocalBroadcastManager
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.*
|
import be.mygod.vpnhotspot.net.*
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Callback {
|
class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Callback {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -23,6 +24,7 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call
|
|||||||
private val routings = HashMap<String, Routing?>()
|
private val routings = HashMap<String, Routing?>()
|
||||||
private var neighbours = emptyList<IpNeighbour>()
|
private var neighbours = emptyList<IpNeighbour>()
|
||||||
private var upstream: String? = null
|
private var upstream: String? = null
|
||||||
|
private var dns: List<InetAddress> = emptyList()
|
||||||
private var receiverRegistered = false
|
private var receiverRegistered = false
|
||||||
private val receiver = broadcastReceiver { _, intent ->
|
private val receiver = broadcastReceiver { _, intent ->
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
@@ -49,7 +51,7 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call
|
|||||||
for ((downstream, value) in routings) if (value == null) {
|
for ((downstream, value) in routings) if (value == null) {
|
||||||
// system tethering already has working forwarding rules
|
// system tethering already has working forwarding rules
|
||||||
// so it doesn't make sense to add additional forwarding rules
|
// so it doesn't make sense to add additional forwarding rules
|
||||||
val routing = Routing(upstream, downstream).rule().forward(true).dnsRedirect(app.dns)
|
val routing = Routing(upstream, downstream).rule().forward(true).dnsRedirect(dns)
|
||||||
if (routing.start()) routings[downstream] = routing else {
|
if (routing.start()) routings[downstream] = routing else {
|
||||||
failed = true
|
failed = true
|
||||||
routing.stop()
|
routing.stop()
|
||||||
@@ -81,15 +83,17 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call
|
|||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAvailable(ifname: String) {
|
override fun onAvailable(ifname: String, dns: List<InetAddress>) {
|
||||||
check(upstream == null || upstream == ifname)
|
check(upstream == null || upstream == ifname)
|
||||||
upstream = ifname
|
upstream = ifname
|
||||||
|
this.dns = dns
|
||||||
updateRoutings()
|
updateRoutings()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLost(ifname: String) {
|
override fun onLost(ifname: String) {
|
||||||
check(upstream == null || upstream == ifname)
|
check(upstream == null || upstream == ifname)
|
||||||
upstream = null
|
upstream = null
|
||||||
|
this.dns = emptyList()
|
||||||
var failed = false
|
var failed = false
|
||||||
for ((iface, routing) in routings) {
|
for ((iface, routing) in routings) {
|
||||||
if (routing?.stop() == false) failed = true
|
if (routing?.stop() == false) failed = true
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.net
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.R
|
import be.mygod.vpnhotspot.R
|
||||||
|
import be.mygod.vpnhotspot.debugLog
|
||||||
import be.mygod.vpnhotspot.noisySu
|
import be.mygod.vpnhotspot.noisySu
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.Inet4Address
|
import java.net.Inet4Address
|
||||||
@@ -82,8 +83,11 @@ class Routing(val upstream: String?, val downstream: String, ownerAddress: InetA
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dnsRedirect(dns: String): Routing {
|
fun dnsRedirect(dnses: List<InetAddress>): Routing {
|
||||||
val hostAddress = hostAddress.hostAddress
|
val hostAddress = hostAddress.hostAddress
|
||||||
|
val dns = dnses.firstOrNull { it is Inet4Address }?.hostAddress
|
||||||
|
?: app.pref.getString("service.dns", "8.8.8.8")
|
||||||
|
debugLog("Routing", "Using $dns from ($dnses)")
|
||||||
startScript.add("$IPTABLES -t nat -A PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
startScript.add("$IPTABLES -t nat -A PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
||||||
startScript.add("$IPTABLES -t nat -A PREROUTING -i $downstream -p udp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
startScript.add("$IPTABLES -t nat -A PREROUTING -i $downstream -p udp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
||||||
stopScript.addFirst("$IPTABLES -t nat -D PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
stopScript.addFirst("$IPTABLES -t nat -D PREROUTING -i $downstream -p tcp -d $hostAddress --dport 53 -j DNAT --to-destination $dns")
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import android.net.NetworkCapabilities
|
|||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.debugLog
|
import be.mygod.vpnhotspot.debugLog
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
object VpnMonitor : ConnectivityManager.NetworkCallback() {
|
object VpnMonitor : ConnectivityManager.NetworkCallback() {
|
||||||
interface Callback {
|
interface Callback {
|
||||||
fun onAvailable(ifname: String)
|
fun onAvailable(ifname: String, dns: List<InetAddress>)
|
||||||
fun onLost(ifname: String)
|
fun onLost(ifname: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,13 +28,14 @@ object VpnMonitor : ConnectivityManager.NetworkCallback() {
|
|||||||
/**
|
/**
|
||||||
* Obtaining ifname in onLost doesn't work so we need to cache it in onAvailable.
|
* Obtaining ifname in onLost doesn't work so we need to cache it in onAvailable.
|
||||||
*/
|
*/
|
||||||
val available = HashMap<Network, String>()
|
private val available = HashMap<Network, String>()
|
||||||
override fun onAvailable(network: Network) {
|
override fun onAvailable(network: Network) {
|
||||||
val ifname = manager.getLinkProperties(network)?.interfaceName ?: return
|
val properties = manager.getLinkProperties(network)
|
||||||
|
val ifname = properties?.interfaceName ?: return
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (available.put(network, ifname) != null) return
|
if (available.put(network, ifname) != null) return
|
||||||
debugLog(TAG, "onAvailable: $ifname")
|
debugLog(TAG, "onAvailable: $ifname, ${properties.dnsServers.joinToString()}")
|
||||||
callbacks.forEach { it.onAvailable(ifname) }
|
callbacks.forEach { it.onAvailable(ifname, properties.dnsServers) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +57,9 @@ object VpnMonitor : ConnectivityManager.NetworkCallback() {
|
|||||||
cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||||
}
|
}
|
||||||
} else if (available.isEmpty()) true else {
|
} else if (available.isEmpty()) true else {
|
||||||
available.forEach { callback.onAvailable(it.value) }
|
available.forEach {
|
||||||
|
callback.onAvailable(it.value, manager.getLinkProperties(it.key)?.dnsServers ?: emptyList())
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}) failfast?.invoke()
|
}) failfast?.invoke()
|
||||||
|
|||||||
@@ -40,9 +40,9 @@
|
|||||||
<string name="connected_state_failed">%s (已断开)</string>
|
<string name="connected_state_failed">%s (已断开)</string>
|
||||||
|
|
||||||
<string name="settings_service">服务</string>
|
<string name="settings_service">服务</string>
|
||||||
<string name="settings_service_dns">下游 DNS 服务器[:端口]</string>
|
|
||||||
<string name="settings_service_repeater_strict">严格模式 (仅用于中继)</string>
|
<string name="settings_service_repeater_strict">严格模式 (仅用于中继)</string>
|
||||||
<string name="settings_service_repeater_strict_summary">只允许通过 VPN 隧道的包通过</string>
|
<string name="settings_service_repeater_strict_summary">只允许通过 VPN 隧道的包通过</string>
|
||||||
|
<string name="settings_service_dns">备用 DNS 服务器[:端口]</string>
|
||||||
<string name="settings_service_clean">清理/重新应用路由规则</string>
|
<string name="settings_service_clean">清理/重新应用路由规则</string>
|
||||||
<string name="settings_misc">杂项</string>
|
<string name="settings_misc">杂项</string>
|
||||||
<string name="settings_misc_logcat">导出日志</string>
|
<string name="settings_misc_logcat">导出日志</string>
|
||||||
|
|||||||
@@ -42,9 +42,9 @@
|
|||||||
<string name="connected_state_failed">%s (lost)</string>
|
<string name="connected_state_failed">%s (lost)</string>
|
||||||
|
|
||||||
<string name="settings_service">Service</string>
|
<string name="settings_service">Service</string>
|
||||||
<string name="settings_service_dns">Downstream DNS server[:port]</string>
|
|
||||||
<string name="settings_service_repeater_strict">Strict mode (repeater only)</string>
|
<string name="settings_service_repeater_strict">Strict mode (repeater only)</string>
|
||||||
<string name="settings_service_repeater_strict_summary">Only allow packets that goes through VPN tunnel</string>
|
<string name="settings_service_repeater_strict_summary">Only allow packets that goes through VPN tunnel</string>
|
||||||
|
<string name="settings_service_dns">Fallback DNS server[:port]</string>
|
||||||
<string name="settings_service_clean">Clean/reapply routing rules</string>
|
<string name="settings_service_clean">Clean/reapply routing rules</string>
|
||||||
<string name="settings_misc">Misc</string>
|
<string name="settings_misc">Misc</string>
|
||||||
<string name="settings_misc_logcat">Export logcat</string>
|
<string name="settings_misc_logcat">Export logcat</string>
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/settings_service">
|
android:title="@string/settings_service">
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="service.repeater.strict"
|
||||||
|
android:title="@string/settings_service_repeater_strict"
|
||||||
|
android:summary="@string/settings_service_repeater_strict_summary"/>
|
||||||
<AutoSummaryEditTextPreference
|
<AutoSummaryEditTextPreference
|
||||||
android:key="service.dns"
|
android:key="service.dns"
|
||||||
android:title="@string/settings_service_dns"
|
android:title="@string/settings_service_dns"
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:defaultValue="8.8.8.8"/>
|
android:defaultValue="8.8.8.8"/>
|
||||||
<SwitchPreference
|
|
||||||
android:key="service.repeater.strict"
|
|
||||||
android:title="@string/settings_service_repeater_strict"
|
|
||||||
android:summary="@string/settings_service_repeater_strict_summary"/>
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="service.clean"
|
android:key="service.clean"
|
||||||
android:title="@string/settings_service_clean"/>
|
android:title="@string/settings_service_clean"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user