Obtain DNS server automatically from VPN service

Demote DNS settings to fallback usages only.
This commit is contained in:
Mygod
2018-02-17 19:52:34 -08:00
parent 2d30b5154b
commit f2d9f25b10
8 changed files with 43 additions and 32 deletions

View File

@@ -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() }
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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()

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"/>