changed scope.cancel, 当协程上下文被取消,会将资源做对应处理

This commit is contained in:
asxalex 2025-07-15 16:36:50 +08:00
parent fa2453a733
commit 867fb5ca72
5 changed files with 96 additions and 61 deletions

View File

@ -24,6 +24,7 @@ import com.jihe.punchnet.sdlan.network.DeviceConfig
import com.jihe.punchnet.sdlan.network.IfaceTun import com.jihe.punchnet.sdlan.network.IfaceTun
import com.jihe.punchnet.sdlan.network.RouteDetail import com.jihe.punchnet.sdlan.network.RouteDetail
import com.jihe.punchnet.sdlan.network.RouteTable import com.jihe.punchnet.sdlan.network.RouteTable
import com.jihe.punchnet.sdlan.network.StartStopChanInfo
import com.jihe.punchnet.sdlan.network.cidrToRouteDetail import com.jihe.punchnet.sdlan.network.cidrToRouteDetail
import com.jihe.punchnet.sdlan.network.ipInt2ByteArray import com.jihe.punchnet.sdlan.network.ipInt2ByteArray
import com.jihe.punchnet.sdlan.network.maskIPToDigit import com.jihe.punchnet.sdlan.network.maskIPToDigit
@ -33,6 +34,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.FileInputStream import java.io.FileInputStream
@ -50,6 +52,7 @@ class PunchnetService : VpnService() , IfaceTun {
private val serviceLock = Any() private val serviceLock = Any()
private val isRunning = AtomicBoolean(false) private val isRunning = AtomicBoolean(false)
private val start_stop_channel = Channel<StartStopChanInfo>(100)
var input: FileInputStream? = null var input: FileInputStream? = null
var output: FileOutputStream? = null var output: FileOutputStream? = null
@ -64,6 +67,10 @@ class PunchnetService : VpnService() , IfaceTun {
const val ACTION_DISCONNECT = "com.jihe.punchnet.punchnetservice.DISCONNECT" const val ACTION_DISCONNECT = "com.jihe.punchnet.punchnetservice.DISCONNECT"
} }
override fun close() {
vpnDescriptor?.close()
}
override suspend fun recv(): ByteArray { override suspend fun recv(): ByteArray {
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
val result = ByteArray(1500) val result = ByteArray(1500)
@ -135,18 +142,18 @@ class PunchnetService : VpnService() , IfaceTun {
} }
Toast.makeText(this, "stop vpn called", Toast.LENGTH_LONG).show() Toast.makeText(this, "stop vpn called", Toast.LENGTH_LONG).show()
stopForeground(STOP_FOREGROUND_REMOVE)
input?.close() input?.close()
output?.close() output?.close()
vpnDescriptor?.close() vpnDescriptor?.close()
vpnDescriptor = null vpnDescriptor = null
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf() stopSelf()
isRunning.set(false)
} }
} }
private fun connect(startArg: PunchnetServiceArgument?) { private fun connect(startArg: PunchnetServiceArgument?) {
synchronized(serviceLock) { synchronized(serviceLock) {
if (isRunning.get()) { if (isRunning.get()) {
return return
@ -183,13 +190,13 @@ class PunchnetService : VpnService() , IfaceTun {
name = "tau", name = "tau",
) )
scope.launch {
run_sdlan(scope, iface, argument, startArg)
}
val notification = createNotification() val notification = createNotification()
startForeground(1, notification) startForeground(1, notification)
scope.launch {
run_sdlan(scope, start_stop_channel, iface, argument, startArg)
}
isRunning.set(true) isRunning.set(true)
} }
} }
@ -199,12 +206,13 @@ class PunchnetService : VpnService() , IfaceTun {
return if (intent?.action == ACTION_DISCONNECT) { return if (intent?.action == ACTION_DISCONNECT) {
scope.cancel() scope.cancel()
disconnect()
START_STICKY
} else {
scope = CoroutineScope(Dispatchers.IO) scope = CoroutineScope(Dispatchers.IO)
arpTable = ARPTable(scope) arpTable = ARPTable(scope)
disconnect()
START_NOT_STICKY
} else {
val argument = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val argument = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent?.getParcelableExtra("argument", PunchnetServiceArgument::class.java) intent?.getParcelableExtra("argument", PunchnetServiceArgument::class.java)
} else { } else {
@ -238,7 +246,9 @@ class PunchnetService : VpnService() , IfaceTun {
// scope.cancel() // scope.cancel()
// disconnect() // disconnect()
scope.cancel() scope.cancel()
stopForeground(STOP_FOREGROUND_REMOVE) disconnect()
Log.e(TAG, "onDestroy is called")
// stopForeground(STOP_FOREGROUND_REMOVE)
super.onDestroy() super.onDestroy()
} }
} }

View File

@ -7,6 +7,7 @@ import com.jihe.punchnet.protobuf.PunchProto.SDLStunProbe
import com.jihe.punchnet.protobuf.PunchProto.SDLStunProbeReply import com.jihe.punchnet.protobuf.PunchProto.SDLStunProbeReply
import com.jihe.punchnet.protobuf.PunchProto.SDLStunRequest import com.jihe.punchnet.protobuf.PunchProto.SDLStunRequest
import com.jihe.punchnet.sdlan.config.SDLanMulticastConfig import com.jihe.punchnet.sdlan.config.SDLanMulticastConfig
import com.jihe.punchnet.sdlan.logs.TerminalLogger
import com.jihe.punchnet.sdlan.utils.AES import com.jihe.punchnet.sdlan.utils.AES
import com.jihe.punchnet.sdlan.utils.RSA import com.jihe.punchnet.sdlan.utils.RSA
import com.jihe.punchnet.sdlan.utils.UniqueNodeID import com.jihe.punchnet.sdlan.utils.UniqueNodeID
@ -312,8 +313,12 @@ class Node private constructor (
} }
suspend fun _sendDataToSocket(msg: ByteArray) { suspend fun _sendDataToSocket(msg: ByteArray) {
if (aes.isAuthorized()) { if (aes.isAuthorized()) {
TerminalLogger.debugf{"authorized, ping to sn"}
toSocket.send(msg) toSocket.send(msg)
} else {
TerminalLogger.debugf{"unauthorized, not ping to sn"}
} }
} }
} }

View File

@ -30,6 +30,7 @@ interface Iface {
suspend fun send(content: ByteArray) suspend fun send(content: ByteArray)
suspend fun reload_config(config: DeviceConfig) suspend fun reload_config(config: DeviceConfig)
fun close() {}
suspend fun handleDataFromNet(node: Node, data: ByteArray) suspend fun handleDataFromNet(node: Node, data: ByteArray)
suspend fun handleDataFromDevice(node: Node, data: ByteArray) suspend fun handleDataFromDevice(node: Node, data: ByteArray)
} }
@ -227,6 +228,9 @@ class IfaceMock: IfaceTun {
var config: DeviceConfig = DeviceConfig(0) var config: DeviceConfig = DeviceConfig(0)
override fun close() {
}
override suspend fun recv(): ByteArray { override suspend fun recv(): ByteArray {
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
val size = input.readInt() val size = input.readInt()

View File

@ -26,6 +26,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -47,7 +48,6 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
TerminalLogger.debugf {"message received"} TerminalLogger.debugf {"message received"}
when(data.packetType) { when(data.packetType) {
PacketType.RegisterSuperACK -> { PacketType.RegisterSuperACK -> {
println("11")
val ack = SDLRegisterSuperAck.parseFrom(data.currentPacket) val ack = SDLRegisterSuperAck.parseFrom(data.currentPacket)
// TerminalLogger.debugf { "got register super ack: $ack" } // TerminalLogger.debugf { "got register super ack: $ack" }
println("ack is ${ack}") println("ack is ${ack}")
@ -65,7 +65,9 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
node.deviceConfig.ip.netAddr = ack.devAddr.netAddr node.deviceConfig.ip.netAddr = ack.devAddr.netAddr
node.deviceConfig.ip.netBitLen = ack.devAddr.netBitLen.toByte() node.deviceConfig.ip.netBitLen = ack.devAddr.netBitLen.toByte()
println("reloading config: node.iface = ${node.iface}")
node.iface?.reload_config(node.deviceConfig) node.iface?.reload_config(node.deviceConfig)
println("reloading config ok: node.iface = ${node.iface}")
node.networkID.set(ack.devAddr.networkId) node.networkID.set(ack.devAddr.networkId)
// println("got aes key: ${aeskey.toList()}, length is ${aeskey.size}") // println("got aes key: ${aeskey.toList()}, length is ${aeskey.size}")
@ -83,7 +85,6 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
*/ */
} }
PacketType.RegisterSuperNAK -> { PacketType.RegisterSuperNAK -> {
println("21")
val nak = SDLRegisterSuperNak.parseFrom(data.currentPacket) val nak = SDLRegisterSuperNak.parseFrom(data.currentPacket)
TerminalLogger.debugf { "got register super nak: $nak" } TerminalLogger.debugf { "got register super nak: $nak" }
val nakcode = NakMsgCode.fromByte(nak.errorCode.toByte()) val nakcode = NakMsgCode.fromByte(nak.errorCode.toByte())
@ -133,7 +134,7 @@ suspend fun onMessage(scope: CoroutineScope, data: SDLanTCP) {
} }
suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments, routeinfo: PunchnetServiceArgument?) { suspend fun run_sdlan(scope: CoroutineScope, start_stop_channel: Channel<StartStopChanInfo>, iface: Iface, argument: Arguments, routeinfo: PunchnetServiceArgument?) {
UniqueNodeID.setBaseDir(argument.baseDir) UniqueNodeID.setBaseDir(argument.baseDir)
val edgeUUID = UniqueNodeID.getUUID() val edgeUUID = UniqueNodeID.getUUID()
val config = parseConfig(edgeUUID, argument) val config = parseConfig(edgeUUID, argument)
@ -146,7 +147,7 @@ suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments,
val toSocket = Channel<ByteArray>(100) val toSocket = Channel<ByteArray>(100)
val start_stop_channel = Channel<StartStopChanInfo>(100) // val start_stop_channel = Channel<StartStopChanInfo>(100)
initEdge(scope, iface, argument.token, config, toSocket, start_stop_channel) initEdge(scope, iface, argument.token, config, toSocket, start_stop_channel)
val tcp = argument.tcp.split(":") val tcp = argument.tcp.split(":")
@ -158,10 +159,10 @@ suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments,
val before = ubyteArrayOf(126u,162u,25u,63u,148u,147u,198u,41u,69u,165u,149u,101u,153u,82u,190u,21u,48u,120u,26u,64u,142u,103u,159u,60u,47u,129u,176u,17u,232u,210u,36u,56u) val before = ubyteArrayOf(126u,162u,25u,63u,148u,147u,198u,41u,69u,165u,149u,101u,153u,82u,190u,21u,48u,120u,26u,64u,142u,103u,159u,60u,47u,129u,176u,17u,232u,210u,36u,56u)
val encrypted = node.rsa.encrypt(before.toByteArray(), use_private_key = false) val encrypted = node.rsa.encrypt(before.toByteArray(), use_private_key = false)
println("encrypted: ${encrypted.contentToString()}") // println("encrypted: ${encrypted.contentToString()}")
val origin = node.rsa.decrypt(encrypted, use_pub_key = false) val origin = node.rsa.decrypt(encrypted, use_pub_key = false)
TerminalLogger.debugf { "got encrypted ${encrypted.size}"} // TerminalLogger.debugf { "got encrypted ${encrypted.size}"}
TerminalLogger.debugf { "got origin ${origin.contentToString()}"} // TerminalLogger.debugf { "got origin ${origin.contentToString()}"}
TerminalLogger.debugf { "self mac: ${macToString(node.mac)}"} TerminalLogger.debugf { "self mac: ${macToString(node.mac)}"}
@ -199,24 +200,34 @@ suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments,
*/ */
scope.launch { scope.launch {
while(true) { try {
val data = node.iface?.recv() while(true) {
Log.d("SDLAN", "async receive data from iface: ${data?.size} bytes") if (node.iface == null) {
if (data == null) { delay(1000)
//delay(1000) continue
println("got data is null") }
continue val data = node.iface?.recv()
Log.d("SDLAN", "async receive data from iface: ${data?.size} bytes")
if (data == null) {
//delay(1000)
delay(1000)
// println("got data is null")
continue
}
if (data.isEmpty()) {
delay(1000)
// println("got data size 0")
// delay(1000)
continue
}
Log.d("SDLAN", "handle data form device starts")
node.iface?.handleDataFromDevice(node, data)
Log.d("SDLAN", "handle data form device stops")
} }
if (data.isEmpty()) { } catch (e: Exception) {
println("got data size 0") println("iface read is cancelled")
// delay(1000) node.iface?.close()
continue
}
Log.d("SDLAN", "handle data form device starts")
node.iface?.handleDataFromDevice(node, data)
Log.d("SDLAN", "handle data form device stops")
} }
} }
/* /*
CoroutineScope((Dispatchers.IO)).async { CoroutineScope((Dispatchers.IO)).async {
@ -242,20 +253,24 @@ suspend fun run_sdlan(scope: CoroutineScope, iface: Iface, argument: Arguments,
// CoroutineScope(Dispatchers.IO).async { // CoroutineScope(Dispatchers.IO).async {
scope.launch { scope.launch {
initTCPConn( try {
scope, initTCPConn(
tcp[0], tcp[1].toInt(), scope,
start_stop_channel, tcp[0], tcp[1].toInt(),
AtomicLong(now), start_stop_channel,
AtomicBoolean(false), AtomicLong(now),
toSocket as ReceiveChannel<ByteArray>, AtomicBoolean(false),
onConnected, toSocket as ReceiveChannel<ByteArray>,
::onMessage, onConnected,
suspend { ::onMessage,
node.aes.setSecret(null) suspend {
}, node.aes.setSecret(null)
null, },
) null,
)
} finally {
println("initTCPConn is cancelled")
}
} }
@ -354,6 +369,7 @@ suspend fun handleAPacket(scope: CoroutineScope, node: Node, from: SocketAddress
return return
} }
PacketType.Register -> { PacketType.Register -> {
TerminalLogger.debugf { "got REGISTER" }
if (from is InetSocketAddress) { if (from is InetSocketAddress) {
val sock = SDLanSock(IPFamily.IPV4, from.port, from.address.address) val sock = SDLanSock(IPFamily.IPV4, from.port, from.address.address)
handlePacketRegister(node, data, sock) handlePacketRegister(node, data, sock)
@ -419,17 +435,20 @@ suspend fun initTCPConn(
onDisconnected: suspend ()->Unit, onDisconnected: suspend ()->Unit,
connectingChan: SendChannel<ConnectingState>? connectingChan: SendChannel<ConnectingState>?
) { ) {
var started: Boolean = false val started = AtomicBoolean(false)
// var started: Boolean = false
var startPktID: Int? = null var startPktID: Int? = null
while(true) { while(true) {
connectingChan?.send(ConnectingState.NotConnected) connectingChan?.send(ConnectingState.NotConnected)
if (!started) { if (!started.get()) {
while(true) { while(true) {
println("waiting for start_stop")
val startStopInfo = start_stop.receive() val startStopInfo = start_stop.receive()
if (startStopInfo.flag == StartStopFlag.IsStart) { if (startStopInfo.flag == StartStopFlag.IsStart) {
started = true started.set(true)
// started = true
startPktID = startStopInfo.packetID startPktID = startStopInfo.packetID
break break
} }
@ -518,11 +537,11 @@ suspend fun initTCPConn(
try { try {
val v = start_stop.receive() val v = start_stop.receive()
if (v.flag == StartStopFlag.IsStop) { if (v.flag == StartStopFlag.IsStop) {
started = false started.set(false)
break break
} }
} catch(e: Exception) { } catch(e: Exception) {
started = false started.set(false)
break break
} }
@ -549,10 +568,15 @@ suspend fun initTCPConn(
} }
} }
println("m1")
job_read_packet.cancelAndJoin() job_read_packet.cancelAndJoin()
println("m2")
job_write_to_packet.cancelAndJoin() job_write_to_packet.cancelAndJoin()
println("m3")
job_check_pong.cancelAndJoin() job_check_pong.cancelAndJoin()
println("m4")
job_check_stop.cancelAndJoin() job_check_stop.cancelAndJoin()
println("m5")
delay(1000) delay(1000)
} }

View File

@ -43,21 +43,13 @@ class AES private constructor () {
if (!isAuthorized()) { if (!isAuthorized()) {
return null return null
} }
println(11.0)
val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM) val cipher = Cipher.getInstance(AESConfig.CIPHER_ALGORITHM)
println(11.1)
val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC) val keyspec = SecretKeySpec(_secret, AESConfig.KEY_SPEC)
println(11.2)
println(11.3)
try { try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, IvParameterSpec(_iv)) cipher.init(Cipher.ENCRYPT_MODE, keyspec, IvParameterSpec(_iv))
println(10.0)
val output = ByteBuffer.allocate(cipher.getOutputSize(content.remaining())) val output = ByteBuffer.allocate(cipher.getOutputSize(content.remaining()))
println(10.1)
cipher.doFinal(content, output) cipher.doFinal(content, output)
println(10.2)
output.flip() output.flip()
println(10.3)
return output return output
} catch (e: Exception) { } catch (e: Exception) {
println("failed to encrypt: ${e.toString()}") println("failed to encrypt: ${e.toString()}")