From 28ae7eeaee2e85c5fb4bc206304764f5302756ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=BE=D0=B7=D0=BE=D0=B2=20=D0=9D=D0=B8=D0=BA=D0=B8?= =?UTF-8?q?=D1=82=D0=B0=20=D0=92=D0=B0=D0=BB=D0=B5=D1=80=D1=8C=D0=B5=D0=B2?= =?UTF-8?q?=D0=B8=D1=87?= Date: Wed, 13 Oct 2021 18:33:43 +0300 Subject: [PATCH] add disable openvpnconnection, refactoring --- .../src/org/amnezia/vpn/OpenVPNThreadv3.kt | 163 ++--- .../android/src/org/amnezia/vpn/VPNService.kt | 555 +++++++++--------- .../src/org/amnezia/vpn/VPNServiceBinder.kt | 177 +++--- .../org/amnezia/vpn/qt/VPNPermissionHelper.kt | 2 +- client/main.cpp | 1 + client/protocols/android_vpnprotocol.cpp | 73 +-- client/protocols/android_vpnprotocol.h | 2 +- client/vpnconnection.cpp | 9 +- 8 files changed, 500 insertions(+), 482 deletions(-) diff --git a/client/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt b/client/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt index cac886ab9..d1224db99 100644 --- a/client/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt +++ b/client/android/src/org/amnezia/vpn/OpenVPNThreadv3.kt @@ -29,94 +29,95 @@ import net.openvpn.ovpn3.ClientAPI_TransportStats class OpenVPNThreadv3(var service: VPNService): ClientAPI_OpenVPNClient(), Runnable { private val tag = "OpenVPNThreadv3" private var mConfig: JSONObject? = null - private var mConnectionTime: Long = 0 private var mAlreadyInitialised = false private var mService: VPNService = service - private var currentTunnelHandle = -1 - override fun run() { //TEMP - val lConfigData: String = readFileDirectlyAsText("/data/local/tmp/android_conf.ovpn") val config: ClientAPI_Config = ClientAPI_Config() - config.content = lConfigData - - val lCreds: ClientAPI_ProvideCreds = ClientAPI_ProvideCreds() - //username from config or GUI - lCreds.username = "" - //password from config or GUI - lCreds.password = "" - - provide_creds(lCreds) + config.content = mService.getVpnConfig().getString("openvpn_config_data") eval_config(config) - connect() - Log.i(tag, "Connect succesfully") + val status = connect() + Log.i(tag, "ERROR " + status) + if (status.getError() != false) { + Log.i(tag, "connect() error: " + status.getError() + ": " + status.getMessage()) + mService.openvpnConnected() + } else { + Log.i(tag, "Connect succesfully, OpenVPN3 thread finished") + mService.openvpnConnected() + } + } + + override fun log(arg0: ClientAPI_LogInfo){ + Log.i(tag, arg0.getText()) + } + + override fun event(event: ClientAPI_Event ){ + Log.i(tag, event.getName()) + } + + override fun tun_builder_new(): Boolean { + return true + } + + override fun tun_builder_establish(): Int { + Log.v(tag, "tun_builder_establish") + return mService.establish()!!.detachFd() + } + + override fun tun_builder_add_address(address: String , prefix_length: Int , gateway: String , ipv6:Boolean , net30: Boolean ): Boolean { + Log.v(tag, "tun_builder_add_address") + mService.addAddress(address, prefix_length) + return true + } + + override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { + Log.v(tag, "tun_builder_add_route") + if (address.equals("remote_host")) + return false + + mService.addRoute(address, prefix_length); + return true + } + + override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { + if (address.equals("remote_host")) + return false + + mService.addRoute(address, prefix_length); + return true + } + + override fun tun_builder_set_remote_address(address: String , ipv6: Boolean): Boolean { + mService.setMtu(1500) + return true + } + + override fun tun_builder_set_mtu(mtu: Int): Boolean { + Log.v(tag, "tun_builder_set_mtu") + mService.setMtu(mtu) + return true + } + + override fun tun_builder_add_dns_server(address: String , ipv6: Boolean): Boolean { + mService.addDNS(address) + return true + } + + override fun tun_builder_set_session_name(name: String ): Boolean { + Log.v(tag, "We should call this session: " + name) + return true + } + + + fun stopVPN(): Boolean { + stop() + return false + } + + override fun stop() { + super.stop() + } } - - fun readFileDirectlyAsText(fileName: String): String = File(fileName).readText(Charsets.UTF_8) - - override fun log(arg0: ClientAPI_LogInfo){ - Log.i(tag, arg0.getText()) - } - - override fun event(event: ClientAPI_Event ){ - Log.i(tag, event.getName()) - } - - - override fun tun_builder_new(): Boolean { - return true - } - - override fun tun_builder_establish(): Int { - Log.v(tag, "tun_builder_establish") - return mService.establish()!!.detachFd() - } - - override fun tun_builder_add_address(address: String , prefix_length: Int , gateway: String , ipv6:Boolean , net30: Boolean ): Boolean { - Log.v(tag, "tun_builder_add_address") - mService.addAddress(address, prefix_length) - return true - } - - override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { - Log.v(tag, "tun_builder_add_route") - if (address.equals("remote_host")) - return false - - mService.addRoute(address, prefix_length); - return true - } - - override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean { - if (address.equals("remote_host")) - return false - - mService.addRoute(address, prefix_length); - return true - } - - override fun tun_builder_set_remote_address(address: String , ipv6: Boolean): Boolean { - mService.setMtu(1500) - return true - } - - override fun tun_builder_set_mtu(mtu: Int): Boolean { - Log.v(tag, "tun_builder_set_mtu") - mService.setMtu(mtu) - return true - } - - override fun tun_builder_add_dns_server(address: String , ipv6: Boolean): Boolean { - mService.addDNS(address) - return true - } - - override fun tun_builder_set_session_name(name: String ): Boolean { - Log.v(tag, "We should call this session: " + name) - return true - } - -} diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt index 1d8535d4e..9a7e35127 100644 --- a/client/android/src/org/amnezia/vpn/VPNService.kt +++ b/client/android/src/org/amnezia/vpn/VPNService.kt @@ -19,11 +19,11 @@ class VPNService : android.net.VpnService() { private val tag = "VPNService" private var mBinder: VPNServiceBinder = VPNServiceBinder(this) private var mConfig: JSONObject? = null + private var mProtocol: String? = null private var mConnectionTime: Long = 0 private var mAlreadyInitialised = false private var mbuilder: Builder = Builder() - private var mOpenVPNThreadv3: OpenVPNThreadv3? = null private var currentTunnelHandle = -1 @@ -67,293 +67,302 @@ class VPNService : android.net.VpnService() { */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { init() - // intent?.let { - // if (intent.getBooleanExtra("startOnly", false)) { - // Log.i(tag, "Start only!") - // return super.onStartCommand(intent, flags, startId) - // } - // } - // // This start is from always-on - // if (this.mConfig == null) { - // // We don't have tunnel to turn on - Try to create one with last config the service got - // val prefs = Prefs.get(this) - // val lastConfString = prefs.getString("lastConf", "") - // if (lastConfString.isNullOrEmpty()) { - // // We have nothing to connect to -> Exit - // Log.e( - // tag, - // "VPN service was triggered without defining a Server or having a tunnel" - // ) - // return super.onStartCommand(intent, flags, startId) - // } - // this.mConfig = JSONObject(lastConfString) - // } - - - // Log.v(tag, "onStartCommand:" + this.mConfig) - // turnOn(this.mConfig) - - return super.onStartCommand(intent, flags, startId) - } - - // Invoked when the application is revoked. - // At this moment, the VPN interface is already deactivated by the system. - override fun onRevoke() { - this.turnOff() - super.onRevoke() - } - - var connectionTime: Long = 0 - get() { - return mConnectionTime - } - - var isUp: Boolean - get() { - return currentTunnelHandle >= 0 - } - private set(value) { - if (value) { - mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "") - mConnectionTime = System.currentTimeMillis() - return + intent?.let { + if (intent.getBooleanExtra("startOnly", false)) { + Log.i(tag, "Start only!") + return super.onStartCommand(intent, flags, startId) + } } - mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "") - mConnectionTime = 0 - } - val status: JSONObject - get() { - val deviceIpv4: String = "" - return JSONObject().apply { - putOpt("rx_bytes", getConfigValue("rx_bytes")) - putOpt("tx_bytes", getConfigValue("tx_bytes")) - putOpt("endpoint", mConfig?.getJSONObject("server")?.getString("ipv4Gateway")) - putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address")) - } - } - /* - * Checks if the VPN Permission is given. - * If the permission is given, returns true - * Requests permission and returns false if not. - */ - fun checkPermissions(): Boolean { - // See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service - // Call Prepare, if we get an Intent back, we dont have the VPN Permission - // from the user. So we need to pass this to our main Activity and exit here. - val intent = prepare(this) - if (intent == null) { - Log.e(tag, "VPN Permission Already Present") - return true - } - Log.e(tag, "Requesting VPN Permission") - return false - } + // This start is from always-on + if (this.mConfig == null) { + // We don't have tunnel to turn on - Try to create one with last config the service got + val prefs = Prefs.get(this) + val lastConfString = prefs.getString("lastConf", "") + if (lastConfString.isNullOrEmpty()) { + // We have nothing to connect to -> Exit + Log.e( + tag, + "VPN service was triggered without defining a Server or having a tunnel" + ) + return super.onStartCommand(intent, flags, startId) + } + this.mConfig = JSONObject(lastConfString) + } - fun turnOn(json: JSONObject?): ParcelFileDescriptor? { - Log.sensitive(tag, "" + json.toString()) - // val wireguard_conf = buildWireugardConfig(json) + return super.onStartCommand(intent, flags, startId) + } + + // Invoked when the application is revoked. + // At this moment, the VPN interface is already deactivated by the system. + override fun onRevoke() { + this.turnOff() + super.onRevoke() + } + + var connectionTime: Long = 0 + get() { + return mConnectionTime + } + + var isUp: Boolean + get() { + return currentTunnelHandle >= 0 + } + private set(value) { + if (value) { + mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "") + mConnectionTime = System.currentTimeMillis() + return + } + mBinder.dispatchEvent(VPNServiceBinder.EVENTS.disconnected, "") + mConnectionTime = 0 + } + val status: JSONObject + get() { + val deviceIpv4: String = "" + return JSONObject().apply { + putOpt("rx_bytes", getConfigValue("rx_bytes")) + putOpt("tx_bytes", getConfigValue("tx_bytes")) + putOpt("endpoint", mConfig?.getJSONObject("server")?.getString("ipv4Gateway")) + putOpt("deviceIpv4", mConfig?.getJSONObject("device")?.getString("ipv4Address")) + } + } + /* + * Checks if the VPN Permission is given. + * If the permission is given, returns true + * Requests permission and returns false if not. + */ + fun checkPermissions(): Boolean { + // See https://developer.android.com/guide/topics/connectivity/vpn#connect_a_service + // Call Prepare, if we get an Intent back, we dont have the VPN Permission + // from the user. So we need to pass this to our main Activity and exit here. + val intent = prepare(this) + if (intent == null) { + Log.e(tag, "VPN Permission Already Present") + return true + } + Log.e(tag, "Requesting VPN Permission") + return false + } + + fun turnOn(json: JSONObject?): Int { + if (!checkPermissions()) { + Log.e(tag, "turn on was called without no permissions present!") + isUp = false + return 0 + } + Log.i(tag, "Permission okay") + mConfig = json + mProtocol = mConfig!!.getString("protocol") + when (mProtocol) { + "openvpn" -> startOpenVpn() + "wireguard" -> startWireGuard() + else -> { + Log.e(tag, "No protocol") + return 0 + } + } + return 1 + } + + fun establish(): ParcelFileDescriptor? { + return mbuilder.establish() + } + + fun setMtu(mtu: Int) { + mbuilder.setMtu(mtu) + } + + fun addAddress(ip: String, len: Int){ + mbuilder.addAddress(ip, len) + } + + fun addRoute(ip: String, len: Int){ + mbuilder.addRoute(ip, len) + } + + fun addDNS(ip: String){ + mbuilder.addDnsServer(ip) + } + + fun turnOff() { + Log.v(tag, "Try to disable tunnel") + when(mProtocol){ + "wireguard" -> wgTurnOff(currentTunnelHandle) + "openvpn" -> mOpenVPNThreadv3?.stopVPN() + else -> { + Log.e(tag, "No protocol") + } + } + currentTunnelHandle = -1 + stopForeground(true) - if (!checkPermissions()) { - Log.e(tag, "turn on was called without no permissions present!") isUp = false - return null + stopSelf(); } - // Log.i(tag, "Permission okay") - // if (currentTunnelHandle != -1) { - // Log.e(tag, "Tunnel already up") - // // Turn the tunnel down because this might be a switch - // wgTurnOff(currentTunnelHandle) - // } - // val wgConfig: String = wireguard_conf!!.toWgUserspaceString() - // val builder = Builder() - // setupBuilder(wireguard_conf, builder) - // builder.setSession("mvpn0") - // builder.establish().use { tun -> - // if (tun == null)return - // Log.i(tag, "Go backend " + wgVersion()) - // currentTunnelHandle = wgTurnOn("mvpn0", tun.detachFd(), wgConfig) - // } - // if (currentTunnelHandle < 0) { - // Log.e(tag, "Activation Error Code -> $currentTunnelHandle") - // isUp = false - // return - // } - // protect(wgGetSocketV4(currentTunnelHandle)) - // protect(wgGetSocketV6(currentTunnelHandle)) - // mConfig = json - // isUp = true - // // Store the config in case the service gets - // // asked boot vpn from the OS - // val prefs = Prefs.get(this) - // prefs.edit() - // .putString("lastConf", json.toString()) - // .apply() + /** + * Configures an Android VPN Service Tunnel + * with a given Wireguard Config + */ + private fun setupBuilder(config: Config, builder: Builder) { + // Setup Split tunnel + for (excludedApplication in config.`interface`.excludedApplications) + builder.addDisallowedApplication(excludedApplication) - // NotificationUtil.show(this) // Go foreground - - - startOpenVpn() - - return 1//localTunnel - - } - - fun establish(): ParcelFileDescriptor? { - return mbuilder.establish() - } - - fun setMtu(mtu: Int) { - Log.v(tag, "setMtu()" + mtu) - mbuilder.setMtu(mtu) - } - - fun addAddress(ip: String, len: Int){ - Log.v(tag, "addAddress()" + ip + " " + len) - mbuilder.addAddress(ip, len) - } - - fun addRoute(ip: String, len: Int){ - Log.v(tag, "addRoute()" + ip + " " + len) - mbuilder.addRoute(ip, len) - } - - fun addDNS(ip: String){ - mbuilder.addDnsServer(ip) - } - - - fun turnOff() { - Log.v(tag, "Try to disable tunnel") - wgTurnOff(currentTunnelHandle) - currentTunnelHandle = -1 - stopForeground(false) - isUp = false - } - - /** - * Configures an Android VPN Service Tunnel - * with a given Wireguard Config - */ - private fun setupBuilder(config: Config, builder: Builder) { - // Setup Split tunnel - for (excludedApplication in config.`interface`.excludedApplications) - builder.addDisallowedApplication(excludedApplication) - - // Device IP - for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask) - // DNS - for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress) - // Add All routes the VPN may route tos - for (peer in config.peers) { - for (addr in peer.allowedIps) { - builder.addRoute(addr.address, addr.mask) - } - } - builder.allowFamily(OsConstants.AF_INET) - builder.allowFamily(OsConstants.AF_INET6) - builder.setMtu(config.`interface`.mtu.orElse(1280)) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null) - - builder.setBlocking(true) - } - - /** - * Gets config value for {key} from the Current - * running Wireguard tunnel - */ - private fun getConfigValue(key: String): String? { - if (!isUp) { - return null - } - val config = wgGetConfig(currentTunnelHandle) ?: return null - val lines = config.split("\n") - for (line in lines) { - val parts = line.split("=") - val k = parts.first() - val value = parts.last() - if (key == k) { - return value - } - } - return null - } - - /** - * Create a Wireguard [Config] from a [json] string - - * The [json] will be created in AndroidVpnProtocol.cpp - */ - private fun buildWireugardConfig(obj: JSONObject): Config { - val confBuilder = Config.Builder() - val jServer = obj.getJSONObject("server") - val peerBuilder = Peer.Builder() - val ep = - InetEndpoint.parse(jServer.getString("ipv4AddrIn") + ":" + jServer.getString("port")) - peerBuilder.setEndpoint(ep) - peerBuilder.setPublicKey(Key.fromBase64(jServer.getString("publicKey"))) - - val jAllowedIPList = obj.getJSONArray("allowedIPs") - if (jAllowedIPList.length() == 0) { - val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet. - peerBuilder.addAllowedIp(internet) - } else { - (0 until jAllowedIPList.length()).toList().forEach { - val network = InetNetwork.parse(jAllowedIPList.getString(it)) - peerBuilder.addAllowedIp(network) + // Device IP + for (addr in config.`interface`.addresses) builder.addAddress(addr.address, addr.mask) + // DNS + for (addr in config.`interface`.dnsServers) builder.addDnsServer(addr.hostAddress) + // Add All routes the VPN may route tos + for (peer in config.peers) { + for (addr in peer.allowedIps) { + builder.addRoute(addr.address, addr.mask) } } + builder.allowFamily(OsConstants.AF_INET) + builder.allowFamily(OsConstants.AF_INET6) + builder.setMtu(config.`interface`.mtu.orElse(1280)) - confBuilder.addPeer(peerBuilder.build()) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(false) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) setUnderlyingNetworks(null) - val privateKey = obj.getJSONObject("keys").getString("privateKey") - val jDevice = obj.getJSONObject("device") - - val ifaceBuilder = Interface.Builder() - ifaceBuilder.parsePrivateKey(privateKey) - ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv4Address"))) - ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv6Address"))) - ifaceBuilder.addDnsServer(InetNetwork.parse(obj.getString("dns")).address) - val jExcludedApplication = obj.getJSONArray("excludedApps") - (0 until jExcludedApplication.length()).toList().forEach { - val appName = jExcludedApplication.get(it).toString() - ifaceBuilder.excludeApplication(appName) - } - confBuilder.setInterface(ifaceBuilder.build()) - return confBuilder.build() + builder.setBlocking(true) } - - private fun startOpenVpn() { - Thread ({ - mOpenVPNThreadv3?.run() - }).start() - Log.i(tag, "OpenVPNThreadv3 start") - isUp = true + /** + * Gets config value for {key} from the Current + * running Wireguard tunnel + */ + private fun getConfigValue(key: String): String? { + if (!isUp) { + return null } + val config = wgGetConfig(currentTunnelHandle) ?: return null + val lines = config.split("\n") + for (line in lines) { + val parts = line.split("=") + val k = parts.first() + val value = parts.last() + if (key == k) { + return value + } + } + return null + } - companion object { - @JvmStatic - fun startService(c: Context) { - c.applicationContext.startService( - Intent(c.applicationContext, VPNService::class.java).apply { - putExtra("startOnly", true) - } - ) + /** + * Create a Wireguard [Config] from a [json] string - + * The [json] will be created in AndroidVpnProtocol.cpp + */ + private fun buildWireugardConfig(obj: JSONObject): Config { + val confBuilder = Config.Builder() + val jServer = obj.getJSONObject("server") + val peerBuilder = Peer.Builder() + val ep = + InetEndpoint.parse(jServer.getString("ipv4AddrIn") + ":" + jServer.getString("port")) + peerBuilder.setEndpoint(ep) + peerBuilder.setPublicKey(Key.fromBase64(jServer.getString("publicKey"))) + + val jAllowedIPList = obj.getJSONArray("allowedIPs") + if (jAllowedIPList.length() == 0) { + val internet = InetNetwork.parse("0.0.0.0/0") // aka The whole internet. + peerBuilder.addAllowedIp(internet) + } else { + (0 until jAllowedIPList.length()).toList().forEach { + val network = InetNetwork.parse(jAllowedIPList.getString(it)) + peerBuilder.addAllowedIp(network) } - - @JvmStatic - private external fun wgGetConfig(handle: Int): String? - @JvmStatic - private external fun wgGetSocketV4(handle: Int): Int - @JvmStatic - private external fun wgGetSocketV6(handle: Int): Int - @JvmStatic - private external fun wgTurnOff(handle: Int) - @JvmStatic - private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int - @JvmStatic - private external fun wgVersion(): String? } + + confBuilder.addPeer(peerBuilder.build()) + + val privateKey = obj.getJSONObject("keys").getString("privateKey") + val jDevice = obj.getJSONObject("device") + + val ifaceBuilder = Interface.Builder() + ifaceBuilder.parsePrivateKey(privateKey) + ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv4Address"))) + ifaceBuilder.addAddress(InetNetwork.parse(jDevice.getString("ipv6Address"))) + ifaceBuilder.addDnsServer(InetNetwork.parse(obj.getString("dns")).address) + val jExcludedApplication = obj.getJSONArray("excludedApps") + (0 until jExcludedApplication.length()).toList().forEach { + val appName = jExcludedApplication.get(it).toString() + ifaceBuilder.excludeApplication(appName) + } + confBuilder.setInterface(ifaceBuilder.build()) + return confBuilder.build() } + + fun getVpnConfig(): JSONObject { + return mConfig!! + } + + private fun startOpenVpn() { + Thread ({ + mOpenVPNThreadv3?.run() + }).start() + } + + fun openvpnConnected(){ + isUp = true; + } + + + private fun startWireGuard(){ + val wireguard_conf = buildWireugardConfig(mConfig!!) + if (currentTunnelHandle != -1) { + Log.e(tag, "Tunnel already up") + // Turn the tunnel down because this might be a switch + wgTurnOff(currentTunnelHandle) + } + val wgConfig: String = wireguard_conf!!.toWgUserspaceString() + val builder = Builder() + setupBuilder(wireguard_conf, builder) + builder.setSession("mvpn0") + builder.establish().use { tun -> + if (tun == null)return + Log.i(tag, "Go backend " + wgVersion()) + currentTunnelHandle = wgTurnOn("mvpn0", tun.detachFd(), wgConfig) + } + if (currentTunnelHandle < 0) { + Log.e(tag, "Activation Error Code -> $currentTunnelHandle") + isUp = false + return + } + protect(wgGetSocketV4(currentTunnelHandle)) + protect(wgGetSocketV6(currentTunnelHandle)) + isUp = true + + // Store the config in case the service gets + // asked boot vpn from the OS + val prefs = Prefs.get(this) + prefs.edit() + .putString("lastConf", mConfig.toString()) + .apply() + + NotificationUtil.show(this) // Go foreground + } + companion object { + @JvmStatic + fun startService(c: Context) { + c.applicationContext.startService( + Intent(c.applicationContext, VPNService::class.java).apply { + putExtra("startOnly", true) + }) + } + + @JvmStatic + private external fun wgGetConfig(handle: Int): String? + @JvmStatic + private external fun wgGetSocketV4(handle: Int): Int + @JvmStatic + private external fun wgGetSocketV6(handle: Int): Int + @JvmStatic + private external fun wgTurnOff(handle: Int) + @JvmStatic + private external fun wgTurnOn(ifName: String, tunFd: Int, settings: String): Int + @JvmStatic + private external fun wgVersion(): String? + } + } diff --git a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt index 51c8e876d..8ed00b859 100644 --- a/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt +++ b/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt @@ -31,7 +31,6 @@ class VPNServiceBinder(service: VPNService) : Binder() { const val resumeActivate = 7 const val setNotificationText = 8 const val setFallBackNotification = 9 - const val myLog = 10 } /** @@ -54,7 +53,6 @@ class VPNServiceBinder(service: VPNService) : Binder() { val buffer = data.createByteArray() val json = buffer?.let { String(it) } val config = JSONObject(json) - Log.v(tag, "config: " + config.toString()) Log.v(tag, "Stored new Tunnel config in Service") if (!mService.checkPermissions()) { @@ -65,109 +63,108 @@ class VPNServiceBinder(service: VPNService) : Binder() { return true } this.mService.turnOn(config) + } catch (e: Exception) { + Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}") + dispatchEvent(EVENTS.activationError, e.localizedMessage) + } + return true + } + + ACTIONS.resumeActivate -> { + // [data] is empty + // Activate the current tunnel + try { + mResumeConfig?.let { this.mService.turnOn(it) } } catch (e: Exception) { Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}") - dispatchEvent(EVENTS.activationError, e.localizedMessage) } return true } - ACTIONS.resumeActivate -> { - // [data] is empty - // Activate the current tunnel - try { - mResumeConfig?.let { this.mService.turnOn(it) } - } catch (e: Exception) { - Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}") - } - return true - } + ACTIONS.deactivate -> { + // [data] here is empty + this.mService.turnOff() + return true + } - ACTIONS.deactivate -> { - // [data] here is empty - this.mService.turnOff() - return true - } + ACTIONS.registerEventListener -> { + // [data] contains the Binder that we need to dispatch the Events + val binder = data.readStrongBinder() + mListener = binder + val obj = JSONObject() + obj.put("connected", mService.isUp) + obj.put("time", mService.connectionTime) + dispatchEvent(EVENTS.init, obj.toString()) + return true + } - ACTIONS.registerEventListener -> { - // [data] contains the Binder that we need to dispatch the Events - val binder = data.readStrongBinder() - mListener = binder - val obj = JSONObject() - obj.put("connected", mService.isUp) - obj.put("time", mService.connectionTime) - dispatchEvent(EVENTS.init, obj.toString()) - Log.i(tag, "ACTIONS.registerEventListener") - return true - } + ACTIONS.requestStatistic -> { + dispatchEvent(EVENTS.statisticUpdate, mService.status.toString()) + return true + } - ACTIONS.requestStatistic -> { - dispatchEvent(EVENTS.statisticUpdate, mService.status.toString()) - return true - } + ACTIONS.requestGetLog -> { + // Grabs all the Logs and dispatch new Log Event + dispatchEvent(EVENTS.backendLogs, Log.getContent()) + return true + } + ACTIONS.requestCleanupLog -> { + Log.clearFile() + return true + } + ACTIONS.setNotificationText -> { + NotificationUtil.update(data) + return true + } + ACTIONS.setFallBackNotification -> { + NotificationUtil.saveFallBackMessage(data, mService) + return true + } + IBinder.LAST_CALL_TRANSACTION -> { + Log.e(tag, "The OS Requested to shut down the VPN") + this.mService.turnOff() + return true + } - ACTIONS.requestGetLog -> { - // Grabs all the Logs and dispatch new Log Event - dispatchEvent(EVENTS.backendLogs, Log.getContent()) - return true - } - ACTIONS.requestCleanupLog -> { - Log.clearFile() - return true - } - ACTIONS.setNotificationText -> { - NotificationUtil.update(data) - return true - } - ACTIONS.setFallBackNotification -> { - // NotificationUtil.saveFallBackMessage(data, mService) - return true - } - IBinder.LAST_CALL_TRANSACTION -> { - Log.e(tag, "The OS Requested to shut down the VPN") - this.mService.turnOff() - return true - } + else -> { + Log.e(tag, "Received invalid bind request \t Code -> $code") + // If we're hitting this there is probably something wrong in the client. + return false + } + } + return false + } - else -> { - Log.e(tag, "Received invalid bind request \t Code -> $code") - // If we're hitting this there is probably something wrong in the client. - return false + /** + * Dispatches an Event to all registered Binders + * [code] the Event that happened - see [EVENTS] + * To register an Eventhandler use [onTransact] with + * [ACTIONS.registerEventListener] + */ + fun dispatchEvent(code: Int, payload: String?) { + try { + mListener?.let { + if (it.isBinderAlive) { + val data = Parcel.obtain() + data.writeByteArray(payload?.toByteArray(charset("UTF-8"))) + it.transact(code, data, Parcel.obtain(), 0) } } - return false + } catch (e: DeadObjectException) { + // If the QT Process is killed (not just inactive) + // we cant access isBinderAlive, so nothing to do here. + } } /** - * Dispatches an Event to all registered Binders - * [code] the Event that happened - see [EVENTS] - * To register an Eventhandler use [onTransact] with - * [ACTIONS.registerEventListener] + * The codes we Are Using in case of [dispatchEvent] */ - fun dispatchEvent(code: Int, payload: String?) { - try { - mListener?.let { - if (it.isBinderAlive) { - val data = Parcel.obtain() - data.writeByteArray(payload?.toByteArray(charset("UTF-8"))) - it.transact(code, data, Parcel.obtain(), 0) - } - } - } catch (e: DeadObjectException) { - // If the QT Process is killed (not just inactive) - // we cant access isBinderAlive, so nothing to do here. - } - } - - /** - * The codes we Are Using in case of [dispatchEvent] - */ - object EVENTS { - const val init = 0 - const val connected = 1 - const val disconnected = 2 - const val statisticUpdate = 3 - const val backendLogs = 4 - const val activationError = 5 - } + object EVENTS { + const val init = 0 + const val connected = 1 + const val disconnected = 2 + const val statisticUpdate = 3 + const val backendLogs = 4 + const val activationError = 5 } + } diff --git a/client/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt b/client/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt index d3d3eeee2..46d1b80b1 100644 --- a/client/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt +++ b/client/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt @@ -16,8 +16,8 @@ class VPNPermissionHelper : android.net.VpnService() { * is present and prompting if not. */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.i(tag, "onStartCommand") val intent = prepare(this.applicationContext) - Log.i(tag, "VPNPermissionHelper onStartCommand") if (intent != null) { startActivityForResult(intent) } diff --git a/client/main.cpp b/client/main.cpp index 8fd339c0c..74bf6e8de 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -121,6 +121,7 @@ int main(int argc, char *argv[]) qRegisterMetaType("Protocol"); qRegisterMetaType("ServiceType"); qRegisterMetaType("Page"); + qRegisterMetaType("ConnectionState"); qRegisterMetaType("PageProtocolLogicBase *"); diff --git a/client/protocols/android_vpnprotocol.cpp b/client/protocols/android_vpnprotocol.cpp index ba098607d..077f68de6 100644 --- a/client/protocols/android_vpnprotocol.cpp +++ b/client/protocols/android_vpnprotocol.cpp @@ -58,7 +58,7 @@ AndroidVpnProtocol* AndroidVpnProtocol::instance() { return s_instance; } -void AndroidVpnProtocol::initialize() +bool AndroidVpnProtocol::initialize() { qDebug() << "Initializing"; @@ -81,9 +81,12 @@ void AndroidVpnProtocol::initialize() "(Landroid/content/Context;)V", appContext.object()); // Start the VPN Service (if not yet) and Bind to it - QtAndroid::bindService( + const bool bindResult = QtAndroid::bindService( QAndroidIntent(appContext.object(), "org.amnezia.vpn.VPNService"), *this, QtAndroid::BindFlag::AutoCreate); + qDebug() << "Binding to the service..." << bindResult; + + return bindResult; } ErrorCode AndroidVpnProtocol::start() @@ -91,45 +94,49 @@ ErrorCode AndroidVpnProtocol::start() qDebug() << "Prompting for VPN permission"; auto appContext = QtAndroid::androidActivity().callObjectMethod( - "getApplicationContext", "()Landroid/content/Context;"); + "getApplicationContext", "()Landroid/content/Context;"); QAndroidJniObject::callStaticMethod( - PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V", - appContext.object()); + PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V", + appContext.object()); -// QJsonObject jServer; -// jServer["ipv4AddrIn"] = server.ipv4AddrIn(); -// jServer["ipv4Gateway"] = server.ipv4Gateway(); -// jServer["ipv6AddrIn"] = server.ipv6AddrIn(); -// jServer["ipv6Gateway"] = server.ipv6Gateway(); -// jServer["publicKey"] = server.publicKey(); -// jServer["port"] = (int)server.choosePort(); + // QJsonObject jServer; + // jServer["ipv4AddrIn"] = server.ipv4AddrIn(); + // jServer["ipv4Gateway"] = server.ipv4Gateway(); + // jServer["ipv6AddrIn"] = server.ipv6AddrIn(); + // jServer["ipv6Gateway"] = server.ipv6Gateway(); + // jServer["publicKey"] = server.publicKey(); + // jServer["port"] = (int)server.choosePort(); -// QJsonArray allowedIPs; -// foreach (auto item, allowedIPAddressRanges) { -// QJsonValue val; -// val = item.toString(); -// allowedIPs.append(val); -// } + // QJsonArray allowedIPs; + // foreach (auto item, allowedIPAddressRanges) { + // QJsonValue val; + // val = item.toString(); + // allowedIPs.append(val); + // } -// QJsonArray excludedApps; -// foreach (auto appID, vpnDisabledApps) { -// excludedApps.append(QJsonValue(appID)); -// } + // QJsonArray excludedApps; + // foreach (auto appID, vpnDisabledApps) { + // excludedApps.append(QJsonValue(appID)); + // } -// QJsonObject args; -// args["device"] = jDevice; -// args["keys"] = jKeys; -// args["server"] = jServer; -// args["reason"] = (int)reason; -// args["allowedIPs"] = allowedIPs; -// args["excludedApps"] = excludedApps; -// args["dns"] = dns.toString(); + // QJsonObject args; + // args["device"] = jDevice; + // args["keys"] = jKeys; + // args["server"] = jServer; + // args["reason"] = (int)reason; + // args["allowedIPs"] = allowedIPs; + // args["excludedApps"] = excludedApps; + // args["dns"] = dns.toString(); QAndroidParcel sendData; sendData.writeData(QJsonDocument(m_rawConfig).toJson()); - m_serviceBinder.transact(ACTION_ACTIVATE, sendData, nullptr); - return NoError; + bool activateResult = false; + while (!activateResult){ + activateResult = m_serviceBinder.transact(ACTION_ACTIVATE, sendData, nullptr); + } + + return activateResult ? NoError : UnknownError; } // Activates the tunnel that is currently set @@ -212,7 +219,7 @@ void AndroidVpnProtocol::cleanupBackendLogs() { void AndroidVpnProtocol::onServiceConnected( const QString& name, const QAndroidBinder& serviceBinder) { - qDebug() << "Server connected"; + qDebug() << "Server " + name + " connected"; Q_UNUSED(name); diff --git a/client/protocols/android_vpnprotocol.h b/client/protocols/android_vpnprotocol.h index 70e1fc1a2..23365eee9 100644 --- a/client/protocols/android_vpnprotocol.h +++ b/client/protocols/android_vpnprotocol.h @@ -22,7 +22,7 @@ public: virtual ~AndroidVpnProtocol() override = default; - void initialize(); + bool initialize(); virtual ErrorCode start() override; virtual void stop() override; diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 930bd952d..37fb3d9bb 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -232,7 +232,6 @@ ErrorCode VpnConnection::connectToVpn(int serverIndex, m_vpnProtocol.reset(); } - ErrorCode e = ErrorCode::NoError; m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig); if (e) { @@ -252,8 +251,12 @@ ErrorCode VpnConnection::connectToVpn(int serverIndex, #else - AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(Protocol::OpenVpn, m_vpnConfiguration); - androidVpnProtocol->initialize(); + Protocol proto = ContainerProps::defaultProtocol(container); + AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration); + if (!androidVpnProtocol->initialize()) { + qDebug() << QString("Init failed") ; + return UnknownError; + } m_vpnProtocol.reset(androidVpnProtocol); #endif