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 = _connectionState suspend fun connect(vpnConfig: VpnConfig): Result { 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 }