diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..005afb3
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index bc8a18c..b1c2b60 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -82,6 +82,9 @@ dependencies {
implementation("com.google.protobuf:protobuf-java:4.29.3")
implementation("com.google.protobuf:protobuf-kotlin:4.29.3")
+ implementation("androidx.navigation:navigation-compose:2.9.0")
+ implementation("io.coil-kt:coil-compose:2.6.0")
+
// implementation(libs.bcprov.jdk18on)
implementation(libs.bcpkix.jdk18on)
// implementation(files("libs/org.asxalex.sdlan/sdlan-1.0-SNAPSHOT.jar"))
diff --git a/app/src/main/java/com/jihe/punchnet/PunchnetService.kt b/app/src/main/java/com/jihe/punchnet/PunchnetService.kt
index a72302a..3fc6285 100644
--- a/app/src/main/java/com/jihe/punchnet/PunchnetService.kt
+++ b/app/src/main/java/com/jihe/punchnet/PunchnetService.kt
@@ -19,6 +19,9 @@ import com.jihe.punchnet.sdlan.network.ARPTable
import com.jihe.punchnet.sdlan.network.ARPWaitList
import com.jihe.punchnet.sdlan.network.DeviceConfig
import com.jihe.punchnet.sdlan.network.IfaceTun
+import com.jihe.punchnet.sdlan.network.RouteDetail
+import com.jihe.punchnet.sdlan.network.RouteTable
+import com.jihe.punchnet.sdlan.network.cidrToRouteDetail
import com.jihe.punchnet.sdlan.network.ipInt2ByteArray
import com.jihe.punchnet.sdlan.network.run_sdlan
import kotlinx.coroutines.CoroutineScope
@@ -69,7 +72,7 @@ class PunchnetService : VpnService() , IfaceTun {
override suspend fun send(content: ByteArray) {
withContext(Dispatchers.IO) {
- Log.d(TAG, "WROTE bytes to vpn service")
+ // Log.d(TAG, "WROTE bytes to vpn service")
output?.write(content)
}
}
@@ -104,7 +107,7 @@ class PunchnetService : VpnService() , IfaceTun {
output?.close()
vpnDescriptor?.close()
- stopForeground(true)
+ // stopForeground(true)
stopSelf()
}
@@ -117,6 +120,14 @@ class PunchnetService : VpnService() , IfaceTun {
Log.d("DIR", "external storage = ${Environment.getExternalStorageDirectory().name}")
Log.d("DIR", "filesdir = ${this.filesDir.path}")
+ arpTable.routeTable.clearRoute()
+ for (route in startArg?.routes?.toList()?:listOf()) {
+ val r = cidrToRouteDetail(route.targetNetCIDR, route.gateway)
+ if (r != null) {
+ arpTable.routeTable.addRoute(r)
+ }
+ }
+
val argument = Arguments(
baseDir = this.filesDir.path,
sn = "$server:1265",
@@ -124,13 +135,12 @@ class PunchnetService : VpnService() , IfaceTun {
nat_server1 = "$server:1265",
nat_server2 = "47.98.178.3:1265",
- token = "",
+ token = startArg?.token?:"",
name = "tau",
)
scope.launch {
-
- run_sdlan(iface, argument)
+ run_sdlan(iface, argument, startArg)
}
val notification = createNotification()
@@ -141,8 +151,9 @@ class PunchnetService : VpnService() , IfaceTun {
super.onStartCommand(intent, flags, startId)
return if (intent?.action == ACTION_DISCONNECT) {
+ scope.cancel()
disconnect()
- START_NOT_STICKY
+ START_STICKY
} else {
val argument = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent?.getParcelableExtra("argument", PunchnetServiceArgument::class.java)
@@ -174,8 +185,8 @@ class PunchnetService : VpnService() , IfaceTun {
}
override fun onDestroy() {
- scope.cancel()
- disconnect()
+ // scope.cancel()
+ // disconnect()
super.onDestroy()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/jihe/punchnet/helper/Screens.kt b/app/src/main/java/com/jihe/punchnet/helper/Screens.kt
new file mode 100644
index 0000000..b614f3e
--- /dev/null
+++ b/app/src/main/java/com/jihe/punchnet/helper/Screens.kt
@@ -0,0 +1,6 @@
+package com.jihe.punchnet.helper
+
+sealed class Screen(val route: String) {
+ object ScreenRoutes: Screen("routes")
+ object ScreenMain: Screen("main")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/jihe/punchnet/screen/MainScreen.kt b/app/src/main/java/com/jihe/punchnet/screen/MainScreen.kt
new file mode 100644
index 0000000..c46fba2
--- /dev/null
+++ b/app/src/main/java/com/jihe/punchnet/screen/MainScreen.kt
@@ -0,0 +1,105 @@
+package com.jihe.punchnet.screen
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+
+@Composable
+fun MainApp(
+ navController: NavHostController,
+) {
+
+}
+
+@Composable
+fun CustomHeaderScreen(
+ text: String,
+ onBack: @Composable (() -> Unit)? = null,
+ onMenu: @Composable (() -> Unit)? = null,
+ body: @Composable () -> Unit,
+) {
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box (
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(56.dp)
+ .background(MaterialTheme.colorScheme.surfaceContainer),
+ //.padding(horizontal = 16.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ Row (
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+
+ Box(
+ modifier = Modifier.fillMaxHeight()
+ .padding(horizontal = 8.dp)
+ .width(35.dp)
+ ) {
+ if (onBack != null) {
+ onBack()
+ }
+ }
+
+
+ Text(
+ text,
+ // color = MaterialTheme.colorScheme.onPrimary,
+ style = MaterialTheme.typography.titleLarge,
+ )
+
+ Box(
+ modifier = Modifier.fillMaxHeight()
+ .padding(horizontal = 8.dp)
+ .width(35.dp)
+ ) {
+ if (onMenu != null) {
+ onMenu()
+ /*
+ Icon(
+ modifier = Modifier
+ .clickable {
+ onMenu()
+ }
+ .fillMaxSize(),
+ imageVector = Icons.Filled.Menu,
+ contentDescription = "setting",
+ // tint = MaterialTheme.colorScheme.onPrimary
+ )
+ */
+
+ }
+ }
+
+
+ }
+ }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ body()
+ }
+ }
+}
diff --git a/app/src/main/java/com/jihe/punchnet/sdlan/network/arp.kt b/app/src/main/java/com/jihe/punchnet/sdlan/network/arp.kt
index d1c67db..8facc70 100644
--- a/app/src/main/java/com/jihe/punchnet/sdlan/network/arp.kt
+++ b/app/src/main/java/com/jihe/punchnet/sdlan/network/arp.kt
@@ -11,7 +11,10 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import java.util.concurrent.ConcurrentHashMap
-class ARPTable() {
+class ARPTable(
+) {
+ val routeTable: RouteTable = RouteTable()
+
val content = ConcurrentHashMap()
suspend fun agingARP() {
diff --git a/app/src/main/java/com/jihe/punchnet/sdlan/network/libs.kt b/app/src/main/java/com/jihe/punchnet/sdlan/network/libs.kt
index 83e7849..dbe3ac4 100644
--- a/app/src/main/java/com/jihe/punchnet/sdlan/network/libs.kt
+++ b/app/src/main/java/com/jihe/punchnet/sdlan/network/libs.kt
@@ -1,6 +1,7 @@
package com.jihe.punchnet.sdlan.network
import android.util.Log
+import com.jihe.punchnet.PunchnetServiceArgument
import com.jihe.punchnet.protobuf.PunchProto.SDLDevAddr
import com.jihe.punchnet.protobuf.PunchProto.SDLRegisterSuper
import com.jihe.punchnet.protobuf.PunchProto.SDLRegisterSuperAck
@@ -125,7 +126,7 @@ suspend fun onMessage(data: SDLanTCP) {
}
-suspend fun run_sdlan(iface: Iface, argument: Arguments) {
+suspend fun run_sdlan(iface: Iface, argument: Arguments, routeinfo: PunchnetServiceArgument?) {
UniqueNodeID.setBaseDir(argument.baseDir)
val edgeUUID = UniqueNodeID.getUUID()
val config = parseConfig(edgeUUID, argument)
@@ -135,6 +136,8 @@ suspend fun run_sdlan(iface: Iface, argument: Arguments) {
}
return
}
+
+
val toSocket = Channel(100)
val start_stop_channel = Channel(100)
initEdge(iface, argument.token, config, toSocket, start_stop_channel)
diff --git a/app/src/main/java/com/jihe/punchnet/sdlan/network/route.kt b/app/src/main/java/com/jihe/punchnet/sdlan/network/route.kt
new file mode 100644
index 0000000..d7c0a27
--- /dev/null
+++ b/app/src/main/java/com/jihe/punchnet/sdlan/network/route.kt
@@ -0,0 +1,122 @@
+package com.jihe.punchnet.sdlan.network
+
+import com.jihe.punchnet.sdlan.logs.TerminalLogger
+import org.bouncycastle.util.Strings
+import java.util.LinkedList
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.locks.ReentrantReadWriteLock
+
+data class RouteDetail(
+ // Int representation of 255.255.255.0
+ val mask: Int,
+ val gw: Int,
+ val maskedAddr: Int,
+)
+
+fun cidrToRouteDetail(cidr: String, gw: String): RouteDetail? {
+ val ipAndMaskDigit = Strings.split(cidr, '/')
+ if (ipAndMaskDigit.size != 2) {
+ TerminalLogger.debugf { "cidr format error: ${cidr}" }
+ return null
+ }
+ val ip = ipStringToInt(ipAndMaskDigit[0])
+ val mask = maskDigitToInt(ipAndMaskDigit[1].toInt())
+
+ if (ip == null || mask == null) {
+ TerminalLogger.debugf { "cidr format error2: ${cidr}" }
+ return null
+ }
+
+ val gateway = ipStringToInt(gw)
+ if (gateway == null) {
+ TerminalLogger.debugf { "gateway format error: ${gateway}" }
+ return null
+ }
+
+ if ((ip and mask) != ip) {
+ TerminalLogger.debugf { "net not masked" }
+ return null
+ }
+
+ return RouteDetail(
+ mask = mask,
+ gw = gateway,
+ maskedAddr = ip,
+ )
+}
+
+fun maskDigitToInt(digit: Int): Int? {
+ if (digit <= 0 || digit >= 32) {
+ return null
+ }
+ return ((2 shl (32-digit)) - 1)
+}
+
+fun ipStringToInt(ip: String): Int? {
+ val ipFiltered = ip.trim { it.isWhitespace() }
+ val ipSeg = Strings.split(ipFiltered, '.')
+ if (ipSeg.size != 4) {
+ return null
+ }
+ return ((ipSeg[0].toInt() and 0xff) shl 24) +
+ ((ipSeg[1].toInt() and 0xff) shl 16) +
+ ((ipSeg[2].toInt() and 0xff) shl 8) +
+ (ipSeg[3].toInt() and 0xff)
+
+}
+
+class RouteTable() {
+ val lock = ReentrantReadWriteLock()
+
+ val routeInfo: MutableList = mutableListOf()
+
+ fun addRoute(vararg routes: RouteDetail) {
+ try {
+ lock.writeLock().lock()
+ for (route in routes) {
+ routeInfo.add(route)
+ }
+ } finally {
+ lock.writeLock().unlock()
+ }
+ }
+
+ fun clearRoute() {
+ try {
+ lock.writeLock().lock()
+ routeInfo.clear()
+ } finally {
+ lock.writeLock().unlock()
+ }
+ }
+
+ fun getGeteway(ip: Int): Int? {
+ try {
+ lock.readLock().lock()
+ for (item in routeInfo) {
+ if ((ip and item.mask) == item.maskedAddr) {
+ return item.gw
+ }
+ }
+ return null
+ } finally {
+ lock.readLock().unlock()
+ }
+ }
+
+}
+
+class RouteTable2(
+ val initRouteInfo: List
+) {
+ val routeInfo: Array = initRouteInfo.toTypedArray()
+
+ fun getGeteway(ip: Int): Int? {
+ for (item in routeInfo) {
+ if ((ip and item.mask) == item.maskedAddr) {
+ return item.gw
+ }
+ }
+ return null
+ }
+}
\ No newline at end of file