Exclude protocol libraries from loading at application startup
This commit is contained in:
parent
138e6f70a4
commit
8735eee662
9 changed files with 92 additions and 59 deletions
|
@ -3,10 +3,16 @@ package org.amnezia.vpn.protocol.cloak
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import net.openvpn.ovpn3.ClientAPI_Config
|
import net.openvpn.ovpn3.ClientAPI_Config
|
||||||
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
import org.amnezia.vpn.protocol.openvpn.OpenVpn
|
||||||
|
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Cloak : OpenVpn() {
|
class Cloak : OpenVpn() {
|
||||||
|
|
||||||
|
override fun internalInit() {
|
||||||
|
super.internalInit()
|
||||||
|
if (!isInitialized) loadSharedLibrary(context, "ck-ovpn-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
override fun parseConfig(config: JSONObject): ClientAPI_Config {
|
override fun parseConfig(config: JSONObject): ClientAPI_Config {
|
||||||
val openVpnConfig = ClientAPI_Config()
|
val openVpnConfig = ClientAPI_Config()
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.amnezia.vpn.protocol.Protocol
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
import org.amnezia.vpn.protocol.Statistics
|
import org.amnezia.vpn.protocol.Statistics
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
|
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
||||||
import org.amnezia.vpn.util.net.InetNetwork
|
import org.amnezia.vpn.util.net.InetNetwork
|
||||||
import org.amnezia.vpn.util.net.getLocalNetworks
|
import org.amnezia.vpn.util.net.getLocalNetworks
|
||||||
import org.amnezia.vpn.util.net.parseInetAddress
|
import org.amnezia.vpn.util.net.parseInetAddress
|
||||||
|
@ -34,7 +35,10 @@ open class OpenVpn : Protocol() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun internalInit() {
|
override fun internalInit() {
|
||||||
if (!isInitialized) loadSharedLibrary(context, "ovpn3")
|
if (!isInitialized) {
|
||||||
|
loadSharedLibrary(context, "ovpn3")
|
||||||
|
loadSharedLibrary(context, "ovpnutil")
|
||||||
|
}
|
||||||
if (this::scope.isInitialized) {
|
if (this::scope.isInitialized) {
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ 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 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)
|
||||||
|
|
|
@ -158,60 +158,6 @@ abstract class Protocol {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
vpnBuilder.setMetered(false)
|
vpnBuilder.setMetered(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
|
|
||||||
Log.d(TAG, "Extracting library: $libraryName")
|
|
||||||
val apks = hashSetOf<String>()
|
|
||||||
context.applicationInfo.run {
|
|
||||||
sourceDir?.let { apks += it }
|
|
||||||
splitSourceDirs?.let { apks += it }
|
|
||||||
}
|
|
||||||
for (abi in Build.SUPPORTED_ABIS) {
|
|
||||||
for (apk in apks) {
|
|
||||||
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
|
|
||||||
val mappedName = System.mapLibraryName(libraryName)
|
|
||||||
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
|
|
||||||
val zipEntry = zipFile.getEntry(libraryZipPath)
|
|
||||||
zipEntry?.let {
|
|
||||||
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
|
|
||||||
FileOutputStream(destination).use { outStream ->
|
|
||||||
zipFile.getInputStream(zipEntry).use { inStream ->
|
|
||||||
inStream.copyTo(outStream, 32 * 1024)
|
|
||||||
outStream.fd.sync()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
|
||||||
fun loadSharedLibrary(context: Context, libraryName: String) {
|
|
||||||
Log.d(TAG, "Loading library: $libraryName")
|
|
||||||
try {
|
|
||||||
System.loadLibrary(libraryName)
|
|
||||||
return
|
|
||||||
} catch (_: UnsatisfiedLinkError) {
|
|
||||||
Log.d(TAG, "Failed to load library, try to extract it from apk")
|
|
||||||
}
|
|
||||||
var tempFile: File? = null
|
|
||||||
try {
|
|
||||||
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
|
|
||||||
if (extractLibrary(context, libraryName, tempFile)) {
|
|
||||||
System.load(tempFile.absolutePath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
|
|
||||||
} finally {
|
|
||||||
tempFile?.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
|
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
||||||
|
|
||||||
<array name="bundled_libs">
|
<array name="bundled_libs">
|
||||||
<!-- %%INSERT_EXTRA_LIBS%% -->
|
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<array name="qt_libs">
|
<array name="qt_libs">
|
||||||
|
|
|
@ -43,6 +43,7 @@ import kotlinx.coroutines.withContext
|
||||||
import org.amnezia.vpn.protocol.getStatistics
|
import org.amnezia.vpn.protocol.getStatistics
|
||||||
import org.amnezia.vpn.protocol.getStatus
|
import org.amnezia.vpn.protocol.getStatus
|
||||||
import org.amnezia.vpn.qt.QtAndroidController
|
import org.amnezia.vpn.qt.QtAndroidController
|
||||||
|
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.Prefs
|
import org.amnezia.vpn.util.Prefs
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
|
@ -158,6 +159,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d(TAG, "Create Amnezia activity: $intent")
|
Log.d(TAG, "Create Amnezia activity: $intent")
|
||||||
|
loadLibs()
|
||||||
window.apply {
|
window.apply {
|
||||||
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
statusBarColor = getColor(R.color.black)
|
statusBarColor = getColor(R.color.black)
|
||||||
|
@ -179,6 +181,17 @@ class AmneziaActivity : QtActivity() {
|
||||||
runBlocking { vpnProto = proto.await() }
|
runBlocking { vpnProto = proto.await() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadLibs() {
|
||||||
|
listOf(
|
||||||
|
"rsapss",
|
||||||
|
"crypto_3",
|
||||||
|
"ssl_3",
|
||||||
|
"ssh"
|
||||||
|
).forEach {
|
||||||
|
loadSharedLibrary(this.applicationContext, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun registerBroadcastReceivers() {
|
private fun registerBroadcastReceivers() {
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
registerBroadcastReceiver(
|
registerBroadcastReceiver(
|
||||||
|
|
|
@ -40,7 +40,6 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withTimeout
|
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.ProtocolState.CONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTING
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
|
@ -50,6 +49,7 @@ import org.amnezia.vpn.protocol.ProtocolState.UNKNOWN
|
||||||
import org.amnezia.vpn.protocol.VpnException
|
import org.amnezia.vpn.protocol.VpnException
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
import org.amnezia.vpn.protocol.putStatus
|
import org.amnezia.vpn.protocol.putStatus
|
||||||
|
import org.amnezia.vpn.util.LoadLibraryException
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.Prefs
|
import org.amnezia.vpn.util.Prefs
|
||||||
import org.amnezia.vpn.util.net.NetworkState
|
import org.amnezia.vpn.util.net.NetworkState
|
||||||
|
|
66
client/android/utils/src/main/kotlin/LibraryLoader.kt
Normal file
66
client/android/utils/src/main/kotlin/LibraryLoader.kt
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package org.amnezia.vpn.util
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
|
private const val TAG = "LibraryLoader"
|
||||||
|
|
||||||
|
object LibraryLoader {
|
||||||
|
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
|
||||||
|
Log.d(TAG, "Extracting library: $libraryName")
|
||||||
|
val apks = hashSetOf<String>()
|
||||||
|
context.applicationInfo.run {
|
||||||
|
sourceDir?.let { apks += it }
|
||||||
|
splitSourceDirs?.let { apks += it }
|
||||||
|
}
|
||||||
|
for (abi in Build.SUPPORTED_ABIS) {
|
||||||
|
for (apk in apks) {
|
||||||
|
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
|
||||||
|
val mappedName = System.mapLibraryName(libraryName)
|
||||||
|
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
|
||||||
|
val zipEntry = zipFile.getEntry(libraryZipPath)
|
||||||
|
zipEntry?.let {
|
||||||
|
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
|
||||||
|
FileOutputStream(destination).use { outStream ->
|
||||||
|
zipFile.getInputStream(zipEntry).use { inStream ->
|
||||||
|
inStream.copyTo(outStream, 32 * 1024)
|
||||||
|
outStream.fd.sync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
|
fun loadSharedLibrary(context: Context, libraryName: String) {
|
||||||
|
Log.d(TAG, "Loading library: $libraryName")
|
||||||
|
try {
|
||||||
|
System.loadLibrary(libraryName)
|
||||||
|
return
|
||||||
|
} catch (_: UnsatisfiedLinkError) {
|
||||||
|
Log.d(TAG, "Failed to load library, try to extract it from apk")
|
||||||
|
}
|
||||||
|
var tempFile: File? = null
|
||||||
|
try {
|
||||||
|
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
|
||||||
|
if (extractLibrary(context, libraryName, tempFile)) {
|
||||||
|
System.load(tempFile.absolutePath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
|
||||||
|
} finally {
|
||||||
|
tempFile?.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
|
|
@ -3,7 +3,6 @@ package org.amnezia.vpn.protocol.wireguard
|
||||||
import android.net.VpnService.Builder
|
import android.net.VpnService.Builder
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.TreeMap
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -13,6 +12,7 @@ import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
import org.amnezia.vpn.protocol.Statistics
|
import org.amnezia.vpn.protocol.Statistics
|
||||||
import org.amnezia.vpn.protocol.VpnStartException
|
import org.amnezia.vpn.protocol.VpnStartException
|
||||||
|
import org.amnezia.vpn.util.LibraryLoader.loadSharedLibrary
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
import org.amnezia.vpn.util.asSequence
|
import org.amnezia.vpn.util.asSequence
|
||||||
import org.amnezia.vpn.util.net.InetEndpoint
|
import org.amnezia.vpn.util.net.InetEndpoint
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue