Add bridge support
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
package io.nekohasekai.sfa.bg;
|
||||||
|
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
interface IBridgeSession {
|
||||||
|
ParcelFileDescriptor getFileDescriptor();
|
||||||
|
String getName();
|
||||||
|
boolean isInet6Active();
|
||||||
|
void setEgress(String interfaceName);
|
||||||
|
void close();
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.nekohasekai.sfa.bg;
|
package io.nekohasekai.sfa.bg;
|
||||||
|
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import io.nekohasekai.sfa.bg.IBridgeSession;
|
||||||
import io.nekohasekai.sfa.bg.INeighborTableCallback;
|
import io.nekohasekai.sfa.bg.INeighborTableCallback;
|
||||||
import io.nekohasekai.sfa.bg.IRootShellSession;
|
import io.nekohasekai.sfa.bg.IRootShellSession;
|
||||||
import io.nekohasekai.sfa.bg.ParceledListSlice;
|
import io.nekohasekai.sfa.bg.ParceledListSlice;
|
||||||
@@ -21,4 +22,6 @@ interface IRootService {
|
|||||||
IRootShellSession openShellSession(String user, String command, in String[] env, String term, int rows, int cols) = 6;
|
IRootShellSession openShellSession(String user, String command, in String[] env, String term, int rows, int cols) = 6;
|
||||||
|
|
||||||
String lookupSFTPServer() = 7;
|
String lookupSFTPServer() = 7;
|
||||||
|
|
||||||
|
IBridgeSession openBridge(String bridgeName, int mtu, String inet4Port, String inet6Port, int ruleIndex, int routeTable) = 8;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import android.provider.Settings
|
|||||||
import android.system.OsConstants
|
import android.system.OsConstants
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.nekohasekai.libbox.BridgeOptions
|
||||||
|
import io.nekohasekai.libbox.BridgeSession
|
||||||
import io.nekohasekai.libbox.ConnectionOwner
|
import io.nekohasekai.libbox.ConnectionOwner
|
||||||
import io.nekohasekai.libbox.InterfaceUpdateListener
|
import io.nekohasekai.libbox.InterfaceUpdateListener
|
||||||
import io.nekohasekai.libbox.Libbox
|
import io.nekohasekai.libbox.Libbox
|
||||||
@@ -279,6 +281,25 @@ interface PlatformInterfaceWrapper : PlatformInterface {
|
|||||||
)?.takeIf { it.isNotBlank() }
|
)?.takeIf { it.isNotBlank() }
|
||||||
?: "${Build.MANUFACTURER} ${Build.MODEL}"
|
?: "${Build.MANUFACTURER} ${Build.MODEL}"
|
||||||
|
|
||||||
|
override fun usePlatformBridge(): Boolean = RootClient.rootAvailable.value ?: runBlocking(Dispatchers.IO) {
|
||||||
|
RootClient.checkRootAvailable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createBridge(options: BridgeOptions?): BridgeSession {
|
||||||
|
options!!
|
||||||
|
val session = runBlocking(Dispatchers.IO) {
|
||||||
|
RootClient.openBridge(
|
||||||
|
options.bridgeName,
|
||||||
|
options.mtu,
|
||||||
|
options.inet4Port,
|
||||||
|
options.inet6Port,
|
||||||
|
options.ruleIndex,
|
||||||
|
options.routeTable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return RootBridgeSessionWrapper(session)
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookupUser(username: String?): io.nekohasekai.libbox.PlatformUser {
|
override fun lookupUser(username: String?): io.nekohasekai.libbox.PlatformUser {
|
||||||
val resolved = UserResolver.resolve(Application.packageManager, username!!)
|
val resolved = UserResolver.resolve(Application.packageManager, username!!)
|
||||||
val platformUser = io.nekohasekai.libbox.PlatformUser()
|
val platformUser = io.nekohasekai.libbox.PlatformUser()
|
||||||
@@ -300,6 +321,24 @@ interface PlatformInterfaceWrapper : PlatformInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RootBridgeSessionWrapper(
|
||||||
|
private val session: IBridgeSession,
|
||||||
|
) : BridgeSession {
|
||||||
|
override fun fileDescriptor(): Int = session.fileDescriptor.detachFd()
|
||||||
|
|
||||||
|
override fun name(): String = session.name
|
||||||
|
|
||||||
|
override fun inet6Active(): Boolean = session.isInet6Active
|
||||||
|
|
||||||
|
override fun setEgress(interfaceName: String?) {
|
||||||
|
session.setEgress(interfaceName ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class RootShellSessionWrapper(
|
private class RootShellSessionWrapper(
|
||||||
private val rootSession: IRootShellSession,
|
private val rootSession: IRootShellSession,
|
||||||
) : ShellSession {
|
) : ShellSession {
|
||||||
|
|||||||
@@ -169,6 +169,22 @@ object RootClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun openBridge(
|
||||||
|
bridgeName: String,
|
||||||
|
mtu: Int,
|
||||||
|
inet4Port: String,
|
||||||
|
inet6Port: String,
|
||||||
|
ruleIndex: Int,
|
||||||
|
routeTable: Int,
|
||||||
|
): IBridgeSession {
|
||||||
|
val svc = bindService()
|
||||||
|
try {
|
||||||
|
return svc.openBridge(bridgeName, mtu, inet4Port, inet6Port, ruleIndex, routeTable)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
throw e.rethrowAsRuntime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun unregisterNeighborTableCallback(callback: INeighborTableCallback) {
|
suspend fun unregisterNeighborTableCallback(callback: INeighborTableCallback) {
|
||||||
try {
|
try {
|
||||||
service?.unregisterNeighborTableCallback(callback)
|
service?.unregisterNeighborTableCallback(callback)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import android.os.ParcelFileDescriptor
|
|||||||
import android.os.RemoteCallbackList
|
import android.os.RemoteCallbackList
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.topjohnwu.superuser.ipc.RootService
|
import com.topjohnwu.superuser.ipc.RootService
|
||||||
|
import io.nekohasekai.libbox.BridgeOptions
|
||||||
|
import io.nekohasekai.libbox.BridgeSession
|
||||||
import io.nekohasekai.libbox.Libbox
|
import io.nekohasekai.libbox.Libbox
|
||||||
import io.nekohasekai.libbox.NeighborEntryIterator
|
import io.nekohasekai.libbox.NeighborEntryIterator
|
||||||
import io.nekohasekai.libbox.NeighborSubscription
|
import io.nekohasekai.libbox.NeighborSubscription
|
||||||
@@ -36,6 +38,8 @@ class RootServer : RootService() {
|
|||||||
private var tetheringCallback: Any? = null
|
private var tetheringCallback: Any? = null
|
||||||
private var tetheringManager: Any? = null
|
private var tetheringManager: Any? = null
|
||||||
|
|
||||||
|
private val bridgeSessions = mutableSetOf<BridgeSessionBinder>()
|
||||||
|
|
||||||
private val binder = object : IRootService.Stub() {
|
private val binder = object : IRootService.Stub() {
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
stopSelf()
|
stopSelf()
|
||||||
@@ -178,6 +182,29 @@ class RootServer : RootService() {
|
|||||||
return RootShellSession(session)
|
return RootShellSession(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openBridge(
|
||||||
|
bridgeName: String?,
|
||||||
|
mtu: Int,
|
||||||
|
inet4Port: String?,
|
||||||
|
inet6Port: String?,
|
||||||
|
ruleIndex: Int,
|
||||||
|
routeTable: Int,
|
||||||
|
): IBridgeSession {
|
||||||
|
val options = BridgeOptions()
|
||||||
|
options.bridgeName = bridgeName ?: ""
|
||||||
|
options.mtu = mtu
|
||||||
|
options.inet4Port = inet4Port ?: ""
|
||||||
|
options.inet6Port = inet6Port ?: ""
|
||||||
|
options.ruleIndex = ruleIndex
|
||||||
|
options.routeTable = routeTable
|
||||||
|
val session = Libbox.newBridgeService(options)
|
||||||
|
val binder = BridgeSessionBinder(session)
|
||||||
|
synchronized(bridgeSessions) {
|
||||||
|
bridgeSessions.add(binder)
|
||||||
|
}
|
||||||
|
return binder
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookupSFTPServer(): String {
|
override fun lookupSFTPServer(): String {
|
||||||
val termuxPrefix = File(UserResolver.TERMUX_PREFIX)
|
val termuxPrefix = File(UserResolver.TERMUX_PREFIX)
|
||||||
for (name in arrayOf("libexec/sftp-server", "lib/openssh/sftp-server")) {
|
for (name in arrayOf("libexec/sftp-server", "lib/openssh/sftp-server")) {
|
||||||
@@ -219,6 +246,28 @@ class RootServer : RootService() {
|
|||||||
return env.map { (k, v) -> "$k=$v" }.toTypedArray()
|
return env.map { (k, v) -> "$k=$v" }.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class BridgeSessionBinder(
|
||||||
|
private val session: BridgeSession,
|
||||||
|
) : IBridgeSession.Stub() {
|
||||||
|
|
||||||
|
override fun getFileDescriptor(): ParcelFileDescriptor = ParcelFileDescriptor.fromFd(session.fileDescriptor())
|
||||||
|
|
||||||
|
override fun getName(): String = session.name()
|
||||||
|
|
||||||
|
override fun isInet6Active(): Boolean = session.inet6Active()
|
||||||
|
|
||||||
|
override fun setEgress(interfaceName: String?) {
|
||||||
|
session.setEgress(interfaceName ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
synchronized(bridgeSessions) {
|
||||||
|
bridgeSessions.remove(this)
|
||||||
|
}
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class RootShellSession(
|
private class RootShellSession(
|
||||||
private val session: ShellSession,
|
private val session: ShellSession,
|
||||||
) : IRootShellSession.Stub() {
|
) : IRootShellSession.Stub() {
|
||||||
@@ -363,6 +412,17 @@ class RootServer : RootService() {
|
|||||||
override fun onBind(intent: Intent): IBinder = binder
|
override fun onBind(intent: Intent): IBinder = binder
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
val sessions = synchronized(bridgeSessions) {
|
||||||
|
val copied = bridgeSessions.toList()
|
||||||
|
bridgeSessions.clear()
|
||||||
|
copied
|
||||||
|
}
|
||||||
|
for (session in sessions) {
|
||||||
|
try {
|
||||||
|
session.close()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
stopTetheringMonitor()
|
stopTetheringMonitor()
|
||||||
neighborSubscription?.close()
|
neighborSubscription?.close()
|
||||||
neighborSubscription = null
|
neighborSubscription = null
|
||||||
|
|||||||
Reference in New Issue
Block a user