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(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
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.ProtocolState
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnException
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.NetworkUtils
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 var openVpnClient: OpenVpnClient? = null
protected var openVpnClient: OpenVpnClient? = null
override val statistics: Statistics
get() {
@ -54,6 +55,7 @@ class OpenVpn : Protocol() {
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(
configBuilder,
state,
@ -61,12 +63,18 @@ class OpenVpn : Protocol() {
makeEstablish(configBuilder, vpnBuilder),
protect
)
try {
parseConfig(config)
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()
if (status.error) {
throw VpnStartException("OpenVpn connect() error: ${status.status}: ${status.message}")
throw VpnException("OpenVpn connect() error: ${status.status}: ${status.message}")
}
}
} catch (e: Exception) {
@ -80,15 +88,10 @@ class OpenVpn : Protocol() {
openVpnClient = null
}
private fun parseConfig(config: JSONObject) {
protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
openVpnClient?.let { client ->
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
}
return openVpnConfig
}
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)
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 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(":awg")
include(":openvpn")
include(":cloak")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties

View file

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