Files
SilverDROID/app/src/main/kotlin/uk/silverlabs/silverdroid/vpn/WireGuardManager.kt
SysAdmin 94887f6cf7 Add configurable deployment system with VPN and Tor support
Features:
- Hidden config.json in assets for per-deployment customization
- Configure target URL, app name, and branding
- Optional WireGuard VPN with auto-connect
- Optional Tor routing via Orbot
- Custom theme colors
- Configuration-driven app behavior

Configuration file location: app/src/main/assets/config.json
Example configuration: config.example.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 18:51:01 +01:00

100 lines
3.0 KiB
Kotlin

package uk.silverlabs.silverdroid.vpn
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.util.Log
import com.wireguard.android.backend.GoBackend
import com.wireguard.config.Config
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import uk.silverlabs.silverdroid.config.VpnConfig
/**
* Manages WireGuard VPN connections
*/
class WireGuardManager(private val context: Context) {
private val backend = GoBackend(context)
private val _connectionState = MutableStateFlow(VpnState.DISCONNECTED)
val connectionState: StateFlow<VpnState> = _connectionState
suspend fun connect(vpnConfig: VpnConfig): Result<Unit> {
return try {
// Check VPN permission
val intent = VpnService.prepare(context)
if (intent != null) {
return Result.failure(Exception("VPN permission required"))
}
// Build WireGuard config
val configText = buildWireGuardConfig(vpnConfig)
val config = Config.parse(configText.byteInputStream())
// Set up tunnel
val tunnel = backend.tunnels.firstOrNull() ?: backend.createTunnel(
"silverdroid_vpn",
config,
null
)
// Connect
backend.setState(tunnel, com.wireguard.android.backend.Tunnel.State.UP)
_connectionState.value = VpnState.CONNECTED
Log.i(TAG, "WireGuard VPN connected successfully")
Result.success(Unit)
} catch (e: Exception) {
Log.e(TAG, "Failed to connect VPN", e)
_connectionState.value = VpnState.ERROR
Result.failure(e)
}
}
suspend fun disconnect() {
try {
backend.tunnels.forEach { tunnel ->
backend.setState(tunnel, com.wireguard.android.backend.Tunnel.State.DOWN)
}
_connectionState.value = VpnState.DISCONNECTED
Log.i(TAG, "WireGuard VPN disconnected")
} catch (e: Exception) {
Log.e(TAG, "Error disconnecting VPN", e)
}
}
private fun buildWireGuardConfig(vpnConfig: VpnConfig): String {
val peers = vpnConfig.peers.joinToString("\n\n") { peer ->
"""
[Peer]
PublicKey = ${peer.publicKey}
Endpoint = ${peer.endpoint}
AllowedIPs = ${peer.allowedIps.joinToString(", ")}
PersistentKeepalive = ${peer.persistentKeepalive}
""".trimIndent()
}
return """
[Interface]
PrivateKey = ${vpnConfig.privateKey}
Address = ${vpnConfig.address}
${if (vpnConfig.dns.isNotEmpty()) "DNS = ${vpnConfig.dns.joinToString(", ")}" else ""}
$peers
""".trimIndent()
}
companion object {
private const val TAG = "WireGuardManager"
}
}
enum class VpnState {
DISCONNECTED,
CONNECTING,
CONNECTED,
DISCONNECTING,
ERROR
}