Add OpenVpn over Cloak module

This commit is contained in:
albexk 2023-11-28 22:27:00 +03:00
parent 51d4aea9e2
commit eaa209bc3a
7 changed files with 110 additions and 16 deletions

View file

@ -89,6 +89,7 @@ dependencies {
implementation(project(":wireguard")) implementation(project(":wireguard"))
implementation(project(":awg")) implementation(project(":awg"))
implementation(project(":openvpn")) implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(libs.androidx.core) implementation(libs.androidx.core)
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
implementation(libs.androidx.security.crypto) implementation(libs.androidx.security.crypto)

View file

@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.cloak"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":openvpn"))
}

View file

@ -0,0 +1,70 @@
package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "cloak",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* "cloak_config_data": {
* "BrowserSig": "chrome",
* "EncryptionMethod": "aes-gcm",
* "NumConn": 1,
* "ProxyMethod": "openvpn",
* "PublicKey": "PublicKey=",
* "RemoteHost": "100.100.100.0",
* "RemotePort": "443",
* "ServerName": "servername",
* "StreamTimeout": 300,
* "Transport": "direct",
* "UID": "UID="
* }
* }
*/
class Cloak : OpenVpn() {
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
openVpnConfig.usePluggableTransports = true
openVpnConfig.content = configStr
return openVpnConfig
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
// todo: strange method
if (!cloakConfigJson.has("NumConn")) cloakConfigJson.put("NumConn", 1)
if (!cloakConfigJson.has("ProxyMethod")) cloakConfigJson.put("ProxyMethod", "openvpn")
if (cloakConfigJson.has("port")) {
val port = cloakConfigJson["port"]
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", port)
}
if (cloakConfigJson.has("remote")) {
val remote = cloakConfigJson["remote"]
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", remote)
}
return cloakConfigJson
}
}

View file

@ -8,6 +8,7 @@ import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnException
import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.NetworkUtils import org.amnezia.vpn.util.NetworkUtils
import org.json.JSONObject import org.json.JSONObject
@ -29,10 +30,10 @@ import org.json.JSONObject
* } * }
*/ */
class OpenVpn : Protocol() { open class OpenVpn : Protocol() {
private lateinit var context: Context private lateinit var context: Context
private var openVpnClient: OpenVpnClient? = null protected var openVpnClient: OpenVpnClient? = null
override val statistics: Statistics override val statistics: Statistics
get() { get() {
@ -54,6 +55,7 @@ class OpenVpn : Protocol() {
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder() val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient( openVpnClient = OpenVpnClient(
configBuilder, configBuilder,
state, state,
@ -61,12 +63,18 @@ class OpenVpn : Protocol() {
makeEstablish(configBuilder, vpnBuilder), makeEstablish(configBuilder, vpnBuilder),
protect protect
) )
try { try {
parseConfig(config)
openVpnClient?.let { client -> openVpnClient?.let { client ->
val openVpnConfig = parseConfig(config)
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
val status = client.connect() val status = client.connect()
if (status.error) { if (status.error) {
throw VpnStartException("OpenVpn connect() error: ${status.status}: ${status.message}") throw VpnException("OpenVpn connect() error: ${status.status}: ${status.message}")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -80,15 +88,10 @@ class OpenVpn : Protocol() {
openVpnClient = null openVpnClient = null
} }
private fun parseConfig(config: JSONObject) { protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config() val openVpnConfig = ClientAPI_Config()
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config") openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
openVpnClient?.let { client -> return openVpnConfig
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
}
} }
private fun makeEstablish(configBuilder: OpenVpnConfig.Builder, vpnBuilder: Builder): () -> Int = private fun makeEstablish(configBuilder: OpenVpnConfig.Builder, vpnBuilder: Builder): () -> Int =

View file

@ -3,7 +3,7 @@ package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnNotAuthorizedException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause) class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)

View file

@ -35,6 +35,7 @@ include(":protocolApi")
include(":wireguard") include(":wireguard")
include(":awg") include(":awg")
include(":openvpn") include(":openvpn")
include(":cloak")
// get values from gradle or local properties // get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties val androidBuildToolsVersion: String by gradleProperties

View file

@ -21,13 +21,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.amnezia.vpn.protocol.BadConfigException import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.LoadLibraryException import org.amnezia.vpn.protocol.LoadLibraryException
import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.Protocol
@ -38,8 +35,10 @@ import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTING
import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
import org.amnezia.vpn.protocol.Statistics import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.Status import org.amnezia.vpn.protocol.Status
import org.amnezia.vpn.protocol.VpnException
import org.amnezia.vpn.protocol.VpnStartException import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.protocol.awg.Awg import org.amnezia.vpn.protocol.awg.Awg
import org.amnezia.vpn.protocol.cloak.Cloak
import org.amnezia.vpn.protocol.openvpn.OpenVpn import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.amnezia.vpn.protocol.putStatistics import org.amnezia.vpn.protocol.putStatistics
import org.amnezia.vpn.protocol.putStatus import org.amnezia.vpn.protocol.putStatus
@ -83,7 +82,8 @@ class AmneziaVpnService : VpnService() {
protocol = null protocol = null
when (e) { when (e) {
is IllegalArgumentException, is IllegalArgumentException,
is VpnStartException -> onError(e.message ?: e.toString()) is VpnStartException,
is VpnException -> onError(e.message ?: e.toString())
is JSONException, is JSONException,
is BadConfigException -> onError("VPN config format error: ${e.message}") is BadConfigException -> onError("VPN config format error: ${e.message}")
@ -314,6 +314,7 @@ class AmneziaVpnService : VpnService() {
"wireguard" -> Wireguard() "wireguard" -> Wireguard()
"awg" -> Awg() "awg" -> Awg()
"openvpn" -> OpenVpn() "openvpn" -> OpenVpn()
"cloak" -> Cloak()
else -> throw IllegalArgumentException("Failed to load $protocolName protocol") else -> throw IllegalArgumentException("Failed to load $protocolName protocol")
}.apply { initialize(applicationContext, protocolState) } }.apply { initialize(applicationContext, protocolState) }