add disable openvpnconnection, refactoring

This commit is contained in:
Розов Никита Валерьевич 2021-10-13 18:33:43 +03:00
parent 6c2e6ead2b
commit 28ae7eeaee
8 changed files with 500 additions and 482 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -121,6 +121,7 @@ int main(int argc, char *argv[])
qRegisterMetaType<Protocol>("Protocol");
qRegisterMetaType<ServiceType>("ServiceType");
qRegisterMetaType<Page>("Page");
qRegisterMetaType<VpnProtocol::ConnectionState>("ConnectionState");
qRegisterMetaType<PageProtocolLogicBase *>("PageProtocolLogicBase *");

View file

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

View file

@ -22,7 +22,7 @@ public:
virtual ~AndroidVpnProtocol() override = default;
void initialize();
bool initialize();
virtual ErrorCode start() override;
virtual void stop() override;

View file

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