diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index 2eaf3842..b3c7153f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -28,7 +28,6 @@ class App : Application() { val handler = Handler() 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() } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt index af9be687..81990fc6 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/RepeaterService.kt @@ -138,7 +138,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca App.ACTION_CLEAN_ROUTINGS -> { val routing = routing 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 private var upstream: String? = null + private var dns: List = emptyList() private var routing: Routing? = null var status = Status.IDLE @@ -194,13 +195,14 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca /** * startService 2nd stop, also called when VPN re-established */ - private fun setup(ifname: String? = null) { + private fun setup(ifname: String? = null, dns: List = emptyList()) { val matcher = patternNetworkInfo.matcher(loggerSu("dumpsys ${Context.WIFI_P2P_SERVICE}") ?: "") when { !matcher.find() -> startFailure(getString(R.string.root_unavailable)) matcher.group(2) == "true" -> { unregisterReceiver() upstream = ifname + this.dns = dns registerReceiver(receiver, intentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) LocalBroadcastManager.getInstance(this) @@ -228,18 +230,18 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca } } - private fun resetup(routing: Routing, ifname: String? = null) = - initRouting(ifname, routing.downstream, routing.hostAddress) + private fun resetup(routing: Routing, ifname: String? = null, dns: List = emptyList()) = + initRouting(ifname, routing.downstream, routing.hostAddress, dns) - override fun onAvailable(ifname: String) = when (status) { - Status.STARTING -> setup(ifname) + override fun onAvailable(ifname: String, dns: List) = when (status) { + Status.STARTING -> setup(ifname, dns) Status.ACTIVE -> { val routing = routing!! if (routing.started) { routing.stop() check(routing.upstream == null) } - resetup(routing, ifname) + resetup(routing, ifname, dns) while (false) { } } 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 receiverRegistered = true try { - if (initRouting(upstream, downstream, owner)) doStart(group) + if (initRouting(upstream, downstream, owner, dns)) doStart(group) } catch (e: Routing.InterfaceNotFoundException) { startFailure(e.message, group) return } } - private fun initRouting(upstream: String?, downstream: String, owner: InetAddress): Boolean { + private fun initRouting(upstream: String?, downstream: String, + owner: InetAddress, dns: List): Boolean { val routing = Routing(upstream, downstream, owner) this.routing = routing + this.dns = dns val strict = app.pref.getBoolean("service.repeater.strict", false) - if (strict && upstream == null) return true // in this case, nothing to be done - return if (routing - .ipForward() // Wi-Fi direct doesn't enable ip_forward - .rule() - .forward(strict) - .dnsRedirect(app.dns) - .start()) true else { + return if (strict && upstream == null || // in this case, nothing to be done + routing.ipForward() // Wi-Fi direct doesn't enable ip_forward + .rule().forward(strict).dnsRedirect(dns).start()) true else { routing.stop() Toast.makeText(this, getText(R.string.noisy_su_failure), Toast.LENGTH_SHORT).show() false diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt index 89bf7d52..ec904d95 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt @@ -7,6 +7,7 @@ import android.support.v4.content.LocalBroadcastManager import android.widget.Toast import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.net.* +import java.net.InetAddress class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Callback { companion object { @@ -23,6 +24,7 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call private val routings = HashMap() private var neighbours = emptyList() private var upstream: String? = null + private var dns: List = emptyList() private var receiverRegistered = false private val receiver = broadcastReceiver { _, intent -> when (intent.action) { @@ -49,7 +51,7 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call for ((downstream, value) in routings) if (value == null) { // system tethering already has working 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 { failed = true routing.stop() @@ -81,15 +83,17 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call return START_NOT_STICKY } - override fun onAvailable(ifname: String) { + override fun onAvailable(ifname: String, dns: List) { check(upstream == null || upstream == ifname) upstream = ifname + this.dns = dns updateRoutings() } override fun onLost(ifname: String) { check(upstream == null || upstream == ifname) upstream = null + this.dns = emptyList() var failed = false for ((iface, routing) in routings) { if (routing?.stop() == false) failed = true diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt index 16615e38..95196b5f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/Routing.kt @@ -3,6 +3,7 @@ package be.mygod.vpnhotspot.net import android.os.Build import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.debugLog import be.mygod.vpnhotspot.noisySu import java.io.IOException import java.net.Inet4Address @@ -82,8 +83,11 @@ class Routing(val upstream: String?, val downstream: String, ownerAddress: InetA return this } - fun dnsRedirect(dns: String): Routing { + fun dnsRedirect(dnses: List): Routing { 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 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") diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/VpnMonitor.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/VpnMonitor.kt index 749ce158..88365be8 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/VpnMonitor.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/VpnMonitor.kt @@ -7,10 +7,11 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.debugLog +import java.net.InetAddress object VpnMonitor : ConnectivityManager.NetworkCallback() { interface Callback { - fun onAvailable(ifname: String) + fun onAvailable(ifname: String, dns: List) 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. */ - val available = HashMap() + private val available = HashMap() override fun onAvailable(network: Network) { - val ifname = manager.getLinkProperties(network)?.interfaceName ?: return + val properties = manager.getLinkProperties(network) + val ifname = properties?.interfaceName ?: return synchronized(this) { if (available.put(network, ifname) != null) return - debugLog(TAG, "onAvailable: $ifname") - callbacks.forEach { it.onAvailable(ifname) } + debugLog(TAG, "onAvailable: $ifname, ${properties.dnsServers.joinToString()}") + callbacks.forEach { it.onAvailable(ifname, properties.dnsServers) } } } @@ -55,7 +57,9 @@ object VpnMonitor : ConnectivityManager.NetworkCallback() { cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) } } 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 } }) failfast?.invoke() diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 5612bf32..0f14650a 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -40,9 +40,9 @@ %s (已断开) 服务 - 下游 DNS 服务器[:端口] 严格模式 (仅用于中继) 只允许通过 VPN 隧道的包通过 + 备用 DNS 服务器[:端口] 清理/重新应用路由规则 杂项 导出日志 diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 2d6af47a..5f4e1cfd 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -42,9 +42,9 @@ %s (lost) Service - Downstream DNS server[:port] Strict mode (repeater only) Only allow packets that goes through VPN tunnel + Fallback DNS server[:port] Clean/reapply routing rules Misc Export logcat diff --git a/mobile/src/main/res/xml/pref_settings.xml b/mobile/src/main/res/xml/pref_settings.xml index 60c641a7..b002fd92 100644 --- a/mobile/src/main/res/xml/pref_settings.xml +++ b/mobile/src/main/res/xml/pref_settings.xml @@ -2,16 +2,16 @@ + -