Add OpenVpn over Cloak module
This commit is contained in:
parent
51d4aea9e2
commit
eaa209bc3a
7 changed files with 110 additions and 16 deletions
|
@ -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)
|
||||
|
|
18
client/android/cloak/build.gradle.kts
Normal file
18
client/android/cloak/build.gradle.kts
Normal 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"))
|
||||
}
|
70
client/android/cloak/src/main/kotlin/Cloak.kt
Normal file
70
client/android/cloak/src/main/kotlin/Cloak.kt
Normal 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
|
||||
}
|
||||
}
|
|
@ -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 =
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue