init
Some checks failed
CI / Rust (push) Successful in 20s
CI / Android (push) Failing after 8m35s

This commit is contained in:
2026-05-31 15:36:07 +03:30
commit 4ffbc3bffe
61 changed files with 2760 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "org.vpnshare.domain"
compileSdk = 36
defaultConfig {
minSdk = 26
}
}

View File

@@ -0,0 +1 @@
<manifest />

View File

@@ -0,0 +1,83 @@
package org.vpnshare.domain.model
import java.time.Instant
@JvmInline
value class PeerId(val value: String)
enum class ShareTransport {
Usb,
Wifi,
Hotspot
}
enum class ClientPlatform {
Windows,
Linux,
MacOs,
Android,
Ios,
Unknown
}
data class GatewayConfig(
val preferredTransport: ShareTransport = ShareTransport.Usb,
val allowWifiFallback: Boolean = true,
val allowHotspot: Boolean = false,
val maxPeers: Int = 4,
val defaultMtu: Int = 1280
)
data class VpnStatus(
val active: Boolean,
val networkName: String?,
val supportsIpv4: Boolean,
val supportsIpv6: Boolean
)
data class PairingRequest(
val requestId: String,
val displayName: String,
val platform: ClientPlatform,
val transport: ShareTransport,
val createdAt: Instant,
val expiresAt: Instant
) {
fun isExpired(now: Instant): Boolean = !expiresAt.isAfter(now)
}
data class PeerDevice(
val id: PeerId,
val displayName: String,
val platform: ClientPlatform,
val trustedAt: Instant,
val lastSeenAt: Instant?,
val revoked: Boolean = false
)
data class TunnelLease(
val peerId: PeerId,
val ipv4Address: String,
val ipv6Address: String?,
val dnsGateway: String,
val mtu: Int,
val routes: List<String>,
val expiresAt: Instant
)
data class GatewayStats(
val connectedPeers: Int = 0,
val bytesFromClients: Long = 0,
val bytesToClients: Long = 0
)
sealed interface ShareState {
data object Stopped : ShareState
data class Starting(val config: GatewayConfig) : ShareState
data class Running(
val config: GatewayConfig,
val vpnStatus: VpnStatus,
val stats: GatewayStats
) : ShareState
data class Failed(val reason: String) : ShareState
}

View File

@@ -0,0 +1,19 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "org.vpnshare.engine"
compileSdk = 36
defaultConfig {
minSdk = 26
}
}
dependencies {
implementation(project(":apps:android:core:domain"))
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
}

View File

@@ -0,0 +1 @@
<manifest />

View File

@@ -0,0 +1,13 @@
package org.vpnshare.engine
import org.vpnshare.domain.model.PairingRequest
import org.vpnshare.domain.model.PeerId
import org.vpnshare.domain.model.ShareState
sealed interface GatewayEvent {
data class StateChanged(val state: ShareState) : GatewayEvent
data class PairingRequested(val request: PairingRequest) : GatewayEvent
data class PeerConnected(val peerId: PeerId) : GatewayEvent
data class PeerDisconnected(val peerId: PeerId, val reason: String) : GatewayEvent
data class Warning(val message: String) : GatewayEvent
}

View File

@@ -0,0 +1,67 @@
package org.vpnshare.engine
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import org.vpnshare.domain.model.GatewayConfig
import org.vpnshare.domain.model.GatewayStats
import org.vpnshare.domain.model.PairingRequest
import org.vpnshare.domain.model.PeerId
import org.vpnshare.domain.model.ShareState
import org.vpnshare.domain.model.VpnStatus
class RustVpnShareEngine(
private val versionProvider: EngineVersionProvider = JniEngineVersionProvider()
) : VpnShareEngine {
private val mutableEvents = MutableSharedFlow<GatewayEvent>(extraBufferCapacity = 64)
override val events: Flow<GatewayEvent> = mutableEvents.asSharedFlow()
override suspend fun startGateway(config: GatewayConfig) {
mutableEvents.emit(GatewayEvent.StateChanged(ShareState.Starting(config)))
mutableEvents.emit(
GatewayEvent.StateChanged(
ShareState.Running(
config = config,
vpnStatus = VpnStatus(
active = false,
networkName = null,
supportsIpv4 = true,
supportsIpv6 = false
),
stats = GatewayStats()
)
)
)
mutableEvents.emit(GatewayEvent.Warning("Rust core linked as ${versionProvider.version()}"))
}
override suspend fun stopGateway() {
mutableEvents.emit(GatewayEvent.StateChanged(ShareState.Stopped))
}
override suspend fun approvePairing(request: PairingRequest): PeerId {
val peerId = PeerId("peer-${request.requestId}")
mutableEvents.emit(GatewayEvent.PeerConnected(peerId))
return peerId
}
override suspend fun rejectPairing(request: PairingRequest) {
mutableEvents.emit(GatewayEvent.Warning("Rejected pairing request ${request.requestId}"))
}
}
interface EngineVersionProvider {
fun version(): String
}
class JniEngineVersionProvider : EngineVersionProvider {
override fun version(): String {
return runCatching {
System.loadLibrary("vpnshare_ffi")
nativeVersion()
}.getOrDefault("unlinked")
}
private external fun nativeVersion(): String
}

View File

@@ -0,0 +1,18 @@
package org.vpnshare.engine
import kotlinx.coroutines.flow.Flow
import org.vpnshare.domain.model.GatewayConfig
import org.vpnshare.domain.model.PairingRequest
import org.vpnshare.domain.model.PeerId
interface VpnShareEngine {
val events: Flow<GatewayEvent>
suspend fun startGateway(config: GatewayConfig)
suspend fun stopGateway()
suspend fun approvePairing(request: PairingRequest): PeerId
suspend fun rejectPairing(request: PairingRequest)
}