From 53d5239de8fa1aabf56ab7e713fe0f227983c9b7 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 16 May 2023 23:09:58 +0800 Subject: [PATCH 01/39] bak --- admin-web/package-lock.json | 4 ++-- .../scala/com/timzaak/fornet/dao/Network.scala | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/admin-web/package-lock.json b/admin-web/package-lock.json index 9a3cb11..7ef2f6f 100644 --- a/admin-web/package-lock.json +++ b/admin-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "admin-web", - "version": "0.0.1", + "version": "0.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "admin-web", - "version": "0.0.1", + "version": "0.0.3", "dependencies": { "@reduxjs/toolkit": "^1.8.6", "@testing-library/jest-dom": "^5.16.5", diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala index c9427ee..5f8852e 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala @@ -21,6 +21,21 @@ object NetworkStatus { } } } + +enum NetworkProtocol { + case TCP, UDP +} +object NetworkProtocol { + given JsonEncoder[NetworkProtocol] = JsonEncoder[Int].contramap(_.ordinal) + + given JsonDecoder[NetworkProtocol] = JsonDecoder[Int].mapOrFail { e => + Try(NetworkProtocol.fromOrdinal(e)) match { + case Success(v) => Right(v) + case Failure(_) => Left("no matching NetworkProtocol enum value") + } + } +} + case class Network( id: Int, name: String, @@ -37,6 +52,7 @@ case class NetworkSetting( port: Int = 51820, keepAlive: Int = 30, mtu: Int = 1420, + protocol:NetworkProtocol = NetworkProtocol.UDP, dns: Option[Seq[String]] = None, ) extends DBSerializer From 6b272bb200df6a9d13c03f4c7eb630e8403b6fde Mon Sep 17 00:00:00 2001 From: timzaak Date: Wed, 17 May 2023 23:12:55 +0800 Subject: [PATCH 02/39] bak it --- .../src/main/scala/com/timzaak/fornet/dao/Network.scala | 9 +++++++++ .../com/timzaak/fornet/grpc/convert/EntityConvert.scala | 1 + protobuf/config.proto | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala index 5f8852e..5d03281 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala @@ -2,6 +2,7 @@ package com.timzaak.fornet.dao // import io.getquill.{UpdateMeta, updateMeta} +import com.timzaak.fornet.dao.NetworkProtocol.TCP import very.util.persistence.quill.DBSerializer import zio.json.* @@ -24,6 +25,14 @@ object NetworkStatus { enum NetworkProtocol { case TCP, UDP + + import com.timzaak.fornet.protobuf.config.Protocol as PProtocol + def gRPCProtocol:PProtocol = { + this match { + case TCP => PProtocol.Protocol_TCP + case UDP => PProtocol.Protocol_UDP + } + } } object NetworkProtocol { given JsonEncoder[NetworkProtocol] = JsonEncoder[Int].contramap(_.ordinal) diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala index 01ab305..2b02151 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala @@ -43,6 +43,7 @@ object EntityConvert { mtu = Some(setting.mtu.getOrElse(nSetting.mtu)), postUp = setting.postUp, postDown = setting.postDown, + protocol = nSetting.protocol.gRPCProtocol ), ), peers = toPeers(relativeNodes.filter(_.id != node.id), network) diff --git a/protobuf/config.proto b/protobuf/config.proto index 5d26b43..68b2ef1 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -6,6 +6,11 @@ import "google/protobuf/empty.proto"; option java_package = "com.timzaak.fornet.protobuf"; +enum Protocol { + Protocol_TCP = 0; + Protocol_UDP = 1; +} + message Interface { optional string name = 1; repeated string address = 2; @@ -17,6 +22,7 @@ message Interface { optional string post_up = 8; optional string pre_down = 9; optional string post_down = 10; + Protocol protocol = 11; } message Peer { From b36e6e05d5425ed3055780e6c75b4e2412312ca1 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Wed, 17 May 2023 23:30:13 +0800 Subject: [PATCH 03/39] bak --- backend/build.sbt | 3 +++ backend/src/main/resources/application.conf | 10 +++++----- .../timzaak/fornet/controller/NetworkController.scala | 7 ++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/backend/build.sbt b/backend/build.sbt index 916fbf6..ab13789 100644 --- a/backend/build.sbt +++ b/backend/build.sbt @@ -13,6 +13,9 @@ Compile / PB.targets := Seq( ) Compile / PB.protoSources += file("../protobuf") +// zio-json default value needs this +ThisBuild / scalacOptions ++= Seq("-Yretain-trees") + import Dependencies._ lazy val app = project diff --git a/backend/src/main/resources/application.conf b/backend/src/main/resources/application.conf index 6b77c7b..d1ba05a 100644 --- a/backend/src/main/resources/application.conf +++ b/backend/src/main/resources/application.conf @@ -36,10 +36,10 @@ auth { # frontClientId : "fornet", # role: "admin", #} - #simple { - # token: "adminToken" - # userId: "admin" - #} + simple { + token: "adminToken" + userId: "admin" + } } - +# you can set your private config in private.conf file include "private.conf" \ No newline at end of file diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala index ce97d14..7fc1f36 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala @@ -2,7 +2,7 @@ package com.timzaak.fornet.controller import com.google.common.net.InetAddresses import com.timzaak.fornet.controller.auth.AppAuthSupport -import com.timzaak.fornet.dao.{DB, Network, NetworkDao, NetworkSetting} +import com.timzaak.fornet.dao.{DB, Network, NetworkDao, NetworkProtocol, NetworkSetting} import com.typesafe.config.Config import org.hashids.Hashids @@ -19,7 +19,7 @@ import zio.json.{DeriveJsonDecoder, JsonDecoder} import java.time.OffsetDateTime -case class CreateNetworkReq(name: String, addressRange: String) +case class CreateNetworkReq(name: String, addressRange: String, protocol:NetworkProtocol) given JsonDecoder[CreateNetworkReq] = DeriveJsonDecoder.gen case class UpdateNetworkReq( name: String, @@ -66,7 +66,7 @@ trait NetworkController( .insert( _.name -> lift(req.name), _.addressRange -> lift(req.addressRange), - _.setting -> lift(NetworkSetting()), + _.setting -> lift(NetworkSetting(protocol = req.protocol)), ) .returning(_.id) } @@ -114,6 +114,7 @@ trait NetworkController( ) } } + //TODO: notify all active nodes in network that config change. Accepted() } } From 93f7878e659d271d3c662ed056be91c69bd0fa83 Mon Sep 17 00:00:00 2001 From: timzaak Date: Thu, 18 May 2023 23:41:18 +0800 Subject: [PATCH 04/39] bak --- backend/README.md | 3 +- .../fornet/controller/NetworkController.scala | 38 +++++++++++-------- .../fornet/controller/NodeController.scala | 19 ++++++---- .../scala/com/timzaak/fornet/dao/Node.scala | 13 +++++++ .../main/scala/com/timzaak/fornet/di/DI.scala | 5 ++- .../fornet/grpc/AuthGRPCController.scala | 4 +- .../fornet/mqtt/MqttCallbackController.scala | 26 +++++++------ .../pubsub/NodeChangeNotifyService.scala | 27 ++++++++++--- .../fornet/service/NodeAuthService.scala | 36 ++---------------- .../timzaak/fornet/service/NodeService.scala | 4 ++ protobuf/config.proto | 4 ++ 11 files changed, 101 insertions(+), 78 deletions(-) diff --git a/backend/README.md b/backend/README.md index 7274703..7923252 100644 --- a/backend/README.md +++ b/backend/README.md @@ -2,4 +2,5 @@ If Scala 3 is a good choice? ### TODO -- [ ] create IpArrange util to handle all, should be careful use. \ No newline at end of file +- [ ] create IpArrange util to handle all, should be careful use. +- [ ] needs async runtime(thread pool) to handle push. \ No newline at end of file diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala index 7fc1f36..08d0ba1 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala @@ -3,6 +3,7 @@ package com.timzaak.fornet.controller import com.google.common.net.InetAddresses import com.timzaak.fornet.controller.auth.AppAuthSupport import com.timzaak.fornet.dao.{DB, Network, NetworkDao, NetworkProtocol, NetworkSetting} +import com.timzaak.fornet.pubsub.NodeChangeNotifyService import com.typesafe.config.Config import org.hashids.Hashids @@ -29,6 +30,7 @@ case class UpdateNetworkReq( given JsonDecoder[UpdateNetworkReq] = DeriveJsonDecoder.gen trait NetworkController( networkDao: NetworkDao, + nodeChangeNotifyService: NodeChangeNotifyService, )(using quill: DB, config: Config, hashId: Hashids) extends Controller with AppAuthSupport { @@ -102,20 +104,28 @@ trait NetworkController( for { _ <- ipV4Range(data.addressRange) } yield { - quill.run { - quote { - query[Network] - .filter(_.id == lift(id)) - .update( - _.name -> lift(data.name), - _.addressRange -> lift(data.addressRange), - _.setting -> lift(data.setting), - _.updatedAt -> lift(OffsetDateTime.now()), - ) - } + networkDao.findById(id) match { + case Some(oldNetwork) => + quill.run { + quote { + query[Network] + .filter(_.id == lift(id)) + .update( + _.name -> lift(data.name), + _.addressRange -> lift(data.addressRange), + _.setting -> lift(data.setting), + _.updatedAt -> lift(OffsetDateTime.now()), + ) + } + } + nodeChangeNotifyService.networkSettingChange(oldNetwork, data.setting) + case _ => } - //TODO: notify all active nodes in network that config change. Accepted() + + + + } } @@ -134,9 +144,7 @@ trait NetworkController( } } if (changeCount > 0) { - // TODO: - // kickoff all nodes in the network - // change all node status to Deleted + nodeChangeNotifyService.networkDeleteNotify(networkId) } Accepted() } diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala index 5428259..9a11b79 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala @@ -96,9 +96,9 @@ trait NodeController( } } - if (oldNode.setting != req.setting && oldNode.status == NodeStatus.Normal) { - // notify self change - nodeChangeNotifyService.nodeInfoChangeNotify(oldNode, req.setting) + val network = networkDao.findById(oldNode.networkId).get + if (oldNode.setting != req.setting && oldNode.realStatus(network.status) == NodeStatus.Normal) { + nodeChangeNotifyService.nodeInfoChangeNotify(oldNode, req.setting, network) } Accepted() } @@ -124,11 +124,14 @@ trait NodeController( } } if (changeNumber > 0) { - nodeChangeNotifyService.nodeStatusChangeNotify( - oldNode, - oldNode.status, - req.status - ) + val network = networkDao.findById(networkId).get + if(network.status == NetworkStatus.Normal) { + nodeChangeNotifyService.nodeStatusChangeNotify( + oldNode, + oldNode.status, + req.status + ) + } } Accepted() } diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala index 8a9c7e8..45d8e3b 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala @@ -75,6 +75,14 @@ case class Node( case NodeType.Client => s"$ip/${network.addressRange.split('/').last}" } } + + def realStatus(networkStatus:NetworkStatus):NodeStatus = { + if(networkStatus == NetworkStatus.Delete) { + NodeStatus.Delete + } else { + status + } + } def peerAddress: String = { nodeType match { @@ -159,6 +167,11 @@ class NodeDao(using quill: DB) { } } + def getAllAvailableNodes(networkId:Int):Seq[Node] = quill.run { + quote { + query[Node].filter(n => n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal)) + } + } def getAllAvailableNodes( networkId: Int, exceptNodeId: Int, diff --git a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala index d2a98e0..7617969 100644 --- a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala +++ b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala @@ -66,7 +66,10 @@ object DI extends DaoDI { di => } ) // web controller - object networkController extends NetworkController(networkDao = di.networkDao) + object networkController extends NetworkController( + networkDao = di.networkDao, + nodeChangeNotifyService = di.nodeChangeNotifyService, + ) object nodeController extends NodeController( nodeDao = di.nodeDao, diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala index 44bdeec..332b018 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala @@ -50,7 +50,7 @@ class AuthGRPCController( if (request.nodeId.nonEmpty) { params = params.appended(request.nodeId.get) } - if (request.encrypt.exists(v => nodeAuthService.validate2(v, params))) { + if (request.encrypt.exists(v => nodeAuthService.validate(v, params))) { val networkId = hashId.decode(request.networkId).head.toInt val publicKey = request.encrypt.get.publicKey @@ -108,7 +108,7 @@ class AuthGRPCController( request: OAuthDeviceCodeRequest ): Future[ActionResponse] = { val params = Seq(request.accessToken, request.deviceCode, request.networkId) - if (request.encrypt.exists(v => nodeAuthService.validate2(v, params))) { + if (request.encrypt.exists(v => nodeAuthService.validate(v, params))) { if (config.hasPath("auth.keycloak")) { val authResult = authStrategyProvider diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index c92ee2f..0abd264 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -1,6 +1,6 @@ package com.timzaak.fornet.mqtt -import com.timzaak.fornet.dao.{DB, NetworkDao, NodeDao, NodeStatus} +import com.timzaak.fornet.dao.{DB, NetworkDao, NodeDao, NodeStatus, Network} import com.timzaak.fornet.entity.PublicKey import com.timzaak.fornet.grpc.convert.EntityConvert import com.timzaak.fornet.mqtt.api.RMqttApiClient @@ -85,19 +85,21 @@ class MqttCallbackController( .toMap } nodes.foreach { node => - val notifyNodes = nodeService.getAllRelativeNodes(node) - val network = networks(node.networkId) - mqttConnectionManager.sendMessage( - networkId = node.networkId, - node.id, - clientId, - ClientMessage( - networkId = hashId.encode(node.networkId), - ClientMessage.Info.Config( - EntityConvert.nodeToWRConfig(node, network, notifyNodes) + val network:Network = networks(node.networkId) + if(node.realStatus(network.status) == NodeStatus.Normal) { + val notifyNodes = nodeService.getAllRelativeNodes(node) + mqttConnectionManager.sendMessage( + networkId = node.networkId, + node.id, + clientId, + ClientMessage( + networkId = hashId.encode(node.networkId), + ClientMessage.Info.Config( + EntityConvert.nodeToWRConfig(node, network, notifyNodes) + ) ) ) - ) + } } } } match { diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index 646cc72..7c3ca5c 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -2,7 +2,7 @@ package com.timzaak.fornet.pubsub import com.timzaak.fornet.dao.* import com.timzaak.fornet.grpc.convert.EntityConvert -import com.timzaak.fornet.protobuf.config.{NodeStatus as PNodeStatus, *} +import com.timzaak.fornet.protobuf.config.{NodeStatus as PNodeStatus, NetworkStatus as PNetworkStatus, *} import com.timzaak.fornet.service.NodeService import org.hashids.Hashids @@ -16,10 +16,7 @@ class NodeChangeNotifyService( import quill.{*, given} - def nodeInfoChangeNotify(oldNode: Node, setting: NodeSetting) = { - // TODO: FIXIT - - val network = networkDao.findById(oldNode.networkId).get + def nodeInfoChangeNotify(oldNode: Node, setting: NodeSetting, network:Network) = { val networkId = hashid.encode(network.id) val relativeNodes = nodeService.getAllRelativeNodes(oldNode) @@ -54,6 +51,25 @@ class NodeChangeNotifyService( } } + def networkSettingChange(oldNetwork:Network, newSetting:NetworkSetting): Unit = { + //only care about protocol, others will trigger push in future version.(after solved async push) + if(oldNetwork.setting.protocol != newSetting.protocol && oldNetwork.status == NetworkStatus.Normal) { + val nodes = nodeDao.getAllAvailableNodes(oldNetwork.id) + //TODO: + } + } + + //PS: Network would never recover from delete status + def networkDeleteNotify(networkId:Int): Unit = { + connectionManager.sendMessage( + networkId, + NetworkMessage( + networkId = hashid.encode(networkId), + NetworkMessage.Info.Status(PNetworkStatus.NETWORK_DELETE) + ) + ) + } + def nodeStatusChangeNotify( node: Node, oldStatus: NodeStatus, @@ -117,6 +133,5 @@ class NodeChangeNotifyService( case _ => // do nothing. } - } } diff --git a/backend/src/main/scala/com/timzaak/fornet/service/NodeAuthService.scala b/backend/src/main/scala/com/timzaak/fornet/service/NodeAuthService.scala index 028b06c..212f157 100644 --- a/backend/src/main/scala/com/timzaak/fornet/service/NodeAuthService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/service/NodeAuthService.scala @@ -22,40 +22,9 @@ object GRPCAuthRequest { case class GRPCAuth(publicKey: PublicKey, networkId: Int) +//TODO: This should not be service, change it to object class NodeAuthService(using hashId: Hashids) { - - // import quill.{ given, * } - - /* def validate(grpcAuth: GRPCAuth): Either[Status, NodeIdentity] = { - if (NodeAuthService.validate(grpcAuth)) { - nodeDao - .findIdByPublicKey(grpcAuth.publicKey.key, grpcAuth.networkId) - .map(NodeIdentity(grpcAuth.networkId, _)) - .toRight( - Status.NOT_FOUND.withDescription("Could not find Node") - ) - } else { - Left(Status.INVALID_ARGUMENT.withDescription("Invalid auth")) - } - } */ - - @deprecated - def validate(grpcAuth: GRPCAuthRequest): Option[Int] = { - import grpcAuth.* - val plainText = s"$timestamp-$networkId-$nonce" - if (publicKey.validate(plainText, sign)) { - val hashIds = hashId.decode(networkId) - if (hashIds.size == 1) { - Some(hashIds.head.toInt) - } else { - None - } - } else { - None - } - } - - def validate2( + def validate( encrypt: EncryptRequest, params: Seq[String], ): Boolean = { @@ -67,3 +36,4 @@ class NodeAuthService(using hashId: Hashids) { }.getOrElse(false) } } + diff --git a/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala b/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala index 5c88b93..d7c45ef 100644 --- a/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala @@ -28,5 +28,9 @@ class NodeService(nodeDao: NodeDao)(using quill: DB, hashId: Hashids) { }) } + def getAllRelativeNodes(node:Node, nodes:List[Node])= { + + //TODO + } } diff --git a/protobuf/config.proto b/protobuf/config.proto index 68b2ef1..9c99e55 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -49,6 +49,9 @@ enum NodeStatus { NODE_NORMAL = 1; NODE_FORBID = 2; } +enum NetworkStatus { + NETWORK_DELETE = 0; +} message ClientMessage { string network_id = 1; @@ -61,5 +64,6 @@ message NetworkMessage { string network_id = 1; oneof info { PeerChange peer = 2; + NetworkStatus status = 3; } } From 9b51fb414575184fa00f7be1224a968315d0c0dd Mon Sep 17 00:00:00 2001 From: timzaak Date: Fri, 19 May 2023 19:05:57 +0800 Subject: [PATCH 05/39] bak --- .../fornet/controller/NetworkController.scala | 2 +- .../pubsub/NodeChangeNotifyService.scala | 21 +++++++++++++++---- .../timzaak/fornet/service/NodeService.scala | 17 ++++++++++++--- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala index 08d0ba1..414f538 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala @@ -118,7 +118,7 @@ trait NetworkController( ) } } - nodeChangeNotifyService.networkSettingChange(oldNetwork, data.setting) + nodeChangeNotifyService.networkSettingChange(oldNetwork, networkDao.findById(id).get) case _ => } Accepted() diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index 7c3ca5c..c3c4328 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -51,11 +51,24 @@ class NodeChangeNotifyService( } } - def networkSettingChange(oldNetwork:Network, newSetting:NetworkSetting): Unit = { + // network must be in normal status + def networkSettingChange(oldNetwork:Network, newNetwork:Network): Unit = { //only care about protocol, others will trigger push in future version.(after solved async push) - if(oldNetwork.setting.protocol != newSetting.protocol && oldNetwork.status == NetworkStatus.Normal) { - val nodes = nodeDao.getAllAvailableNodes(oldNetwork.id) - //TODO: + if(oldNetwork.setting.protocol != newNetwork.setting.protocol && newNetwork.status == NetworkStatus.Normal) { + val nodes = nodeDao.getAllAvailableNodes(oldNetwork.id).toList + for ((node, relativeNodes) <-nodeService.getNetworkAllRelativeNodes(nodes)) { + val wrConfig = EntityConvert.nodeToWRConfig(node, newNetwork, relativeNodes) + // this would trigger all nodes restart. + connectionManager.sendMessage( + node.networkId, + node.id, + node.publicKey, + ClientMessage(networkId = hashid.encode(newNetwork.id), ClientMessage.Info.Config(wrConfig)) + ) + + } + + } } diff --git a/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala b/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala index d7c45ef..2f1f2eb 100644 --- a/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/service/NodeService.scala @@ -28,9 +28,20 @@ class NodeService(nodeDao: NodeDao)(using quill: DB, hashId: Hashids) { }) } - def getAllRelativeNodes(node:Node, nodes:List[Node])= { - - //TODO + def getNetworkAllRelativeNodes(nodes:List[Node]):List[(Node,List[Node])] = { + val relayNodes = nodes.filter(_.nodeType == NodeType.Relay) + val clientNodes = nodes.filter(_.nodeType == NodeType.Client) + + nodes.map{ node => + val nodeIp = IPAddressString(node.ip) + node -> ((relayNodes.filter(rNode => IPAddressString(rNode.ip).prefixContains(nodeIp)) ++ ( + node.nodeType match { + case NodeType.Relay => + clientNodes.filter(cNode => nodeIp.prefixContains(IPAddressString(cNode.ip))) + case NodeType.Client => + List.empty + })).filter(_.id != node.id)) + } } } From efb1f0ec9e3625b303a828ea44b861216cf2a8f7 Mon Sep 17 00:00:00 2001 From: timzaak Date: Sun, 21 May 2023 13:44:24 +0800 Subject: [PATCH 06/39] bak it --- client/README.md | 2 +- client/lib/src/device/peer.rs | 5 ++++- client/lib/src/device/unix_device.rs | 1 + client/lib/src/sc_manager.rs | 9 ++++++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/README.md b/client/README.md index 7e91178..8926e53 100644 --- a/client/README.md +++ b/client/README.md @@ -1,5 +1,5 @@ -some code in device is from `BoringTun`. I reimplement it for Tokio. +some code in device is from `BoringTun`. I refactor it with Tokio. diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index 4e073fc..fc14670 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use std::sync::Arc; use boringtun::noise::{Tunn, TunnResult}; -use tokio::net::UdpSocket; +use tokio::net::{TcpSocket, UdpSocket}; use crate::device::allowed_ips::AllowedIps; @@ -18,6 +18,7 @@ use crate::device::allowed_ips::AllowedIps; pub struct Endpoint { pub addr: Option, pub conn: Option>, + pub tcp_conn: Option> } pub struct Peer { @@ -74,6 +75,7 @@ impl Peer { endpoint: Endpoint { addr: endpoint, conn: None, + tcp_conn:None, }, allowed_ips: allowed_ips.iter().map(|ip| (ip, ())).collect(), preshared_key, @@ -105,6 +107,7 @@ impl Peer { self.endpoint = Endpoint { addr: Some(addr), conn: None, + tcp_conn: None, } }; } diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 4500ded..9f962b3 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -37,6 +37,7 @@ impl Device { let (mut iface_reader, iface_writer,pi, name) = create_async_tun(name, mtu, address)?; tracing::debug!("finish to create tun"); let iface_writer = Arc::new(Mutex::new(iface_writer)); + // create tcp/udp server let udp4 = create_udp_socket(port, Domain::IPV4, None)?; let port = udp4.local_addr()?.port(); let udp6 = create_udp_socket(Some(port), Domain::IPV6, None)?; diff --git a/client/lib/src/sc_manager.rs b/client/lib/src/sc_manager.rs index 50219f7..1ca29b2 100644 --- a/client/lib/src/sc_manager.rs +++ b/client/lib/src/sc_manager.rs @@ -11,7 +11,7 @@ use tonic::metadata::{Ascii, MetadataValue}; use tonic::Request; use tonic::transport::Channel; -use crate::protobuf::config::{ClientMessage, NetworkMessage, NodeStatus, PeerChange, WrConfig}; +use crate::protobuf::config::{ClientMessage, NetworkMessage, NetworkStatus, NodeStatus, PeerChange, WrConfig}; use crate::protobuf::config::client_message::Info::{Config, Status}; use crate::protobuf::config::network_message::Info::Peer; use crate::server_manager::ServerMessage; @@ -116,6 +116,13 @@ impl SCManager { Peer(peer_change) => { let _ = self.sender.send(ServerMessage::SyncPeers(peer_change)).await; } + Status(status) => { + if let Some(NetworkStatus::NetworkDelete) = NetworkStatus.from_i32(status) { + let _ = self.sender.send( + ServerMessage::StopWR("network has been delete".to_owned()) + ).await; + } + } } } } else { From 0a2afef70362c992387ee48bb24355248e704706 Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 22 May 2023 13:36:29 +0800 Subject: [PATCH 07/39] bak it --- client/lib/src/device/mod.rs | 4 +++- client/lib/src/device/peer.rs | 21 +++++++++++---------- client/lib/src/device/windows_device.rs | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 5c2c4b7..7b048f0 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -249,6 +249,7 @@ pub async fn tun_read_handle(peers: &Arc>, udp4: &UdpSocket, udp6: } else { tracing::error!("No endpoint"); } + //TODO: get tcp socket from peers and send } _ => panic!("Unexpected result from encapsulate"), }; @@ -285,6 +286,7 @@ pub async fn peers_timer(peers: &Arc>, udp4: &UdpSocket, udp6: &Ud } TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), TunnResult::WriteToNetwork(packet) => { + let _ = match endpoint_addr { SocketAddr::V4(_) => udp4.send_to(packet, endpoint_addr).await, SocketAddr::V6(_) => udp6.send_to(packet, endpoint_addr).await, @@ -407,7 +409,7 @@ pub async fn udp_handler(udp: &UdpSocket, while let TunnResult::WriteToNetwork(packet) = p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) - { + { let _ = udp.send_to(packet, addr).await; } diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index fc14670..6c6a634 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -17,7 +17,7 @@ use crate::device::allowed_ips::AllowedIps; #[derive(Default, Debug)] pub struct Endpoint { pub addr: Option, - pub conn: Option>, + pub udp_conn: Option>, pub tcp_conn: Option> } @@ -74,8 +74,8 @@ impl Peer { index, endpoint: Endpoint { addr: endpoint, - conn: None, - tcp_conn:None, + udp_conn: None, + tcp_conn: None, }, allowed_ips: allowed_ips.iter().map(|ip| (ip, ())).collect(), preshared_key, @@ -91,7 +91,7 @@ impl Peer { } pub fn shutdown_endpoint(&mut self) { - if let Some(conn) = self.endpoint.conn.take() { + if let Some(conn) = self.endpoint.udp_conn.take() { tracing::info!("Disconnecting from endpoint"); drop(conn) } @@ -100,15 +100,16 @@ impl Peer { pub fn set_endpoint(&mut self, addr: SocketAddr) { if self.endpoint.addr != Some(addr) { // We only need to update the endpoint if it differs from the current one - if let Some(conn) = self.endpoint.conn.take() { + if let Some(conn) = self.endpoint.udp_conn.take() { drop(conn) // conn.shutdown(); } - self.endpoint = Endpoint { - addr: Some(addr), - conn: None, - tcp_conn: None, - } + self.endpoint.addr = Some(addr); + // self.endpoint = Endpoint { + // addr: Some(addr), + // udp_conn: None, + // tcp_conn: None, + // } }; } diff --git a/client/lib/src/device/windows_device.rs b/client/lib/src/device/windows_device.rs index fd48fbe..c2ec0a3 100644 --- a/client/lib/src/device/windows_device.rs +++ b/client/lib/src/device/windows_device.rs @@ -29,7 +29,7 @@ impl Device { key_pair: (x25519_dalek::StaticSecret, x25519_dalek::PublicKey), port: Option, mtu: u32, - pub_key: String, + _pub_key: String, scripts:Scripts, ) -> anyhow::Result{ run_opt_script(&scripts.pre_up)?; From 6a2d0db16af30f5ddce66bcf1a64405d7ee0fca2 Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 22 May 2023 19:54:40 +0800 Subject: [PATCH 08/39] bak it --- client/lib/src/device/mod.rs | 2 +- client/lib/src/device/tunnel.rs | 69 +++++++++++++++++++++++++ client/lib/src/device/udp_network.rs | 30 ----------- client/lib/src/device/unix_device.rs | 7 ++- client/lib/src/device/windows_device.rs | 9 ++-- client/lib/src/wr_manager.rs | 17 +++--- protobuf/config.proto | 2 +- 7 files changed, 93 insertions(+), 43 deletions(-) create mode 100644 client/lib/src/device/tunnel.rs delete mode 100644 client/lib/src/device/udp_network.rs diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 7b048f0..19da1d5 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -1,6 +1,6 @@ mod allowed_ips; pub mod peer; -mod udp_network; +mod tunnel; mod tun; pub mod auto_launch; pub mod script_run; diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs new file mode 100644 index 0000000..72b1a65 --- /dev/null +++ b/client/lib/src/device/tunnel.rs @@ -0,0 +1,69 @@ +use std::net::{SocketAddr}; +use tokio::net::{TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; +use socket2::{Type, Protocol, Domain}; + +pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> anyhow::Result { + let socket = socket2::Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?; + socket.set_nonblocking(true)?; + + #[cfg(target_os = "linux")] + { + socket.set_reuse_address(true)?; // On Linux SO_REUSEPORT won't prefer a connected IPv6 socket + if let Some(mark) = mark { + socket.set_mark(mark)?; + } + } + #[cfg(not(any(target_os = "linux", target_os = "windows")))] + socket.set_reuse_port(true)?; + + let port = port.unwrap_or(0); + + let address: SocketAddr = match domain { + Domain::IPV4 => + format!("0.0.0.0:{}", port), + Domain::IPV6 => + format!("[::]:{}", port), + _ => panic!("udp client don't support Domain::Unix") + }.parse()?; + socket.bind(&address.into())?; + Ok(UdpSocket::from_std(socket.into())?) +} + +pub async fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ + let socket = socket2::Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; + #[cfg(target_os = "linux")] + { + socket.set_reuse_address(true)?; // On Linux SO_REUSEPORT won't prefer a connected IPv6 socket + if let Some(mark) = mark { + socket.set_mark(mark)?; + } + } + #[cfg(not(any(target_os = "linux", target_os = "windows")))] + socket.set_reuse_port(true)?; + + let port = port.unwrap_or(0); + let address: SocketAddr = match domain { + Domain::IPV4 => + format!("0.0.0.0:{}", port), + Domain::IPV6 => + format!("[::]:{}", port), + _ => panic!("udp client don't support Domain::Unix") + }.parse()?; + socket.bind(&address.into())?; + let tcp_listener = TcpListener::from_std(socket.into())?; + Ok(tcp_listener) +} + +#[cfg(test)] +mod test { + use socket2::Domain; + use crate::device::tunnel::create_tcp_server; + + #[tokio::test] + async fn test_tcp_bind() { + let ip4_server = create_tcp_server(None, Domain::IPV4, None).await.unwrap(); + let ip6_server = create_tcp_server(Some(ip4_server.local_addr().unwrap().port()), Domain::IPV6, None).await.unwrap(); + + println!("init ip4/ip6 in same port ok"); + } +} \ No newline at end of file diff --git a/client/lib/src/device/udp_network.rs b/client/lib/src/device/udp_network.rs deleted file mode 100644 index f5f21da..0000000 --- a/client/lib/src/device/udp_network.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::net::SocketAddr; -use tokio::net::UdpSocket; -use socket2::{Type, Protocol, Domain}; - -pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> anyhow::Result { - let socket = socket2::Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?; - socket.set_nonblocking(true)?; - - #[cfg(target_os = "linux")] - { - socket.set_reuse_address(true)?; // On Linux SO_REUSEPORT won't prefer a connected IPv6 socket - if let Some(mark) = mark { - socket.set_mark(mark)?; - } - } - #[cfg(not(any(target_os = "linux", target_os = "windows")))] - socket.set_reuse_port(true)?; - - let port = port.unwrap_or(0); - - let address: SocketAddr = match domain { - Domain::IPV4 => - format!("0.0.0.0:{}", port), - Domain::IPV6 => - format!("[::]:{}", port), - _ => panic!("udp client don't support Domain::Unix") - }.parse()?; - socket.bind(&address.into())?; - Ok(UdpSocket::from_std(socket.into())?) -} \ No newline at end of file diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 9f962b3..536fef5 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -12,13 +12,15 @@ use crate::device::{DeviceData, Peers, HANDSHAKE_RATE_LIMIT, MAX_UDP_SIZE}; use crate::device::peer::AllowedIP; use crate::device::script_run::{run_opt_script, Scripts}; use crate::device::tun::create_async_tun; -use crate::device::udp_network::create_udp_socket; +use crate::device::tunnel::create_udp_socket; use nix::unistd::Uid; +use crate::protobuf::config::Protocol; pub struct Device { pub device_data:DeviceData, task:JoinHandle<()>, + protocol:Protocol, } impl Device { @@ -29,8 +31,8 @@ impl Device { key_pair: (x25519_dalek::StaticSecret, x25519_dalek::PublicKey), port: Option, mtu: u32, - pub_key: String, scripts:Scripts, + protocol: Protocol, ) -> anyhow::Result { run_opt_script(&scripts.pre_up)?; tracing::debug!("begin to create tun"); @@ -75,6 +77,7 @@ impl Device { let device = Device { device_data: DeviceData::new(name,peers1, key_pair1, port, scripts), task, + protocol, }; //run_opt_script(&Some("iptables -A FORWARD -i for0 -j ACCEPT".to_owned()))?; diff --git a/client/lib/src/device/windows_device.rs b/client/lib/src/device/windows_device.rs index c2ec0a3..cc8c489 100644 --- a/client/lib/src/device/windows_device.rs +++ b/client/lib/src/device/windows_device.rs @@ -11,14 +11,15 @@ use crate::device::{HANDSHAKE_RATE_LIMIT, MAX_UDP_SIZE}; use crate::device::peer::AllowedIP; use crate::device::tun::{create_async_tun, ReadPart, WritePart}; use crate::device::script_run::{run_opt_script, Scripts}; -use crate::device::udp_network::create_udp_socket; - +use crate::device::tunnel::create_udp_socket; +use crate::protobuf::config::Protocol; pub struct Device { pub device_data: DeviceData, read_task:JoinHandle<()>, write_task:JoinHandle<()>, + pub protocol: Protocol, } impl Device { @@ -29,13 +30,14 @@ impl Device { key_pair: (x25519_dalek::StaticSecret, x25519_dalek::PublicKey), port: Option, mtu: u32, - _pub_key: String, scripts:Scripts, + protocol: Protocol, ) -> anyhow::Result{ run_opt_script(&scripts.pre_up)?; let (mut iface_reader, iface_writer, name) = create_async_tun(name, mtu, address)?; + let udp4 = Arc::new(create_udp_socket(port, Domain::IPV4, None)?); let port = udp4.local_addr()?.port(); @@ -51,6 +53,7 @@ impl Device { device_data:DeviceData::new(name, peers, key_pair, port, scripts), read_task, write_task, + protocol, }; run_opt_script(&device.scripts.post_up)?; Ok(device) diff --git a/client/lib/src/wr_manager.rs b/client/lib/src/wr_manager.rs index 4bffa6d..1541657 100644 --- a/client/lib/src/wr_manager.rs +++ b/client/lib/src/wr_manager.rs @@ -5,7 +5,7 @@ use anyhow::anyhow; use serde_derive::{Deserialize, Serialize}; use crate::config::{Config, Identity}; use crate::device::peer::AllowedIP; -use crate::protobuf::config::WrConfig; +use crate::protobuf::config::{Protocol, WrConfig}; use crate::device::Device; use crate::device::script_run::Scripts; @@ -64,13 +64,18 @@ impl WRManager { self.close().await; tracing::info!("close device before restart"); let tun_name = config.get_tun_name(); + let protocol = Protocol::from_i32(interface.protocol).unwrap_or(Protocol::Udp); let scripts = Scripts::load_from_interface(&interface); let key_pair = (config.identity.x25519_sk.clone(), config.identity.x25519_pk.clone()); - let wr_interface = Device::new(&tun_name, &address, key_pair, Some(interface.listen_port as u16), - interface.mtu.unwrap_or(1420) as u32, - config.identity.pk_base64.clone(), - scripts, + let wr_interface = Device::new( + &tun_name, + &address, + key_pair, + Some(interface.listen_port as u16), + interface.mtu.unwrap_or(1420) as u32, + scripts, + protocol, )?; self.device = Some(wr_interface); @@ -90,6 +95,7 @@ impl WRManager { } pub fn is_alive(&self) -> bool { self.device.is_some() } + pub async fn close(&mut self) { if let Some(ref mut device) = self.device.take() { device.close().await @@ -97,7 +103,6 @@ impl WRManager { } pub fn device_info(&self) -> Vec { - self.device.as_ref().map_or(vec![], |device| { vec![DeviceInfoResp { name: device.name.clone() diff --git a/protobuf/config.proto b/protobuf/config.proto index 9c99e55..ac61672 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -15,7 +15,7 @@ message Interface { optional string name = 1; repeated string address = 2; int32 listen_port = 3; - optional string private_key = 4; + // optional string private_key = 4; // this is no needed now, we may support it in future version repeated string dns = 5; optional uint32 mtu = 6; optional string pre_up = 7; From 1f57958a5bac7cc5f2638debc0647e6f45529191 Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 22 May 2023 22:32:42 +0800 Subject: [PATCH 09/39] bak it --- client/lib/src/device/tunnel.rs | 3 ++- client/lib/src/sc_manager.rs | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs index 72b1a65..167f9cb 100644 --- a/client/lib/src/device/tunnel.rs +++ b/client/lib/src/device/tunnel.rs @@ -29,7 +29,7 @@ pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> Ok(UdpSocket::from_std(socket.into())?) } -pub async fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ +pub async fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ let socket = socket2::Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; #[cfg(target_os = "linux")] { @@ -61,6 +61,7 @@ mod test { #[tokio::test] async fn test_tcp_bind() { + // macOS support let ip4_server = create_tcp_server(None, Domain::IPV4, None).await.unwrap(); let ip6_server = create_tcp_server(Some(ip4_server.local_addr().unwrap().port()), Domain::IPV6, None).await.unwrap(); diff --git a/client/lib/src/sc_manager.rs b/client/lib/src/sc_manager.rs index 1ca29b2..5e71d70 100644 --- a/client/lib/src/sc_manager.rs +++ b/client/lib/src/sc_manager.rs @@ -1,19 +1,14 @@ -use std::convert::identity; use std::sync::Arc; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use paho_mqtt as mqtt; -use paho_mqtt::SslVersion::Default; use prost::Message; use tokio::sync::mpsc::Sender; use tokio_stream::StreamExt; -use tonic::metadata::{Ascii, MetadataValue}; -use tonic::Request; -use tonic::transport::Channel; -use crate::protobuf::config::{ClientMessage, NetworkMessage, NetworkStatus, NodeStatus, PeerChange, WrConfig}; +use crate::protobuf::config::{ClientMessage, NetworkMessage, NetworkStatus, NodeStatus, WrConfig}; use crate::protobuf::config::client_message::Info::{Config, Status}; -use crate::protobuf::config::network_message::Info::Peer; +use crate::protobuf::config::network_message::Info::{Peer, Status as NStatus}; use crate::server_manager::ServerMessage; //Sync Config Manager @@ -116,8 +111,8 @@ impl SCManager { Peer(peer_change) => { let _ = self.sender.send(ServerMessage::SyncPeers(peer_change)).await; } - Status(status) => { - if let Some(NetworkStatus::NetworkDelete) = NetworkStatus.from_i32(status) { + NStatus(status) => { + if let Some(NetworkStatus::NetworkDelete) = NetworkStatus::from_i32(status) { let _ = self.sender.send( ServerMessage::StopWR("network has been delete".to_owned()) ).await; From c260b5c15ab798529039a2b49e25565cf4acc5e7 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 23 May 2023 00:00:45 +0800 Subject: [PATCH 10/39] bak --- client/lib/src/device/mod.rs | 60 ++++++++++++++++++++++++++++ client/lib/src/device/peer.rs | 18 ++++----- client/lib/src/device/tunnel.rs | 3 +- client/lib/src/device/unix_device.rs | 57 ++++++++++++++++++++------ 4 files changed, 114 insertions(+), 24 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 19da1d5..64474a9 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -42,6 +42,7 @@ use self::tun::WritePart; const HANDSHAKE_RATE_LIMIT: u64 = 100; // The number of handshakes per second we can tolerate before using cookies const MAX_UDP_SIZE: usize = (1 << 16) - 1; +const MAX_TCP_SIZE: usize = (1 << 16) -1; // const MAX_ITR: usize = 100; // Number of packets to handle per handler call #[derive(Debug)] @@ -257,6 +258,36 @@ pub async fn tun_read_handle(peers: &Arc>, udp4: &UdpSocket, udp6: } } +pub async fn tun_read_tcp_handle(peers: &Arc>, src_buf: &[u8], dst_buf: &mut [u8]) { + //tracing::debug!("tun read:{:x?},{:?}", Tunn::dst_address(src_buf), src_buf); + if let Some(dst_addr) = Tunn::dst_address(src_buf) { + if let Some(peer) = peers.read().await.by_ip.find(dst_addr) { + let mut peer = peer.lock().await; + match peer.tunnel.encapsulate(src_buf, &mut dst_buf[..]) { + TunnResult::Done => { + // tracing::debug!("done"); + } + TunnResult::Err(e) => { + tracing::error!(message = "Encapsulate error", error = ?e) + } + TunnResult::WriteToNetwork(packet) => { + let endpoint = peer.endpoint(); + if let Some(addr @ SocketAddr::V4(_)) = endpoint.addr { + //tracing::debug!("send:{}, size:{}",addr,packet.len()); + let _ = udp4.send_to(packet, addr).await; + } else if let Some(addr @ SocketAddr::V6(_)) = endpoint.addr { + let _ = udp6.send_to(packet, addr).await; + } else { + tracing::error!("No endpoint"); + } + //TODO: get tcp socket from peers and send + } + _ => panic!("Unexpected result from encapsulate"), + }; + } + } +} + pub async fn rate_limiter_timer(rate_limiter: &Arc) { let mut interval = time::interval(Duration::from_secs(1)); loop { @@ -298,6 +329,35 @@ pub async fn peers_timer(peers: &Arc>, udp4: &UdpSocket, udp6: &Ud } } +pub async fn tcp_peers_timer(peers: &Arc>) { + let mut interval = time::interval(Duration::from_millis(250)); + let mut dst_buf: Vec= vec![0; MAX_UDP_SIZE]; + + loop { + interval.tick().await; + let peer_map = &peers.read().await.by_key; + for peer in peer_map.values() { + let mut p = peer.lock().await; + //TODO: if needs to create tcp when p.endpoint().addr.is_some() + let conn = match &mut p.endpoint().tcp_conn { + Some(addr) => addr, + None => continue, + }; + match p.update_timers(&mut dst_buf) { + TunnResult::Done => {} + TunnResult::Err(WireGuardError::ConnectionExpired) => { + p.shutdown_endpoint(); // close open udp socket + } + TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), + TunnResult::WriteToNetwork(packet) => { + let _ = conn.write_all(packet).await; + } + _ => panic!("Unexpected result from update_timers"), + }; + } + } +} + pub async fn udp_handler(udp: &UdpSocket, key_pair: &(x25519_dalek::StaticSecret, x25519_dalek::PublicKey), diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index 6c6a634..eab2d99 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use std::sync::Arc; use boringtun::noise::{Tunn, TunnResult}; -use tokio::net::{TcpSocket, UdpSocket}; +use tokio::net::{TcpStream, UdpSocket}; use crate::device::allowed_ips::AllowedIps; @@ -18,7 +18,7 @@ use crate::device::allowed_ips::AllowedIps; pub struct Endpoint { pub addr: Option, pub udp_conn: Option>, - pub tcp_conn: Option> + pub tcp_conn: Option> } pub struct Peer { @@ -91,19 +91,19 @@ impl Peer { } pub fn shutdown_endpoint(&mut self) { - if let Some(conn) = self.endpoint.udp_conn.take() { - tracing::info!("Disconnecting from endpoint"); - drop(conn) + if let Some(_) = self.endpoint.udp_conn.take() { + tracing::info!("disconnecting from endpoint"); + //drop(conn) + } + if let Some(_) = self.endpoint.tcp_conn.take() { + tracing::info!("disconnecting from endpoint"); } } pub fn set_endpoint(&mut self, addr: SocketAddr) { if self.endpoint.addr != Some(addr) { // We only need to update the endpoint if it differs from the current one - if let Some(conn) = self.endpoint.udp_conn.take() { - drop(conn) - // conn.shutdown(); - } + self.shutdown_endpoint(); self.endpoint.addr = Some(addr); // self.endpoint = Endpoint { // addr: Some(addr), diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs index 167f9cb..0b8dea7 100644 --- a/client/lib/src/device/tunnel.rs +++ b/client/lib/src/device/tunnel.rs @@ -29,7 +29,7 @@ pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> Ok(UdpSocket::from_std(socket.into())?) } -pub async fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ +pub fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ let socket = socket2::Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; #[cfg(target_os = "linux")] { @@ -61,7 +61,6 @@ mod test { #[tokio::test] async fn test_tcp_bind() { - // macOS support let ip4_server = create_tcp_server(None, Domain::IPV4, None).await.unwrap(); let ip6_server = create_tcp_server(Some(ip4_server.local_addr().unwrap().port()), Domain::IPV6, None).await.unwrap(); diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 536fef5..3e807a5 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -12,7 +12,7 @@ use crate::device::{DeviceData, Peers, HANDSHAKE_RATE_LIMIT, MAX_UDP_SIZE}; use crate::device::peer::AllowedIP; use crate::device::script_run::{run_opt_script, Scripts}; use crate::device::tun::create_async_tun; -use crate::device::tunnel::create_udp_socket; +use crate::device::tunnel::{create_tcp_server, create_udp_socket}; use nix::unistd::Uid; use crate::protobuf::config::Protocol; @@ -39,22 +39,26 @@ impl Device { let (mut iface_reader, iface_writer,pi, name) = create_async_tun(name, mtu, address)?; tracing::debug!("finish to create tun"); let iface_writer = Arc::new(Mutex::new(iface_writer)); - // create tcp/udp server - let udp4 = create_udp_socket(port, Domain::IPV4, None)?; - let port = udp4.local_addr()?.port(); - let udp6 = create_udp_socket(Some(port), Domain::IPV6, None)?; + let rate_limiter = Arc::new(RateLimiter::new(&key_pair.1, HANDSHAKE_RATE_LIMIT)); let peers: Arc> = Arc::new(RwLock::new(Peers::default())); + // create tcp/udp server + let (port,task) = match protocol { + Protocol::Udp => { + let udp4 = create_udp_socket(port, Domain::IPV4, None)?; + let port = udp4.local_addr()?.port(); + let udp6 = create_udp_socket(Some(port), Domain::IPV6, None)?; + - let mut tun_src_buf: Vec = vec![0; MAX_UDP_SIZE]; - let mut tun_dst_buf: Vec = vec![0; MAX_UDP_SIZE]; - let key_pair1 = key_pair.clone(); - let peers1 = peers.clone(); + let mut tun_src_buf: Vec = vec![0; MAX_UDP_SIZE]; + let mut tun_dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + let key_pair1 = key_pair.clone(); + let peers1 = peers.clone(); - let task:JoinHandle<()> = tokio::spawn(async move { - loop { - tokio::select! { + let task:JoinHandle<()> = tokio::spawn(async move { + loop { + tokio::select! { _ = device::rate_limiter_timer(&rate_limiter) => {} _ = device::peers_timer(&peers,&udp4, &udp6) => {} // iface listen @@ -71,9 +75,36 @@ impl Device { _ = device::udp_handler(&udp6, &key_pair,&rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, } + } + + }); + (port, task) + } + Protocol::Tcp => { + let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; + let port = tcp4.local_addr()?.port(); + let tcp6 = create_tcp_server(Some(port), Domain::IPV6, None)?; + let task:JoinHandle<()> = tokio::spawn(async move { + loop { + tokio::select! { + _ = device::rate_limiter_timer(&rate_limiter) => {} + _ = device::tcp_peers_timer(&peers) => {} + // iface listen + Ok(len) = iface_reader.read(&mut tun_src_buf) => { + let src_buf = if pi { + &tun_src_buf[4..(len+4)] + } else { + &tun_src_buf[0..len] + }; + device::tun_read_tcp_handle(&peers, src_buf, &mut tun_dst_buf).await; + } + } + } + }); + (port, task) } + }; - }); let device = Device { device_data: DeviceData::new(name,peers1, key_pair1, port, scripts), task, From b36fef8166e4918c18228c6fbf6465c3a88e3934 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 23 May 2023 19:52:58 +0800 Subject: [PATCH 11/39] bak it --- client/lib/src/device/mod.rs | 150 ++++++++++++++++++++++++++++++---- client/lib/src/device/peer.rs | 7 +- 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 64474a9..867fafb 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -28,10 +28,11 @@ use boringtun::noise::rate_limiter::RateLimiter; use boringtun::noise::{Packet, Tunn, TunnResult}; use boringtun::noise::handshake::parse_handshake_anon; use prost::bytes::BufMut; -use tokio::net::UdpSocket; +use tokio::net::{TcpStream, UdpSocket}; use tokio::sync::{Mutex, RwLock}; use tokio::time; -use tokio::io::AsyncWriteExt;//keep +use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep +use tokio::net::tcp::{OwnedReadHalf, ReadHalf, WriteHalf}; use allowed_ips::AllowedIps; use peer::{AllowedIP, Peer}; @@ -272,15 +273,11 @@ pub async fn tun_read_tcp_handle(peers: &Arc>, src_buf: &[u8], dst } TunnResult::WriteToNetwork(packet) => { let endpoint = peer.endpoint(); - if let Some(addr @ SocketAddr::V4(_)) = endpoint.addr { - //tracing::debug!("send:{}, size:{}",addr,packet.len()); - let _ = udp4.send_to(packet, addr).await; - } else if let Some(addr @ SocketAddr::V6(_)) = endpoint.addr { - let _ = udp6.send_to(packet, addr).await; + if let Some(conn) = &mut endpoint.tcp_conn { + let _ = conn.write_all(packet).await; } else { - tracing::error!("No endpoint"); + tracing::info!("no endpoint of {:?}", endpoint.addr); } - //TODO: get tcp socket from peers and send } _ => panic!("Unexpected result from encapsulate"), }; @@ -339,10 +336,9 @@ pub async fn tcp_peers_timer(peers: &Arc>) { for peer in peer_map.values() { let mut p = peer.lock().await; //TODO: if needs to create tcp when p.endpoint().addr.is_some() - let conn = match &mut p.endpoint().tcp_conn { - Some(addr) => addr, - None => continue, - }; + if p.endpoint().tcp_conn.is_none() { + continue + } match p.update_timers(&mut dst_buf) { TunnResult::Done => {} TunnResult::Err(WireGuardError::ConnectionExpired) => { @@ -350,7 +346,10 @@ pub async fn tcp_peers_timer(peers: &Arc>) { } TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), TunnResult::WriteToNetwork(packet) => { - let _ = conn.write_all(packet).await; + if let Some(conn) = &mut p.endpoint().tcp_conn { + let _ = conn.write_all(packet).await; + } + } _ => panic!("Unexpected result from update_timers"), }; @@ -479,6 +478,129 @@ pub async fn udp_handler(udp: &UdpSocket, } } +pub async fn tcp_handler(mut tcp: OwnedReadHalf, + key_pair: &(x25519_dalek::StaticSecret, x25519_dalek::PublicKey), + rate_limiter: &RateLimiter, + peers: Arc>, + iface: Arc>, + pi: bool, +) { + + + let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; + let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + let (private_key, public_key) = key_pair; + + while let Ok(size) = tcp.read(&mut src_buf).await { + //tracing::debug!("recv: {addr:?}, {size}"); + let parsed_packet = + match rate_limiter.verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { + Ok(packet) => packet, + Err(TunnResult::WriteToNetwork(cookie)) => { + //TODO: send + continue; + } + Err(_) => continue, + }; + let peer = match &parsed_packet { + Packet::HandshakeInit(p) => { + if let Ok(hh) = parse_handshake_anon(private_key, public_key, p) { + let by_key = &peers.read().await.by_key; + by_key.get(&x25519_dalek::PublicKey::from(hh.peer_static_public)).map(Arc::clone) + } else { + None + } + } + Packet::HandshakeResponse(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketCookieReply(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketData(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + }; + let peer = match peer { + None => continue, + Some(peer) => peer, + }; + + let mut p = peer.lock().await; + + // We found a peer, use it to decapsulate the message+ + let mut flush = false; // Are there packets to send from the queue? + match p + .tunnel + .handle_verified_packet(parsed_packet, &mut dst_buf[..]) + { + TunnResult::Done => {} + TunnResult::Err(_) => continue, + TunnResult::WriteToNetwork(packet) => { + flush = true; + let _ = udp.send_to(packet, addr).await; + } + TunnResult::WriteToTunnelV4(packet, addr) => { + // tracing::debug!("{addr:?}"); + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP4_HEADER); + buf.put_slice(&packet); + cfg_if! { + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&buf); + } else { + let _ = iface.lock().await.write(&buf).await; + } + } + } else { + cfg_if! { + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&packet); + } else { + let _ = iface.lock().await.write(&packet).await; + } + } + } + } else {} + } + TunnResult::WriteToTunnelV6(packet, addr) => { + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP6_HEADER); + buf.put_slice(&packet); + cfg_if! { + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&buf); + } else { + let _ = iface.lock().await.write(&buf).await; + } + } + } else { + cfg_if! { + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(packet); + } else { + let _ = iface.lock().await.write(packet).await; + } + } + }; + + } + } + }; + + if flush { + // Flush pending queue + + while let TunnResult::WriteToNetwork(packet) = + p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) + { + let _ = udp.send_to(packet, addr).await; + } + + + } + p.set_endpoint(addr); + } +} + pub struct Peers { pub by_key: HashMap>>, diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index eab2d99..e9022f6 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -10,7 +10,8 @@ use std::str::FromStr; use std::sync::Arc; use boringtun::noise::{Tunn, TunnResult}; -use tokio::net::{TcpStream, UdpSocket}; +use tokio::net::{UdpSocket}; +use tokio::net::tcp::OwnedWriteHalf; use crate::device::allowed_ips::AllowedIps; @@ -18,7 +19,7 @@ use crate::device::allowed_ips::AllowedIps; pub struct Endpoint { pub addr: Option, pub udp_conn: Option>, - pub tcp_conn: Option> + pub tcp_conn: Option } pub struct Peer { @@ -26,7 +27,7 @@ pub struct Peer { pub(crate) tunnel: Tunn, /// The index the tunnel uses index: u32, - endpoint: Endpoint, + pub endpoint: Endpoint, allowed_ips: AllowedIps<()>, preshared_key: Option<[u8; 32]>, } From 9675affa469d0ac63b66ff88747daa935d5cf556 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 23 May 2023 23:22:17 +0800 Subject: [PATCH 12/39] bak it --- client/lib/src/device/mod.rs | 175 ++++++++++++++------------- client/lib/src/device/peer.rs | 5 - client/lib/src/device/unix_device.rs | 1 + 3 files changed, 92 insertions(+), 89 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 867fafb..5071c96 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -28,7 +28,7 @@ use boringtun::noise::rate_limiter::RateLimiter; use boringtun::noise::{Packet, Tunn, TunnResult}; use boringtun::noise::handshake::parse_handshake_anon; use prost::bytes::BufMut; -use tokio::net::{TcpStream, UdpSocket}; +use tokio::net::{TcpListener, TcpStream, UdpSocket}; use tokio::sync::{Mutex, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep @@ -478,126 +478,133 @@ pub async fn udp_handler(udp: &UdpSocket, } } -pub async fn tcp_handler(mut tcp: OwnedReadHalf, +pub async fn tcp_handler(listener:TcpListener, key_pair: &(x25519_dalek::StaticSecret, x25519_dalek::PublicKey), rate_limiter: &RateLimiter, peers: Arc>, iface: Arc>, pi: bool, ) { - - - let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; - let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + //TODO: socket let (private_key, public_key) = key_pair; + loop { + let (mut socket, addr) = listener.accept().await?; + tokio::spawn(async move { + let (mut reader, mut writer ) = socket.into_split(); + let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; + let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + while let Ok(size) = reader.read(&mut src_buf).await { + if size > 0 { + let parsed_packet = + match rate_limiter.verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { + Ok(packet) => packet, + Err(TunnResult::WriteToNetwork(cookie)) => { + let _ = writer.write_all(cookie).await; + continue; + } + Err(_) => continue, + }; + let peer = match &parsed_packet { + Packet::HandshakeInit(p) => { + if let Ok(hh) = parse_handshake_anon(private_key, public_key, p) { + let by_key = &peers.read().await.by_key; + by_key.get(&x25519_dalek::PublicKey::from(hh.peer_static_public)).map(Arc::clone) + } else { + None + } + } + Packet::HandshakeResponse(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketCookieReply(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketData(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + }; + let peer = match peer { + None => continue, + Some(peer) => peer, + }; - while let Ok(size) = tcp.read(&mut src_buf).await { - //tracing::debug!("recv: {addr:?}, {size}"); - let parsed_packet = - match rate_limiter.verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { - Ok(packet) => packet, - Err(TunnResult::WriteToNetwork(cookie)) => { - //TODO: send - continue; - } - Err(_) => continue, - }; - let peer = match &parsed_packet { - Packet::HandshakeInit(p) => { - if let Ok(hh) = parse_handshake_anon(private_key, public_key, p) { - let by_key = &peers.read().await.by_key; - by_key.get(&x25519_dalek::PublicKey::from(hh.peer_static_public)).map(Arc::clone) - } else { - None - } - } - Packet::HandshakeResponse(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - Packet::PacketCookieReply(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - Packet::PacketData(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - }; - let peer = match peer { - None => continue, - Some(peer) => peer, - }; - - let mut p = peer.lock().await; - - // We found a peer, use it to decapsulate the message+ - let mut flush = false; // Are there packets to send from the queue? - match p - .tunnel - .handle_verified_packet(parsed_packet, &mut dst_buf[..]) - { - TunnResult::Done => {} - TunnResult::Err(_) => continue, - TunnResult::WriteToNetwork(packet) => { - flush = true; - let _ = udp.send_to(packet, addr).await; - } - TunnResult::WriteToTunnelV4(packet, addr) => { - // tracing::debug!("{addr:?}"); - if p.is_allowed_ip(addr) { - if pi { - let mut buf: Vec = Vec::new(); - buf.put_slice(&IP4_HEADER); - buf.put_slice(&packet); - cfg_if! { + let mut p = peer.lock().await; + + // We found a peer, use it to decapsulate the message+ + let mut flush = false; // Are there packets to send from the queue? + match p + .tunnel + .handle_verified_packet(parsed_packet, &mut dst_buf[..]) + { + TunnResult::Done => {} + TunnResult::Err(_) => continue, + TunnResult::WriteToNetwork(packet) => { + flush = true; + if let Some(conn) = &mut p.endpoint.tcp_conn { + let _ = conn.write_all(packet).await; + } + } + TunnResult::WriteToTunnelV4(packet, addr) => { + // tracing::debug!("{addr:?}"); + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP4_HEADER); + buf.put_slice(&packet); + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&buf); } else { let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&packet); } else { let _ = iface.lock().await.write(&packet).await; } } - } - } else {} - } - TunnResult::WriteToTunnelV6(packet, addr) => { - if p.is_allowed_ip(addr) { - if pi { - let mut buf: Vec = Vec::new(); - buf.put_slice(&IP6_HEADER); - buf.put_slice(&packet); - cfg_if! { + } + } else {} + } + TunnResult::WriteToTunnelV6(packet, addr) => { + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP6_HEADER); + buf.put_slice(&packet); + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&buf); } else { let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(packet); } else { let _ = iface.lock().await.write(packet).await; } } + }; + + } + } }; + if flush { + // Flush pending queue + while let TunnResult::WriteToNetwork(packet) = + p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) + { + if let Some(conn) = &mut p.endpoint.tcp_conn { + let _ = conn.write_all(packet).await; + } + } + } } - } - }; - - if flush { - // Flush pending queue - while let TunnResult::WriteToNetwork(packet) = - p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) - { - let _ = udp.send_to(packet, addr).await; } - - - } - p.set_endpoint(addr); + tracing::info!("tcp: {addr:?} close"); + }) } } diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index e9022f6..648f668 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -106,11 +106,6 @@ impl Peer { // We only need to update the endpoint if it differs from the current one self.shutdown_endpoint(); self.endpoint.addr = Some(addr); - // self.endpoint = Endpoint { - // addr: Some(addr), - // udp_conn: None, - // tcp_conn: None, - // } }; } diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 3e807a5..0b05d13 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -98,6 +98,7 @@ impl Device { }; device::tun_read_tcp_handle(&peers, src_buf, &mut tun_dst_buf).await; } + //_ = device::tcp_handler(&key_pair,&rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} } } }); From 0affc1592ea3cc73b01b3769ed652d6a7132f8c0 Mon Sep 17 00:00:00 2001 From: timzaak Date: Fri, 26 May 2023 10:21:07 +0800 Subject: [PATCH 13/39] bak --- client/lib/src/device/mod.rs | 100 ++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 5071c96..2c21f9e 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -23,6 +23,7 @@ use rand::rngs::OsRng; use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; +use anyhow::anyhow; use boringtun::noise::errors::WireGuardError; use boringtun::noise::rate_limiter::RateLimiter; use boringtun::noise::{Packet, Tunn, TunnResult}; @@ -32,7 +33,7 @@ use tokio::net::{TcpListener, TcpStream, UdpSocket}; use tokio::sync::{Mutex, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep -use tokio::net::tcp::{OwnedReadHalf, ReadHalf, WriteHalf}; +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf, ReadHalf, WriteHalf}; use allowed_ips::AllowedIps; use peer::{AllowedIP, Peer}; @@ -478,28 +479,50 @@ pub async fn udp_handler(udp: &UdpSocket, } } -pub async fn tcp_handler(listener:TcpListener, - key_pair: &(x25519_dalek::StaticSecret, x25519_dalek::PublicKey), - rate_limiter: &RateLimiter, - peers: Arc>, - iface: Arc>, - pi: bool, -) { +enum WriterState { + PureWriter(OwnedWriteHalf), + PeerWriter(Arc>), +} +pub async fn tcp_handler( + listener:TcpListener, + key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, + rate_limiter: Arc, + peers: Arc>, + iface: Arc>, + pi: bool, +)->anyhow::Result<()> { //TODO: socket - let (private_key, public_key) = key_pair; loop { let (mut socket, addr) = listener.accept().await?; + let key_pair = key_pair.clone(); + let rate_limiter = rate_limiter.clone(); + let peers = peers.clone(); + let iface = iface.clone(); tokio::spawn(async move { + let (private_key, public_key) = key_pair.as_ref(); let (mut reader, mut writer ) = socket.into_split(); + let mut writer = WriterState::PureWriter(writer); let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; while let Ok(size) = reader.read(&mut src_buf).await { if size > 0 { let parsed_packet = - match rate_limiter.verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { + match rate_limiter.as_ref().verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { Ok(packet) => packet, Err(TunnResult::WriteToNetwork(cookie)) => { - let _ = writer.write_all(cookie).await; + match &mut writer { + WriterState::PureWriter(writer) => { + let _ = writer.write_all(cookie).await; + }, + WriterState::PeerWriter(peer)=> { + let mut p = peer.lock().await; + if let Some(w) = &mut p.endpoint.tcp_conn { + let _ = w.write_all(cookie).await; + }else { + tracing::warn!("should not come here"); + } + } + } continue; } Err(_) => continue, @@ -523,7 +546,8 @@ pub async fn tcp_handler(listener:TcpListener, }; let mut p = peer.lock().await; - + if p.endpoint.tcp_conn.is_none() { + } // We found a peer, use it to decapsulate the message+ let mut flush = false; // Are there packets to send from the queue? match p @@ -546,20 +570,20 @@ pub async fn tcp_handler(listener:TcpListener, buf.put_slice(&IP4_HEADER); buf.put_slice(&packet); cfg_if! { - if #[cfg(target_os="windows")] { - let _ = iface.lock().await.write(&buf); - } else { - let _ = iface.lock().await.write(&buf).await; - } - } + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&buf); + } else { + let _ = iface.lock().await.write(&buf).await; + } + } } else { cfg_if! { - if #[cfg(target_os="windows")] { - let _ = iface.lock().await.write(&packet); - } else { - let _ = iface.lock().await.write(&packet).await; - } - } + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&packet); + } else { + let _ = iface.lock().await.write(&packet).await; + } + } } } else {} } @@ -570,22 +594,21 @@ pub async fn tcp_handler(listener:TcpListener, buf.put_slice(&IP6_HEADER); buf.put_slice(&packet); cfg_if! { - if #[cfg(target_os="windows")] { - let _ = iface.lock().await.write(&buf); - } else { - let _ = iface.lock().await.write(&buf).await; - } - } + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(&buf); + } else { + let _ = iface.lock().await.write(&buf).await; + } + } } else { cfg_if! { - if #[cfg(target_os="windows")] { - let _ = iface.lock().await.write(packet); - } else { - let _ = iface.lock().await.write(packet).await; - } - } + if #[cfg(target_os="windows")] { + let _ = iface.lock().await.write(packet); + } else { + let _ = iface.lock().await.write(packet).await; + } + } }; - } } }; @@ -604,8 +627,9 @@ pub async fn tcp_handler(listener:TcpListener, } tracing::info!("tcp: {addr:?} close"); - }) + }); } + Ok(()) } From d6999e80d92c48b61d4b2e5044b8237e54d1ddf7 Mon Sep 17 00:00:00 2001 From: timzaak Date: Fri, 26 May 2023 18:49:08 +0800 Subject: [PATCH 14/39] bak it --- client/lib/src/device/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 2c21f9e..15f10e8 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -17,6 +17,7 @@ cfg_if! { } use std::collections::HashMap; +use std::mem; use cfg_if::cfg_if; use rand::RngCore; use rand::rngs::OsRng; @@ -547,6 +548,12 @@ pub async fn tcp_handler( let mut p = peer.lock().await; if p.endpoint.tcp_conn.is_none() { + if let WriterState::PureWriter(_) = &mut writer { + let pure_writer = mem::replace(&mut writer,WriterState::PeerWriter(peer.clone())); + if let WriterState::PureWriter(_writer) = pure_writer { + p.endpoint.tcp_conn = Some(_writer); + } + } } // We found a peer, use it to decapsulate the message+ let mut flush = false; // Are there packets to send from the queue? From 95d35c4bf321beb42975da2d9f1657a08881c705 Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 29 May 2023 13:57:19 +0800 Subject: [PATCH 15/39] bak --- client/lib/src/device/mod.rs | 217 ++++++++++++++++++----------------- 1 file changed, 114 insertions(+), 103 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 15f10e8..9a5f4b7 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -484,159 +484,170 @@ enum WriterState { PureWriter(OwnedWriteHalf), PeerWriter(Arc>), } -pub async fn tcp_handler( + +pub async fn tcp_listener_handler( listener:TcpListener, key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, rate_limiter: Arc, peers: Arc>, iface: Arc>, pi: bool, -)->anyhow::Result<()> { - //TODO: socket +) ->anyhow::Result<()> { loop { let (mut socket, addr) = listener.accept().await?; let key_pair = key_pair.clone(); let rate_limiter = rate_limiter.clone(); let peers = peers.clone(); let iface = iface.clone(); - tokio::spawn(async move { - let (private_key, public_key) = key_pair.as_ref(); - let (mut reader, mut writer ) = socket.into_split(); - let mut writer = WriterState::PureWriter(writer); - let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; - let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; - while let Ok(size) = reader.read(&mut src_buf).await { - if size > 0 { - let parsed_packet = - match rate_limiter.as_ref().verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { - Ok(packet) => packet, - Err(TunnResult::WriteToNetwork(cookie)) => { - match &mut writer { - WriterState::PureWriter(writer) => { - let _ = writer.write_all(cookie).await; - }, - WriterState::PeerWriter(peer)=> { - let mut p = peer.lock().await; - if let Some(w) = &mut p.endpoint.tcp_conn { - let _ = w.write_all(cookie).await; - }else { - tracing::warn!("should not come here"); - } + tcp_handler(socket, addr,key_pair, rate_limiter, peers, iface, pi); + } + //Ok(()) +} +pub fn tcp_handler( + mut socket: TcpStream, + addr: SocketAddr, + key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, + rate_limiter: Arc, + peers: Arc>, + iface: Arc>, + pi: bool, +) { + tokio::spawn(async move { + let (private_key, public_key) = key_pair.as_ref(); + let (mut reader, mut writer ) = socket.into_split(); + let mut writer = WriterState::PureWriter(writer); + let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; + let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + while let Ok(size) = reader.read(&mut src_buf).await { + if size > 0 { + let parsed_packet = + match rate_limiter.as_ref().verify_packet(Some(addr.ip()), &src_buf[..size], &mut dst_buf) { + Ok(packet) => packet, + Err(TunnResult::WriteToNetwork(cookie)) => { + match &mut writer { + WriterState::PureWriter(writer) => { + let _ = writer.write_all(cookie).await; + }, + WriterState::PeerWriter(peer)=> { + let mut p = peer.lock().await; + if let Some(w) = &mut p.endpoint.tcp_conn { + let _ = w.write_all(cookie).await; + }else { + tracing::warn!("should not come here"); } } - continue; - } - Err(_) => continue, - }; - let peer = match &parsed_packet { - Packet::HandshakeInit(p) => { - if let Ok(hh) = parse_handshake_anon(private_key, public_key, p) { - let by_key = &peers.read().await.by_key; - by_key.get(&x25519_dalek::PublicKey::from(hh.peer_static_public)).map(Arc::clone) - } else { - None } + continue; } - Packet::HandshakeResponse(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - Packet::PacketCookieReply(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - Packet::PacketData(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), - }; - let peer = match peer { - None => continue, - Some(peer) => peer, + Err(_) => continue, }; - - let mut p = peer.lock().await; - if p.endpoint.tcp_conn.is_none() { - if let WriterState::PureWriter(_) = &mut writer { - let pure_writer = mem::replace(&mut writer,WriterState::PeerWriter(peer.clone())); - if let WriterState::PureWriter(_writer) = pure_writer { - p.endpoint.tcp_conn = Some(_writer); - } + let peer = match &parsed_packet { + Packet::HandshakeInit(p) => { + if let Ok(hh) = parse_handshake_anon(private_key, public_key, p) { + let by_key = &peers.read().await.by_key; + by_key.get(&x25519_dalek::PublicKey::from(hh.peer_static_public)).map(Arc::clone) + } else { + None } } - // We found a peer, use it to decapsulate the message+ - let mut flush = false; // Are there packets to send from the queue? - match p - .tunnel - .handle_verified_packet(parsed_packet, &mut dst_buf[..]) - { - TunnResult::Done => {} - TunnResult::Err(_) => continue, - TunnResult::WriteToNetwork(packet) => { - flush = true; - if let Some(conn) = &mut p.endpoint.tcp_conn { - let _ = conn.write_all(packet).await; - } + Packet::HandshakeResponse(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketCookieReply(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + Packet::PacketData(p) => peers.read().await.by_idx.get(&(p.receiver_idx >> 8)).map(Arc::clone), + }; + let peer = match peer { + None => continue, + Some(peer) => peer, + }; + + let mut p = peer.lock().await; + if p.endpoint.tcp_conn.is_none() { + if let WriterState::PureWriter(_) = &mut writer { + let pure_writer = mem::replace(&mut writer,WriterState::PeerWriter(peer.clone())); + if let WriterState::PureWriter(_writer) = pure_writer { + p.endpoint.tcp_conn = Some(_writer); + } + } + } + // We found a peer, use it to decapsulate the message+ + let mut flush = false; // Are there packets to send from the queue? + match p + .tunnel + .handle_verified_packet(parsed_packet, &mut dst_buf[..]) + { + TunnResult::Done => {} + TunnResult::Err(_) => continue, + TunnResult::WriteToNetwork(packet) => { + flush = true; + if let Some(conn) = &mut p.endpoint.tcp_conn { + let _ = conn.write_all(packet).await; } - TunnResult::WriteToTunnelV4(packet, addr) => { - // tracing::debug!("{addr:?}"); - if p.is_allowed_ip(addr) { - if pi { - let mut buf: Vec = Vec::new(); - buf.put_slice(&IP4_HEADER); - buf.put_slice(&packet); - cfg_if! { + } + TunnResult::WriteToTunnelV4(packet, addr) => { + // tracing::debug!("{addr:?}"); + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP4_HEADER); + buf.put_slice(&packet); + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&buf); } else { let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&packet); } else { let _ = iface.lock().await.write(&packet).await; } } - } - } else {} - } - TunnResult::WriteToTunnelV6(packet, addr) => { - if p.is_allowed_ip(addr) { - if pi { - let mut buf: Vec = Vec::new(); - buf.put_slice(&IP6_HEADER); - buf.put_slice(&packet); - cfg_if! { + } + } else {} + } + TunnResult::WriteToTunnelV6(packet, addr) => { + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP6_HEADER); + buf.put_slice(&packet); + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&buf); } else { let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(packet); } else { let _ = iface.lock().await.write(packet).await; } } - }; - } + }; } - }; + } + }; - if flush { - // Flush pending queue - while let TunnResult::WriteToNetwork(packet) = - p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) - { - if let Some(conn) = &mut p.endpoint.tcp_conn { - let _ = conn.write_all(packet).await; - } + if flush { + // Flush pending queue + while let TunnResult::WriteToNetwork(packet) = + p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) + { + if let Some(conn) = &mut p.endpoint.tcp_conn { + let _ = conn.write_all(packet).await; } } } - } - tracing::info!("tcp: {addr:?} close"); - }); - } - Ok(()) + + } + tracing::info!("tcp: {addr:?} close"); + }); } From 9495735db41b47729b6a7503321070a4f79f40ad Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 30 May 2023 22:43:25 +0800 Subject: [PATCH 16/39] bak --- client/lib/src/device/mod.rs | 66 +++++++++++++++------------- client/lib/src/device/unix_device.rs | 18 ++++---- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 9a5f4b7..a24b936 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -34,7 +34,7 @@ use tokio::net::{TcpListener, TcpStream, UdpSocket}; use tokio::sync::{Mutex, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep -use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf, ReadHalf, WriteHalf}; +use tokio::net::tcp::OwnedWriteHalf; use allowed_ips::AllowedIps; use peer::{AllowedIP, Peer}; @@ -338,7 +338,11 @@ pub async fn tcp_peers_timer(peers: &Arc>) { for peer in peer_map.values() { let mut p = peer.lock().await; //TODO: if needs to create tcp when p.endpoint().addr.is_some() - if p.endpoint().tcp_conn.is_none() { + if p.endpoint.tcp_conn.is_none() { + if let Some(addr) = &p.endpoint.addr { + + + } continue } match p.update_timers(&mut dst_buf) { @@ -596,58 +600,60 @@ pub fn tcp_handler( let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&packet); } else { let _ = iface.lock().await.write(&packet).await; } } - } - } else {} - } - TunnResult::WriteToTunnelV6(packet, addr) => { - if p.is_allowed_ip(addr) { - if pi { - let mut buf: Vec = Vec::new(); - buf.put_slice(&IP6_HEADER); - buf.put_slice(&packet); - cfg_if! { + } + } else {} + } + TunnResult::WriteToTunnelV6(packet, addr) => { + if p.is_allowed_ip(addr) { + if pi { + let mut buf: Vec = Vec::new(); + buf.put_slice(&IP6_HEADER); + buf.put_slice(&packet); + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(&buf); } else { let _ = iface.lock().await.write(&buf).await; } } - } else { - cfg_if! { + } else { + cfg_if! { if #[cfg(target_os="windows")] { let _ = iface.lock().await.write(packet); } else { let _ = iface.lock().await.write(packet).await; } } - }; + }; + } } - } - }; + }; - if flush { - // Flush pending queue - while let TunnResult::WriteToNetwork(packet) = - p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) - { - if let Some(conn) = &mut p.endpoint.tcp_conn { - let _ = conn.write_all(packet).await; + if flush { + // Flush pending queue + while let TunnResult::WriteToNetwork(packet) = + p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) + { + if let Some(conn) = &mut p.endpoint.tcp_conn { + let _ = conn.write_all(packet).await; + } } } } - } - } - tracing::info!("tcp: {addr:?} close"); - }); + } + tracing::info!("tcp: {addr:?} close"); + }); + } + Ok(()) } diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 0b05d13..91ba120 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -39,10 +39,14 @@ impl Device { let (mut iface_reader, iface_writer,pi, name) = create_async_tun(name, mtu, address)?; tracing::debug!("finish to create tun"); let iface_writer = Arc::new(Mutex::new(iface_writer)); - let rate_limiter = Arc::new(RateLimiter::new(&key_pair.1, HANDSHAKE_RATE_LIMIT)); let peers: Arc> = Arc::new(RwLock::new(Peers::default())); + let mut tun_src_buf: Vec = vec![0; MAX_UDP_SIZE]; + let mut tun_dst_buf: Vec = vec![0; MAX_UDP_SIZE]; + let key_pair1 = key_pair.clone(); + let peers1 = peers.clone(); + // create tcp/udp server let (port,task) = match protocol { Protocol::Udp => { @@ -51,10 +55,6 @@ impl Device { let udp6 = create_udp_socket(Some(port), Domain::IPV6, None)?; - let mut tun_src_buf: Vec = vec![0; MAX_UDP_SIZE]; - let mut tun_dst_buf: Vec = vec![0; MAX_UDP_SIZE]; - let key_pair1 = key_pair.clone(); - let peers1 = peers.clone(); let task:JoinHandle<()> = tokio::spawn(async move { loop { @@ -71,9 +71,8 @@ impl Device { device::tun_read_handle(&peers, &udp4, &udp6, src_buf, &mut tun_dst_buf).await; } // udp listen - _ = device::udp_handler(&udp4, &key_pair,&rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, - _ = device::udp_handler(&udp6, &key_pair,&rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, - + _ = device::udp_handler(&udp4, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, + _ = device::udp_handler(&udp6, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, } } @@ -98,7 +97,8 @@ impl Device { }; device::tun_read_tcp_handle(&peers, src_buf, &mut tun_dst_buf).await; } - //_ = device::tcp_handler(&key_pair,&rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} + _ = device::tcp_handler(&tcp4, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} + _ = device::tcp_handler(&tcp6, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} } } }); From 1a1316660c7d89a414fc8d1df7c057c1178f5d39 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 30 May 2023 23:04:21 +0800 Subject: [PATCH 17/39] bak it --- client/lib/src/device/mod.rs | 6 ++---- client/lib/src/device/unix_device.rs | 10 ++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index a24b936..798e640 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -490,7 +490,7 @@ enum WriterState { } pub async fn tcp_listener_handler( - listener:TcpListener, + listener: &TcpListener, key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, rate_limiter: Arc, peers: Arc>, @@ -648,15 +648,13 @@ pub fn tcp_handler( } } } - } tracing::info!("tcp: {addr:?} close"); }); - } - Ok(()) } + pub struct Peers { pub by_key: HashMap>>, pub by_ip: AllowedIps>>, diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 91ba120..81de621 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -71,8 +71,8 @@ impl Device { device::tun_read_handle(&peers, &udp4, &udp6, src_buf, &mut tun_dst_buf).await; } // udp listen - _ = device::udp_handler(&udp4, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, - _ = device::udp_handler(&udp6, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, + _ = device::udp_handler(&udp4, &key_pair, rate_limiter.as_ref(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, + _ = device::udp_handler(&udp6, &key_pair, rate_limiter.as_ref(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => break, } } @@ -83,6 +83,8 @@ impl Device { let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; let port = tcp4.local_addr()?.port(); let tcp6 = create_tcp_server(Some(port), Domain::IPV6, None)?; + let key_pair = Arc::new(key_pair); + let task:JoinHandle<()> = tokio::spawn(async move { loop { tokio::select! { @@ -97,8 +99,8 @@ impl Device { }; device::tun_read_tcp_handle(&peers, src_buf, &mut tun_dst_buf).await; } - _ = device::tcp_handler(&tcp4, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} - _ = device::tcp_handler(&tcp6, &key_pair, &rate_limiter, Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} + _ = device::tcp_listener_handler(&tcp4, key_pair.clone(), rate_limiter.clone(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} + _ = device::tcp_listener_handler(&tcp6, key_pair.clone(), rate_limiter.clone(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} } } }); From dff21282035588ac1af0a64b012610d1c48818ce Mon Sep 17 00:00:00 2001 From: timzaak Date: Wed, 31 May 2023 18:05:16 +0800 Subject: [PATCH 18/39] bak it --- .../scala/com/timzaak/fornet/dao/Node.scala | 25 +++++++++++++------ .../fornet/grpc/convert/EntityConvert.scala | 7 +++--- client/lib/src/device/mod.rs | 11 ++++---- client/lib/src/device/peer.rs | 14 +++++++++++ client/lib/src/device/tunnel.rs | 4 +-- client/lib/src/wr_manager.rs | 6 ++++- protobuf/config.proto | 2 ++ 7 files changed, 50 insertions(+), 19 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala index 45d8e3b..90e39ce 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala @@ -5,7 +5,7 @@ import very.util.persistence.quill.DBSerializer import zio.json.* import java.time.OffsetDateTime -import scala.util.{Failure, Success, Try} +import scala.util.{ Failure, Success, Try } enum NodeType { // Normal: Fornet Client @@ -75,21 +75,28 @@ case class Node( case NodeType.Client => s"$ip/${network.addressRange.split('/').last}" } } - - def realStatus(networkStatus:NetworkStatus):NodeStatus = { - if(networkStatus == NetworkStatus.Delete) { + + def realStatus(networkStatus: NetworkStatus): NodeStatus = { + if (networkStatus == NetworkStatus.Delete) { NodeStatus.Delete } else { status } } - def peerAddress: String = { + def peerAllowedIp: String = { nodeType match { case NodeType.Relay => ip case NodeType.Client => s"$ip/32" } } + + def peerAddress: String = { + nodeType match { + case NodeType.Relay => ip.split("/").head + case NodeType.Client => ip + } + } } object Node { @@ -116,7 +123,7 @@ import io.getquill.* class NodeDao(using quill: DB) { - import quill.{*, given} + import quill.{ *, given } def findIdByPublicKey(publicKey: String, networkId: Int): Option[Int] = quill.run { @@ -167,9 +174,11 @@ class NodeDao(using quill: DB) { } } - def getAllAvailableNodes(networkId:Int):Seq[Node] = quill.run { + def getAllAvailableNodes(networkId: Int): Seq[Node] = quill.run { quote { - query[Node].filter(n => n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal)) + query[Node].filter(n => + n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal) + ) } } def getAllAvailableNodes( diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala index 2b02151..6801306 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala @@ -1,7 +1,7 @@ package com.timzaak.fornet.grpc.convert -import com.timzaak.fornet.dao.{Network, Node} -import com.timzaak.fornet.protobuf.config.{Interface, Peer, WRConfig} +import com.timzaak.fornet.dao.{ Network, Node } +import com.timzaak.fornet.protobuf.config.{ Interface, Peer, WRConfig } object EntityConvert { @@ -19,8 +19,9 @@ object EntityConvert { endpoint = nodeSetting.endpoint.map(v => s"$v:${nodeSetting.port.getOrElse(defaultPort)}" ), - allowedIp = Seq(node.peerAddress), + allowedIp = Seq(node.peerAllowedIp), publicKey = node.publicKey, + address = Seq(node.peerAddress), persistenceKeepAlive = nodeSetting.keepAlive.getOrElse(defaultKeepAlive), ) } diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 798e640..b97714f 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -21,7 +21,7 @@ use std::mem; use cfg_if::cfg_if; use rand::RngCore; use rand::rngs::OsRng; -use std::net::SocketAddr; +use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; use std::time::Duration; use anyhow::anyhow; @@ -181,6 +181,7 @@ impl DeviceData { endpoint: Option, allowed_ips: &[AllowedIP], keepalive: Option, + ip: IpAddr, preshared_key: Option<[u8; 32]>, ) { // Update an existing peer @@ -202,7 +203,7 @@ impl DeviceData { ) .unwrap(); - let peer = Peer::new(tunn, next_index, endpoint, allowed_ips, preshared_key); + let peer = Peer::new(tunn, next_index, endpoint, allowed_ips, ip, preshared_key); let peer = Arc::new(Mutex::new(peer)); let mut peers = self.peers.write().await; @@ -338,9 +339,9 @@ pub async fn tcp_peers_timer(peers: &Arc>) { for peer in peer_map.values() { let mut p = peer.lock().await; //TODO: if needs to create tcp when p.endpoint().addr.is_some() - if p.endpoint.tcp_conn.is_none() { + if p.endpoint.tcp_conn.is_none(){ if let Some(addr) = &p.endpoint.addr { - + } continue @@ -508,7 +509,7 @@ pub async fn tcp_listener_handler( //Ok(()) } pub fn tcp_handler( - mut socket: TcpStream, + socket: TcpStream, addr: SocketAddr, key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, rate_limiter: Arc, diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index 648f668..32b3922 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -29,6 +29,7 @@ pub struct Peer { index: u32, pub endpoint: Endpoint, allowed_ips: AllowedIps<()>, + ip: IpAddr, preshared_key: Option<[u8; 32]>, } @@ -68,6 +69,7 @@ impl Peer { index: u32, endpoint: Option, allowed_ips: &[AllowedIP], + ip:IpAddr, preshared_key: Option<[u8; 32]>, ) -> Peer { Peer { @@ -78,6 +80,7 @@ impl Peer { udp_conn: None, tcp_conn: None, }, + ip, allowed_ips: allowed_ips.iter().map(|ip| (ip, ())).collect(), preshared_key, } @@ -137,6 +140,7 @@ impl Peer { #[cfg(test)] mod tests { + use std::net::{IpAddr, SocketAddr}; use crate::device::peer::AllowedIP; #[test] @@ -145,4 +149,14 @@ mod tests { assert_eq!(ip_v4.to_string(), String::from("10.0.0.0/32")); assert_eq!(ip_v4.addr.to_string(), String::from("10.0.0.0")); } + + #[test] + fn ip_compare() { + let ip = "10.0.0.1".parse::(); + //println!("123 {:?}", ip); + let ip1:IpAddr = "10.0.0.1".parse().unwrap(); + let ip2:IpAddr = "10.0.0.2".parse().unwrap(); + println!("should be false {}", ip1 == ip2); + println!("should be true {}", ip1 < ip2); + } } diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs index 0b8dea7..ef8229e 100644 --- a/client/lib/src/device/tunnel.rs +++ b/client/lib/src/device/tunnel.rs @@ -61,8 +61,8 @@ mod test { #[tokio::test] async fn test_tcp_bind() { - let ip4_server = create_tcp_server(None, Domain::IPV4, None).await.unwrap(); - let ip6_server = create_tcp_server(Some(ip4_server.local_addr().unwrap().port()), Domain::IPV6, None).await.unwrap(); + let ip4_server = create_tcp_server(None, Domain::IPV4, None).unwrap(); + let ip6_server = create_tcp_server(Some(ip4_server.local_addr().unwrap().port()), Domain::IPV6, None).unwrap(); println!("init ip4/ip6 in same port ok"); } diff --git a/client/lib/src/wr_manager.rs b/client/lib/src/wr_manager.rs index 1541657..7a4c95f 100644 --- a/client/lib/src/wr_manager.rs +++ b/client/lib/src/wr_manager.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; use std::time::Duration; use anyhow::anyhow; @@ -34,6 +34,7 @@ impl WRManager { pub_key: x25519_dalek::PublicKey, endpoint: Option, allowed_ips: &[AllowedIP], + ip:IpAddr, keepalive: Option) { if let Some(device) = &mut self.device { device.update_peer( @@ -42,6 +43,7 @@ impl WRManager { endpoint, allowed_ips, keepalive, + ip, None, ).await; } else { @@ -83,10 +85,12 @@ impl WRManager { let (x_pub_key,_) = Identity::get_pub_identity_from_base64(&peer.public_key)?; let endpoint = peer.endpoint.map(|v| SocketAddr::from_str(&v).unwrap()); let allowed_ip:Vec = peer.allowed_ip.into_iter().map(|ip| AllowedIP::from_str(&ip).unwrap()).collect(); + let ip:IpAddr = peer.address.first().unwrap().parse().unwrap(); self.add_peer( x_pub_key, endpoint, allowed_ip.as_slice(), + ip, Some(peer.persistence_keep_alive as u16), ).await; tracing::debug!("peer: {} join network", peer.public_key); diff --git a/protobuf/config.proto b/protobuf/config.proto index ac61672..58710e5 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -30,6 +30,8 @@ message Peer { repeated string allowed_ip = 2; string public_key = 3; uint32 persistence_keep_alive = 4; + // This is for tcp + repeated string address = 5; } From ab40b03883f87bc149c228b4e16128f116f98b34 Mon Sep 17 00:00:00 2001 From: timzaak Date: Sun, 4 Jun 2023 21:56:27 +0800 Subject: [PATCH 19/39] bak it --- client/lib/src/device/mod.rs | 15 +++++++++++++-- client/lib/src/device/peer.rs | 5 +++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index b97714f..b5ba67d 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -329,7 +329,7 @@ pub async fn peers_timer(peers: &Arc>, udp4: &UdpSocket, udp6: &Ud } } -pub async fn tcp_peers_timer(peers: &Arc>) { +pub async fn tcp_peers_timer(ip: &IpAddr, peers: &Arc>) { let mut interval = time::interval(Duration::from_millis(250)); let mut dst_buf: Vec= vec![0; MAX_UDP_SIZE]; @@ -341,7 +341,18 @@ pub async fn tcp_peers_timer(peers: &Arc>) { //TODO: if needs to create tcp when p.endpoint().addr.is_some() if p.endpoint.tcp_conn.is_none(){ if let Some(addr) = &p.endpoint.addr { - + if ip < &p.ip { + match TcpStream::connect(addr).await { + Ok(connection) => { + //let (reader, writer) = connection.into_split(); + //tcp_handler(); + //p.endpoint.tcp_conn = Some(writer) + } + Err(e) => { + tracing::debug!("try to tcp connect {addr} fail: {}", e) + } + } + } } continue diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index 32b3922..d32efe8 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -15,11 +15,12 @@ use tokio::net::tcp::OwnedWriteHalf; use crate::device::allowed_ips::AllowedIps; + #[derive(Default, Debug)] pub struct Endpoint { pub addr: Option, pub udp_conn: Option>, - pub tcp_conn: Option + pub tcp_conn: Option// Nothing, Connecting, Connected(OwnedWriteHalf), ConnectedFailure(Error) } pub struct Peer { @@ -29,7 +30,7 @@ pub struct Peer { index: u32, pub endpoint: Endpoint, allowed_ips: AllowedIps<()>, - ip: IpAddr, + pub ip: IpAddr, preshared_key: Option<[u8; 32]>, } From 7b8590974889b6ec8505e10eeff99504da899847 Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 5 Jun 2023 16:47:16 +0800 Subject: [PATCH 20/39] bak it --- client/lib/src/device/mod.rs | 76 ++++++++++++++++++++++------------- client/lib/src/device/peer.rs | 24 ++++++----- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index b5ba67d..0c4df2b 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -22,8 +22,8 @@ use cfg_if::cfg_if; use rand::RngCore; use rand::rngs::OsRng; use std::net::{IpAddr, SocketAddr}; -use std::sync::Arc; -use std::time::Duration; +use std::sync::{Arc}; +use std::time::{Duration, SystemTime}; use anyhow::anyhow; use boringtun::noise::errors::WireGuardError; use boringtun::noise::rate_limiter::RateLimiter; @@ -31,7 +31,7 @@ use boringtun::noise::{Packet, Tunn, TunnResult}; use boringtun::noise::handshake::parse_handshake_anon; use prost::bytes::BufMut; use tokio::net::{TcpListener, TcpStream, UdpSocket}; -use tokio::sync::{Mutex, RwLock}; +use tokio::sync::{Mutex,MutexGuard, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep use tokio::net::tcp::OwnedWriteHalf; @@ -39,6 +39,7 @@ use tokio::net::tcp::OwnedWriteHalf; use allowed_ips::AllowedIps; use peer::{AllowedIP, Peer}; use script_run::Scripts; +use crate::device::peer::TcpConnection; use crate::device::script_run::run_opt_script; use self::tun::WritePart; @@ -276,7 +277,8 @@ pub async fn tun_read_tcp_handle(peers: &Arc>, src_buf: &[u8], dst } TunnResult::WriteToNetwork(packet) => { let endpoint = peer.endpoint(); - if let Some(conn) = &mut endpoint.tcp_conn { + if let TcpConnection::Connected(conn) = &mut endpoint.tcp_conn { + //TODO: error detect let _ = conn.write_all(packet).await; } else { tracing::info!("no endpoint of {:?}", endpoint.addr); @@ -329,34 +331,49 @@ pub async fn peers_timer(peers: &Arc>, udp4: &UdpSocket, udp6: &Ud } } -pub async fn tcp_peers_timer(ip: &IpAddr, peers: &Arc>) { +pub async fn tcp_peers_timer( + ip: &IpAddr, + peers: &Arc>, + key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, + rate_limiter: Arc, + iface: Arc>, + pi: bool) { let mut interval = time::interval(Duration::from_millis(250)); let mut dst_buf: Vec= vec![0; MAX_UDP_SIZE]; + loop { interval.tick().await; let peer_map = &peers.read().await.by_key; for peer in peer_map.values() { let mut p = peer.lock().await; - //TODO: if needs to create tcp when p.endpoint().addr.is_some() - if p.endpoint.tcp_conn.is_none(){ - if let Some(addr) = &p.endpoint.addr { - if ip < &p.ip { - match TcpStream::connect(addr).await { - Ok(connection) => { - //let (reader, writer) = connection.into_split(); - //tcp_handler(); - //p.endpoint.tcp_conn = Some(writer) - } - Err(e) => { - tracing::debug!("try to tcp connect {addr} fail: {}", e) - } + let endpoint_addr = match p.endpoint().addr { + Some(addr) => addr, + None => continue, + }; + match &mut p.endpoint.tcp_conn { + TcpConnection::Nothing| TcpConnection::ConnectedFailure(_) => { + p.endpoint.tcp_conn = TcpConnection::Connecting(SystemTime::now()); + match TcpStream::connect(&endpoint_addr).await { + Ok(conn) => { + tcp_handler(conn, endpoint_addr, key_pair.clone(),rate_limiter.clone(), peers.clone(), iface.clone(), pi); + }, + Err(error) => { + tracing::debug!("connect {endpoint_addr:?} failure, error: {error:?}"); + p.endpoint.tcp_conn = TcpConnection::ConnectedFailure(error) } - } + }; + continue; + } + TcpConnection::Connecting(_) => { + //TODO: add check of time, and reconnect + continue; + } + TcpConnection::Connected(connection) => { + //connection } - continue - } + }; match p.update_timers(&mut dst_buf) { TunnResult::Done => {} TunnResult::Err(WireGuardError::ConnectionExpired) => { @@ -364,12 +381,12 @@ pub async fn tcp_peers_timer(ip: &IpAddr, peers: &Arc>) { } TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), TunnResult::WriteToNetwork(packet) => { - if let Some(conn) = &mut p.endpoint().tcp_conn { - let _ = conn.write_all(packet).await; + if let TcpConnection::Connected(connection) = &mut p.endpoint.tcp_conn { + let _ = connection.write_all(packet).await; } } - _ => panic!("Unexpected result from update_timers"), + _ => tracing::warn!("Unexpected result from update_timers"), }; } } @@ -546,7 +563,7 @@ pub fn tcp_handler( }, WriterState::PeerWriter(peer)=> { let mut p = peer.lock().await; - if let Some(w) = &mut p.endpoint.tcp_conn { + if let TcpConnection::Connected(w) = &mut p.endpoint.tcp_conn { let _ = w.write_all(cookie).await; }else { tracing::warn!("should not come here"); @@ -576,11 +593,11 @@ pub fn tcp_handler( }; let mut p = peer.lock().await; - if p.endpoint.tcp_conn.is_none() { + if let TcpConnection::Nothing | TcpConnection::ConnectedFailure(_) = p.endpoint.tcp_conn { if let WriterState::PureWriter(_) = &mut writer { let pure_writer = mem::replace(&mut writer,WriterState::PeerWriter(peer.clone())); if let WriterState::PureWriter(_writer) = pure_writer { - p.endpoint.tcp_conn = Some(_writer); + p.endpoint.tcp_conn = TcpConnection::Connected(_writer); } } } @@ -594,7 +611,8 @@ pub fn tcp_handler( TunnResult::Err(_) => continue, TunnResult::WriteToNetwork(packet) => { flush = true; - if let Some(conn) = &mut p.endpoint.tcp_conn { + + if let TcpConnection::Connected(conn) = &mut p.endpoint.tcp_conn { let _ = conn.write_all(packet).await; } } @@ -654,7 +672,7 @@ pub fn tcp_handler( while let TunnResult::WriteToNetwork(packet) = p.tunnel.decapsulate(None, &[], &mut dst_buf[..]) { - if let Some(conn) = &mut p.endpoint.tcp_conn { + if let TcpConnection::Connected(conn) = &mut p.endpoint.tcp_conn { let _ = conn.write_all(packet).await; } } diff --git a/client/lib/src/device/peer.rs b/client/lib/src/device/peer.rs index d32efe8..a77e038 100644 --- a/client/lib/src/device/peer.rs +++ b/client/lib/src/device/peer.rs @@ -8,19 +8,25 @@ use std::net::{IpAddr}; use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; +use std::time::SystemTime; use boringtun::noise::{Tunn, TunnResult}; use tokio::net::{UdpSocket}; use tokio::net::tcp::OwnedWriteHalf; use crate::device::allowed_ips::AllowedIps; - - -#[derive(Default, Debug)] +#[derive(Debug)] +pub enum TcpConnection { + Nothing, + Connecting(SystemTime), + Connected(OwnedWriteHalf), + ConnectedFailure(std::io::Error) +} +#[derive(Debug)] pub struct Endpoint { pub addr: Option, pub udp_conn: Option>, - pub tcp_conn: Option// Nothing, Connecting, Connected(OwnedWriteHalf), ConnectedFailure(Error) + pub tcp_conn: TcpConnection, } pub struct Peer { @@ -79,7 +85,7 @@ impl Peer { endpoint: Endpoint { addr: endpoint, udp_conn: None, - tcp_conn: None, + tcp_conn: TcpConnection::Nothing, }, ip, allowed_ips: allowed_ips.iter().map(|ip| (ip, ())).collect(), @@ -96,13 +102,13 @@ impl Peer { } pub fn shutdown_endpoint(&mut self) { - if let Some(_) = self.endpoint.udp_conn.take() { + if let Some(_) = &mut self.endpoint.udp_conn.take() { tracing::info!("disconnecting from endpoint"); - //drop(conn) } - if let Some(_) = self.endpoint.tcp_conn.take() { - tracing::info!("disconnecting from endpoint"); + if let TcpConnection::Connected(_) = &mut self.endpoint.tcp_conn { + tracing::info!("disconnecting tcp connection"); } + self.endpoint.tcp_conn = TcpConnection::Nothing; } pub fn set_endpoint(&mut self, addr: SocketAddr) { From 1146a566727ad3c0140d330d2e4ffce6002e4ed5 Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 6 Jun 2023 13:48:33 +0800 Subject: [PATCH 21/39] [backend]refact pagination --- .../scala/com/timzaak/fornet/dao/DB.scala | 36 ++++--------------- .../scala/very/util/entity/Pagination.scala | 9 +++++ .../util/persistence/quill/PageSupport.scala | 33 +++++++++++++++++ .../very/util/web/PaginationSupport.scala | 16 +++------ 4 files changed, 52 insertions(+), 42 deletions(-) create mode 100644 backend/src/main/scala/very/util/entity/Pagination.scala create mode 100644 backend/src/main/scala/very/util/persistence/quill/PageSupport.scala diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala index 510e3c6..77e7bcd 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala @@ -2,19 +2,16 @@ package com.timzaak.fornet.dao import com.timzaak.fornet.dao.Network import io.getquill.* -import io.getquill.context.jdbc.{Decoders, Encoders} -import very.util.persistence.quill.IDSupport +import io.getquill.context.jdbc.{ Decoders, Encoders } +import very.util.persistence.quill.{ IDSupport, PageSupport, ZIOJsonSupport } //import org.json4s.Extraction //import org.json4s.JsonAST.JValue -import very.util.persistence.quill.ZIOJsonSupport -import very.util.web.Pagination +import very.util.entity.Pagination -import java.time.{LocalDateTime, OffsetDateTime} +import java.time.{ LocalDateTime, OffsetDateTime } import java.util.Calendar -class DB - extends PostgresJdbcContext(SnakeCase, "database") - with ZIOJsonSupport with IDSupport { +class DB extends PostgresJdbcContext(SnakeCase, "database") with ZIOJsonSupport with IDSupport with PageSupport[SnakeCase] { given encodeOffsetDateTime: Encoder[OffsetDateTime] = encoder( @@ -66,28 +63,7 @@ class DB MappedEncoding(_.ordinal) given decodeNetworkStatus: MappedEncoding[Int, NetworkStatus] = MappedEncoding(NetworkStatus.fromOrdinal) - - extension [T](inline q: Query[T]) { - inline def page(using pagination: Pagination) = { - q.drop(lift(pagination.offset)).take(lift(pagination.pageSize)) - } - - // warning: sortBy should be split, because PG would report error for count(*) - inline def pageWithCount(using pagination: Pagination) = { - (this.run(quote(q.page)), this.run(quote(q.size))) - } - - inline def pageWithCount( - sort: Query[T] => Query[T] - )(using pagination: Pagination) = { - (this.run(quote(sort(q).page)), this.run(quote(q.size))) - } - - // inline def pageWithPram(param:T => Boolean)(using pagination:Pagination) = { -// q.filter(param).page -// } - inline def single = q.take(1) - } + } //@main def testQuill = { diff --git a/backend/src/main/scala/very/util/entity/Pagination.scala b/backend/src/main/scala/very/util/entity/Pagination.scala new file mode 100644 index 0000000..fd99531 --- /dev/null +++ b/backend/src/main/scala/very/util/entity/Pagination.scala @@ -0,0 +1,9 @@ +package very.util.entity + +case class Pagination(page: Int, pageSize: Int) { + assert(pageSize <= 50) + assert(page > 0) + def offset: Int = (page - 1) * pageSize + def limit: Int = pageSize + +} diff --git a/backend/src/main/scala/very/util/persistence/quill/PageSupport.scala b/backend/src/main/scala/very/util/persistence/quill/PageSupport.scala new file mode 100644 index 0000000..d7d70fd --- /dev/null +++ b/backend/src/main/scala/very/util/persistence/quill/PageSupport.scala @@ -0,0 +1,33 @@ +package very.util.persistence.quill + +import io.getquill.context.jdbc.JdbcContextTypes +import io.getquill.* +import very.util.entity.Pagination + +trait PageSupport[+N <: NamingStrategy] { + this: PostgresJdbcContext[N] => + + + extension[T] (inline q: Query[T]) { + inline def page(using pagination: Pagination) = { + q.drop(lift(pagination.offset)).take(lift(pagination.pageSize)) + } + + // warning: sortBy should be split, because PG would report error for count(*) + inline def pageWithCount(using pagination: Pagination) = { + (this.run(quote(q.page)), this.run(quote(q.size))) + } + + inline def pageWithCount( + sort: Query[T] => Query[T] + )(using pagination: Pagination) = { + (this.run(quote(sort(q).page)), this.run(quote(q.size))) + } + + // inline def pageWithPram(param:T => Boolean)(using pagination:Pagination) = { + // q.filter(param).page + // } + inline def single = q.take(1) + } + +} diff --git a/backend/src/main/scala/very/util/web/PaginationSupport.scala b/backend/src/main/scala/very/util/web/PaginationSupport.scala index 8594be8..e19b04d 100644 --- a/backend/src/main/scala/very/util/web/PaginationSupport.scala +++ b/backend/src/main/scala/very/util/web/PaginationSupport.scala @@ -1,14 +1,6 @@ package very.util.web -import com.timzaak.fornet.dao.DB - -case class Pagination(page: Int, pageSize: Int) { - assert(pageSize <= 50) - assert(page > 0) - def offset: Int = (page - 1) * pageSize - def limit: Int = pageSize - -} +import very.util.entity.Pagination trait PaginationSupport { this: org.scalatra.ScalatraBase => @@ -19,12 +11,12 @@ trait PaginationSupport { this: org.scalatra.ScalatraBase => given pagination: Pagination = Pagination(page, pageSize) inline def search[T]( - arguments: Map[String, String => T => Boolean] - ): Iterable[T => Boolean] = { + arguments: Map[String, String => T => Boolean] + ): Iterable[T => Boolean] = { for { (k, func) <- arguments value <- params.get(k) if value.nonEmpty } yield func(value) - + } } From db9e19b245cfd3de83489e4b89136eaf6cfa5bb6 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Tue, 6 Jun 2023 22:40:15 +0800 Subject: [PATCH 22/39] bug fix: ID --- admin-web/src/api/authAPI.ts | 4 ++-- admin-web/src/api/http.ts | 5 ++++- admin-web/src/api/networkAPI.ts | 10 +++++----- admin-web/src/api/nodeAPI.ts | 18 +++++++++--------- .../src/view/network/NetworkDetailPage.tsx | 2 +- admin-web/src/view/network/NetworkListPage.tsx | 6 +++--- admin-web/src/view/node/CreateNodePage.tsx | 4 ++-- admin-web/src/view/node/NodeDetailPage.tsx | 6 +++--- admin-web/src/view/node/NodeListPage.tsx | 7 ++++--- .../fornet/controller/NetworkController.scala | 5 ++++- .../main/scala/very/util/web/Controller.scala | 2 ++ .../very/util/web/json/ZIOJsonSupport.scala | 2 +- command/docker/mqtt/run.sh | 2 +- command/docker/proxy/run_dev.sh | 0 command/docker/proxy/run_test.sh | 0 15 files changed, 41 insertions(+), 32 deletions(-) mode change 100644 => 100755 command/docker/mqtt/run.sh mode change 100644 => 100755 command/docker/proxy/run_dev.sh mode change 100644 => 100755 command/docker/proxy/run_test.sh diff --git a/admin-web/src/api/authAPI.ts b/admin-web/src/api/authAPI.ts index 2e4e45b..24b6700 100644 --- a/admin-web/src/api/authAPI.ts +++ b/admin-web/src/api/authAPI.ts @@ -1,4 +1,4 @@ -import http from "./http"; +import http, {ID} from "./http"; export function checkSampleTokenCorrect(token: string) { return http.post('/auth/st/check', { @@ -7,7 +7,7 @@ export function checkSampleTokenCorrect(token: string) { } -export function getSSOInviteCode(networkId:number):Promise { +export function getSSOInviteCode(networkId:ID):Promise { // @ts-ignore return http.get(`/auth/oauth/${networkId}/device_code`, { // @ts-ignore diff --git a/admin-web/src/api/http.ts b/admin-web/src/api/http.ts index ff3f090..29b408c 100644 --- a/admin-web/src/api/http.ts +++ b/admin-web/src/api/http.ts @@ -7,10 +7,13 @@ const instance = axios.create({ timeout: 5 * 1000, }) + +export type ID = string; export interface CreatedSuccess { - id: number + id: ID } + export interface Page { total: number list: T[] diff --git a/admin-web/src/api/networkAPI.ts b/admin-web/src/api/networkAPI.ts index dd3413a..9f51673 100644 --- a/admin-web/src/api/networkAPI.ts +++ b/admin-web/src/api/networkAPI.ts @@ -1,4 +1,4 @@ -import http, {CreatedSuccess, Page} from "./http"; +import http, {CreatedSuccess, ID, Page} from "./http"; export interface NetworkSetting { @@ -9,7 +9,7 @@ export interface NetworkSetting { } export interface Network { - id: number, + id: ID, name: string, setting: NetworkSetting, createdAt: string, @@ -41,15 +41,15 @@ export function updateNetwork(id: number, data: Network) { return http.put(`/network/${id}`, data) } -export function getNetwork(id: number) { +export function getNetwork(id: ID) { return http.get(`/network/${id}`).then(r => r.data) } -export function getNetworkInviteCode(networkId: number) { +export function getNetworkInviteCode(networkId: ID) { return http.get(`/network/${networkId}/invite_code`).then(r => r.data) } -export function deleteNetwork(id:number) { +export function deleteNetwork(id:ID) { return http.delete(`/network/${id}`) } diff --git a/admin-web/src/api/nodeAPI.ts b/admin-web/src/api/nodeAPI.ts index 493a103..a9b358c 100644 --- a/admin-web/src/api/nodeAPI.ts +++ b/admin-web/src/api/nodeAPI.ts @@ -1,4 +1,4 @@ -import http, {CreatedSuccess} from "./http"; +import http, {CreatedSuccess, ID} from "./http"; export enum NodeStatus { @@ -10,11 +10,11 @@ export enum NodeType { } export interface Node { - id: number, + id: ID, nodeType: NodeType, status: NodeStatus, setting: NodeSetting, - networkId: number, + networkId: ID, name: string, } @@ -34,7 +34,7 @@ export interface UpdateNode { setting: NodeSetting, } -export function getNodeList(networkId: number, page: number = 1, pageSize: number = 10) { +export function getNodeList(networkId: ID, page: number = 1, pageSize: number = 10) { return http.get(`/node/${networkId}`, { params: { page, @@ -50,22 +50,22 @@ export interface CreateNode { setting: NodeSetting, } -export function createNode(networkId: number, data: CreateNode) { +export function createNode(networkId: ID, data: CreateNode) { return http.post(`/node/${networkId}`, data) } -export function getNode(networkId: number, nodeId: number) { +export function getNode(networkId: ID, nodeId: ID) { return http.get(`/node/${networkId}/${nodeId}`).then(r => r.data) } -export function updateNode(networkId: number, nodeId: number, data: UpdateNode) { +export function updateNode(networkId: ID, nodeId: ID, data: UpdateNode) { return http.put(`/node/${networkId}/${nodeId}`, data) } -export function getNodeActiveCode(networkId: number, nodeId: number) { +export function getNodeActiveCode(networkId: ID, nodeId: ID) { return http.get(`/node/${networkId}/${nodeId}/active_code`).then(r => r.data) } -export function updateNodeStatus(networkId: number, nodeId: number, status: NodeStatus.Forbid | NodeStatus.Normal) { +export function updateNodeStatus(networkId: ID, nodeId: ID, status: NodeStatus.Forbid | NodeStatus.Normal) { return http.put(`/node/${networkId}/${nodeId}/status`, {status}) } diff --git a/admin-web/src/view/network/NetworkDetailPage.tsx b/admin-web/src/view/network/NetworkDetailPage.tsx index c043c9e..2ceb40c 100644 --- a/admin-web/src/view/network/NetworkDetailPage.tsx +++ b/admin-web/src/view/network/NetworkDetailPage.tsx @@ -12,7 +12,7 @@ export default function NetworkDetailPage() { const [form] = useForm() useEffect(() => { - getNetwork(parseInt(networkId!)).then((d) => { + getNetwork(networkId!).then((d) => { form.setFieldsValue(d) }) }, [networkId, form]) diff --git a/admin-web/src/view/network/NetworkListPage.tsx b/admin-web/src/view/network/NetworkListPage.tsx index 3e07008..f9cd9df 100644 --- a/admin-web/src/view/network/NetworkListPage.tsx +++ b/admin-web/src/view/network/NetworkListPage.tsx @@ -5,7 +5,7 @@ import {deleteNetwork, getNetworkInviteCode, Network, networkList} from "../../a import {useEffect, useState} from "react"; import DayjsFormat from "../../component/DayjsFormat"; import {Link, useSearchParams} from "react-router-dom"; -import {defaultPage, Page} from "../../api/http"; +import {defaultPage, ID, Page} from "../../api/http"; import {QRCodeCanvas} from "qrcode.react"; import copy from "copy-to-clipboard"; import {getPersistenceToken, getSSOInviteCode} from "../../api/authAPI"; @@ -23,13 +23,13 @@ export default function NetworkListPage() { networkList(name, page).then((d) => setData(d)) }, [name, page]) - const showInviteModel = (id: number) => { + const showInviteModel = (id: ID) => { if((getPersistenceToken()??'').startsWith('Bearer')) { getSSOInviteCode(id).then(r => setShowSSO(r)) } getNetworkInviteCode(id).then(setShowModal) } - const deleteNetworkAction = async (id: number) => { + const deleteNetworkAction = async (id: ID) => { await deleteNetwork(id) message.info(intl.formatMessage({id: 'result.deleteSuccess'}, {'0': intl.formatMessage({id: 'nav.network'})})) diff --git a/admin-web/src/view/node/CreateNodePage.tsx b/admin-web/src/view/node/CreateNodePage.tsx index 67452b1..600a5bc 100644 --- a/admin-web/src/view/node/CreateNodePage.tsx +++ b/admin-web/src/view/node/CreateNodePage.tsx @@ -16,7 +16,7 @@ export function CreateNodePage() { const intl = useIntl() useEffect(() => { if (networkId) { - getNetwork(parseInt(networkId)).then(r => setNetwork(r)) + getNetwork(networkId).then(r => setNetwork(r)) } }, [networkId]) @@ -36,7 +36,7 @@ export function CreateNodePage() { delete data.ip } - const {id} = (await createNode(parseInt(networkId as string), data)).data + const {id} = (await createNode(networkId!, data)).data message.info(intl.formatMessage({id: 'result.createSuccess'}, {'0': intl.formatMessage({id: 'nav.node'})})) navi(`/network/${networkId}/node/${id}`, {replace: true}) } diff --git a/admin-web/src/view/node/NodeDetailPage.tsx b/admin-web/src/view/node/NodeDetailPage.tsx index ee09a63..62c016e 100644 --- a/admin-web/src/view/node/NodeDetailPage.tsx +++ b/admin-web/src/view/node/NodeDetailPage.tsx @@ -71,8 +71,8 @@ export default function NodeDetailPage() { useEffect(() => { if (networkId && nodeId) { - const networkIdNum = parseInt(networkId) - Promise.all([getNetwork(networkIdNum), getNode(networkIdNum, parseInt(nodeId))]).then(r => { + //const networkIdNum = parseInt(networkId) + Promise.all([getNetwork(networkId), getNode(networkId, nodeId)]).then(r => { setNetwork(r[0]) form.setFieldsValue(r[1]) }) @@ -91,7 +91,7 @@ export default function NodeDetailPage() { name: data.name, setting: data.setting, } - await updateNode(parseInt(networkId as string), parseInt(nodeId as string), updateData) + await updateNode(networkId!, nodeId!, updateData) message.info(intl.formatMessage({id: 'result.updateSuccess'}, {'0': intl.formatMessage({id: 'nav.node'})})) } diff --git a/admin-web/src/view/node/NodeListPage.tsx b/admin-web/src/view/node/NodeListPage.tsx index a13cad5..da5c5c4 100644 --- a/admin-web/src/view/node/NodeListPage.tsx +++ b/admin-web/src/view/node/NodeListPage.tsx @@ -7,6 +7,7 @@ import {useIntl} from "react-intl"; import {enumToDesc} from "../../local/intl"; import {QRCodeCanvas} from "qrcode.react"; import copy from "copy-to-clipboard"; +import {ID} from "../../api/http"; export default function NodeListPage() { const {networkId} = useParams<{ networkId: string }>() @@ -14,14 +15,14 @@ export default function NodeListPage() { const [data, setData] = useState([]) const intl = useIntl() useEffect(() => { - getNodeList(parseInt(networkId!)).then((d) => setData(d.data)) + getNodeList(networkId!).then((d) => setData(d.data)) }, [networkId]) - const showActiveModalAction = async (networkId: number, nodeId: number) => { + const showActiveModalAction = async (networkId: ID, nodeId: ID) => { const activeCode = await getNodeActiveCode(networkId, nodeId) setShowActiveCode(activeCode) } - const updateNodeStatusAction = async (networkId: number, nodeId: number, status: NodeStatus.Forbid | NodeStatus.Normal) => { + const updateNodeStatusAction = async (networkId: ID, nodeId: ID, status: NodeStatus.Forbid | NodeStatus.Normal) => { await updateNodeStatus(networkId, nodeId, status) message.info(intl.formatMessage({id: 'result.updateSuccess'}, {'0': intl.formatMessage({id: 'status'})})) getNodeList(networkId).then((d) => setData(d.data)) diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala index 6025f7a..ad83167 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NetworkController.scala @@ -55,9 +55,12 @@ trait NetworkController( .filter(_.groupId == lift(groupId)) )(_.sortBy(_.id)(Ord.desc)) case _ => - pageWithCount( + val r= pageWithCount( query[Network].filter(_.status == lift(NetworkStatus.Normal)) )(_.sortBy(_.id)(Ord.desc)) + import zio.json.* + val j = r.toJson + r } } diff --git a/backend/src/main/scala/very/util/web/Controller.scala b/backend/src/main/scala/very/util/web/Controller.scala index ce6c39f..cfb24ef 100644 --- a/backend/src/main/scala/very/util/web/Controller.scala +++ b/backend/src/main/scala/very/util/web/Controller.scala @@ -39,10 +39,12 @@ class Controller //(using val jsonFormats: Formats) super.renderPipeline(info) }: RenderPipeline) orElse super.renderPipeline + /* def created(id: Long): ActionResult = { contentType = formats("json") Created(s"""{"id":$id}""") } + */ def created(id: very.util.security.ID[_]): ActionResult = { contentType = formats("json") Created(s"""{"id":${id.secretId}}""") diff --git a/backend/src/main/scala/very/util/web/json/ZIOJsonSupport.scala b/backend/src/main/scala/very/util/web/json/ZIOJsonSupport.scala index 131283f..399f1d0 100644 --- a/backend/src/main/scala/very/util/web/json/ZIOJsonSupport.scala +++ b/backend/src/main/scala/very/util/web/json/ZIOJsonSupport.scala @@ -98,4 +98,4 @@ given intIDDecoder(using hashId: Hashids): JsonDecoder[IntID] = JsonDecoder[Stri case Failure(_) => Left("Invalid ID") } } -given intIDEncoder(using hashId: Hashids): JsonEncoder[IntID] = JsonEncoder.int.contramap(_.id) +given intIDEncoder: JsonEncoder[IntID] = JsonEncoder.string.contramap(_.secretId) diff --git a/command/docker/mqtt/run.sh b/command/docker/mqtt/run.sh old mode 100644 new mode 100755 index cbaddbb..906e046 --- a/command/docker/mqtt/run.sh +++ b/command/docker/mqtt/run.sh @@ -2,5 +2,5 @@ # 1883(mqtt) 6060(http) 5363(grpc) # docker rm -f mqtt -docker run -d --name mqtt --network=host -v $(pwd)/log:/var/log/rmqtt -v $(pwd)/config/rmqtt.toml:/app/rmqtt/rmqtt.toml -v $(pwd)/config/plugin:/app/rmqtt/rmqtt-plugins rmqtt/rmqtt:latest +docker run -d --name mqtt --network=host -v $(pwd)/log:/var/log/rmqtt -v $(pwd)/config/rmqtt.toml:/app/rmqtt/rmqtt.toml -v $(pwd)/config/plugin:/app/rmqtt/plugin rmqtt/rmqtt:latest # docker logs -f --tail 50 mqtt \ No newline at end of file diff --git a/command/docker/proxy/run_dev.sh b/command/docker/proxy/run_dev.sh old mode 100644 new mode 100755 diff --git a/command/docker/proxy/run_test.sh b/command/docker/proxy/run_test.sh old mode 100644 new mode 100755 From b58207aecb849dff01a5c8d1884f5e5e1bb3626b Mon Sep 17 00:00:00 2001 From: Timzaak Date: Wed, 7 Jun 2023 14:48:24 +0800 Subject: [PATCH 23/39] fix 2, IntId trigger bug --- .../fornet/controller/AuthController.scala | 2 +- .../fornet/controller/NodeController.scala | 2 +- .../main/scala/com/timzaak/fornet/dao/DB.scala | 1 - .../scala/com/timzaak/fornet/dao/Network.scala | 18 +++++++++++++++--- .../com/timzaak/fornet/dao/NetworkDao.scala | 4 ++++ .../scala/com/timzaak/fornet/dao/Node.scala | 2 +- .../main/scala/com/timzaak/fornet/di/DI.scala | 8 +------- .../scala/com/timzaak/fornet/di/DaoDI.scala | 10 ++++++++++ .../fornet/grpc/AuthGRPCController.scala | 10 +++++----- .../fornet/mqtt/MqttCallbackController.scala | 2 +- .../pubsub/NodeChangeNotifyService.scala | 4 ++-- .../util/persistence/quill/IDSupport.scala | 1 - .../persistence/quill/ZIOJsonSupport.scala | 3 +-- .../scala/very/util/practice/JsonSuite.scala | 15 ++++++++++----- 14 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/AuthController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/AuthController.scala index b018164..978e345 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/AuthController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/AuthController.scala @@ -3,7 +3,7 @@ package com.timzaak.fornet.controller import com.google.common.base.Charsets import com.timzaak.fornet.config.AppConfig import com.timzaak.fornet.controller.auth.AppAuthSupport -import com.timzaak.fornet.dao.{ NetworkDao, NetworkStatus } +import com.timzaak.fornet.dao.{NetworkDao, NetworkStatus} import com.timzaak.fornet.di.DI.hashId import com.typesafe.config.Config import org.hashids.Hashids diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala index 10bacf0..8452a5c 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala @@ -1,7 +1,7 @@ package com.timzaak.fornet.controller import com.timzaak.fornet.config.AppConfig -import com.timzaak.fornet.controller.auth.{ AppAuthSupport, User } +import com.timzaak.fornet.controller.auth.{AppAuthSupport, User} import com.timzaak.fornet.dao.* import com.timzaak.fornet.grpc.convert.EntityConvert import com.timzaak.fornet.pubsub.NodeChangeNotifyService diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala index 77e7bcd..86b78ec 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala @@ -1,6 +1,5 @@ package com.timzaak.fornet.dao -import com.timzaak.fornet.dao.Network import io.getquill.* import io.getquill.context.jdbc.{ Decoders, Encoders } import very.util.persistence.quill.{ IDSupport, PageSupport, ZIOJsonSupport } diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala index 079e301..6079509 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Network.scala @@ -35,6 +35,15 @@ enum NetworkProtocol { case UDP => PProtocol.Protocol_UDP } } + + given JsonEncoder[NetworkProtocol] = JsonEncoder[Int].contramap(_.ordinal) + + given JsonDecoder[NetworkProtocol] = JsonDecoder[Int].mapOrFail { e => + Try(NetworkProtocol.fromOrdinal(e)) match { + case Success(v) => Right(v) + case Failure(_) => Left("no matching NodeType enum value") + } + } } object NetworkProtocol { given JsonEncoder[NetworkProtocol] = JsonEncoder[Int].contramap(_.ordinal) @@ -55,7 +64,7 @@ case class Network( setting: NetworkSetting, status: NetworkStatus, createdAt: OffsetDateTime, - updatedAt: OffsetDateTime + updatedAt: OffsetDateTime, ) //object Network { // given networkUpdateMeta:UpdateMeta[Network] = updateMeta[Network](_.id) @@ -76,9 +85,12 @@ object NetworkSetting { given JsonCodec[NetworkSetting] = DeriveJsonCodec.gen } + import io.getquill.* -class NetworkDao(using quill: DB) { - import quill.{ *, given } +import org.hashids.Hashids + +class NetworkDao(using quill: DB, hashIds:Hashids) { + import quill.{*, given} def findById(id: IntID): Option[Network] = { quill.run(quote(query[Network]).filter(_.id == lift(id)).single).headOption diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala b/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala new file mode 100644 index 0000000..0464eb2 --- /dev/null +++ b/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala @@ -0,0 +1,4 @@ +package com.timzaak.fornet.dao + + + diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala index 5b50ff1..d29becc 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala @@ -124,7 +124,7 @@ object NodeSetting { import io.getquill.* -class NodeDao(using quill: DB) { +class NodeDao(using quill: DB, hashids: Hashids) { import quill.{ *, given } diff --git a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala index 806039d..6cae26d 100644 --- a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala +++ b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala @@ -7,18 +7,12 @@ import com.timzaak.fornet.mqtt.MqttCallbackController import com.timzaak.fornet.mqtt.api.RMqttApiClient import com.timzaak.fornet.pubsub.{MqttConnectionManager, NodeChangeNotifyService} import com.timzaak.fornet.service.* -import com.typesafe.config.{Config, ConfigFactory} -import org.hashids.Hashids import very.util.keycloak.{JWKPublicKeyLocator, JWKTokenVerifier, KeycloakJWTAuthStrategy} import very.util.web.auth.{AuthStrategy, AuthStrategyProvider, SingleUserAuthStrategy} object DI extends DaoDI { di => - given config: Config = ConfigFactory.load() - + object appConfig extends AppConfigImpl(config) - object hashId extends Hashids(config.getString("server.hashId"), 5) - given Hashids = hashId - // connection Manager // object connectionManager extends ConnectionManager object connectionManager diff --git a/backend/src/main/scala/com/timzaak/fornet/di/DaoDI.scala b/backend/src/main/scala/com/timzaak/fornet/di/DaoDI.scala index aa01dd5..beedf90 100644 --- a/backend/src/main/scala/com/timzaak/fornet/di/DaoDI.scala +++ b/backend/src/main/scala/com/timzaak/fornet/di/DaoDI.scala @@ -1,12 +1,22 @@ package com.timzaak.fornet.di import com.timzaak.fornet.dao.{DB, NetworkDao, NodeDao} +import com.typesafe.config.{Config, ConfigFactory} +import org.hashids.Hashids + //import org.json4s.Formats trait DaoDI { //given formats: Formats = org.json4s.DefaultFormats + org.json4s.ext.JOffsetDateTimeSerializer + given config: Config = ConfigFactory.load() + + object hashId extends Hashids(config.getString("server.hashId"), 5) + + given Hashids = hashId + + object db extends DB given DB = db diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala index 54a5060..974a0ae 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala @@ -8,7 +8,7 @@ import com.timzaak.fornet.controller.auth.AppAuthStrategyProvider import com.timzaak.fornet.dao.* import com.timzaak.fornet.protobuf.auth.* import com.timzaak.fornet.pubsub.NodeChangeNotifyService -import com.timzaak.fornet.service.{ GRPCAuth, NodeAuthService } +import com.timzaak.fornet.service.{GRPCAuth, NodeAuthService} import com.typesafe.config.Config import com.typesafe.scalalogging.Logger import inet.ipaddr.IPAddress.IPVersion @@ -20,12 +20,12 @@ import very.util.keycloak.KeycloakJWTAuthStrategy import very.util.web.LogSupport import very.util.security.IntID import zio.json.* -import zio.json.ast.{ Json, JsonCursor } +import zio.json.ast.{Json, JsonCursor} import java.net.http.HttpRequest.BodyPublishers -import java.net.http.{ HttpClient, HttpRequest } -import java.net.{ URI, URLEncoder } -import java.time.{ LocalDateTime, OffsetDateTime } +import java.net.http.{HttpClient, HttpRequest} +import java.net.{URI, URLEncoder} +import java.time.{LocalDateTime, OffsetDateTime} import scala.concurrent.Future class AuthGRPCController( diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index 21b3fbb..cd9aa52 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -1,6 +1,6 @@ package com.timzaak.fornet.mqtt -import com.timzaak.fornet.dao.{ DB, NetworkDao, NodeDao, NodeStatus, Network } +import com.timzaak.fornet.dao.{DB, Network, NetworkDao, NodeDao, NodeStatus} import com.timzaak.fornet.entity.PublicKey import com.timzaak.fornet.grpc.convert.EntityConvert import com.timzaak.fornet.mqtt.api.RMqttApiClient diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index dea6603..e97eb29 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -1,8 +1,8 @@ package com.timzaak.fornet.pubsub -import com.timzaak.fornet.dao.* +import com.timzaak.fornet.dao.{NetworkDao, *} import com.timzaak.fornet.grpc.convert.EntityConvert -import com.timzaak.fornet.protobuf.config.{ NetworkStatus as PNetworkStatus, NodeStatus as PNodeStatus, * } +import com.timzaak.fornet.protobuf.config.{NetworkStatus as PNetworkStatus, NodeStatus as PNodeStatus, *} import com.timzaak.fornet.service.NodeService import org.hashids.Hashids import very.util.security.IntID diff --git a/backend/src/main/scala/very/util/persistence/quill/IDSupport.scala b/backend/src/main/scala/very/util/persistence/quill/IDSupport.scala index b619ea4..0f9930e 100644 --- a/backend/src/main/scala/very/util/persistence/quill/IDSupport.scala +++ b/backend/src/main/scala/very/util/persistence/quill/IDSupport.scala @@ -9,7 +9,6 @@ trait IDSupport { this: JdbcContextTypes[PostgresDialect, _] => given intIDEncode: MappedEncoding[IntID, Int] = MappedEncoding(_.id) - given intIDDecode(using hashId: Hashids): MappedEncoding[Int, IntID] = MappedEncoding(IntID.apply) given intIDListEncoder: MappedEncoding[List[IntID], List[Int]] = MappedEncoding(_.map(_.id)) diff --git a/backend/src/main/scala/very/util/persistence/quill/ZIOJsonSupport.scala b/backend/src/main/scala/very/util/persistence/quill/ZIOJsonSupport.scala index fe61f29..3ae8f7b 100644 --- a/backend/src/main/scala/very/util/persistence/quill/ZIOJsonSupport.scala +++ b/backend/src/main/scala/very/util/persistence/quill/ZIOJsonSupport.scala @@ -22,9 +22,8 @@ trait ZIOJsonSupport { } given decodeJsonb[T<:DBSerializer](using JsonDecoder[T]):Decoder[T] = - decoder{(index,row, _) => + decoder{(index,row, session) => val data = row.getString(index) - // println(s"data convert:${data},${data.fromJson[T]}") data.fromJson[T].toOption.get } } diff --git a/backend/src/test/scala/very/util/practice/JsonSuite.scala b/backend/src/test/scala/very/util/practice/JsonSuite.scala index 9961b3d..86394f3 100644 --- a/backend/src/test/scala/very/util/practice/JsonSuite.scala +++ b/backend/src/test/scala/very/util/practice/JsonSuite.scala @@ -3,7 +3,7 @@ package very.util.practice //import com.fasterxml.jackson.annotation.JsonFormat import com.timzaak.fornet.dao.{NetworkSetting, NodeSetting} import munit.FunSuite -import org.json4s.Extraction + import zio.json.* import zio.json.ast.Json @@ -23,10 +23,10 @@ class JsonSuite extends FunSuite { DeriveJsonDecoder.gen[NodeSetting] given JsonEncoder[NodeSetting] = DeriveJsonEncoder.gen[NodeSetting] - test("jsonFormat, encode") { - val a = Extraction.decompose(NodeSetting())(org.json4s.DefaultFormats) - println(a) - } +// test("jsonFormat, encode") { +// val a = Extraction.decompose(NodeSetting())(org.json4s.DefaultFormats) +// println(a) +// } test("zio-json") { val a = NodeSetting(port = Some(1)).toJson @@ -43,6 +43,11 @@ class JsonSuite extends FunSuite { println(a.fromJson[TestEnum]) } + test("json default value") { + val str = """{"mtu": 1420, "port": 51820, "keepAlive": 30}""" + println(s"result: ${str.fromJson[NetworkSetting]}") + } + /* test("jresponse") { val data = PageJResponse(10,List("1","2")) println(data.toJson) From 1a16bb98d3a41cf3c83b27fa9042abe1284ad0b9 Mon Sep 17 00:00:00 2001 From: timzaak Date: Wed, 7 Jun 2023 17:17:50 +0800 Subject: [PATCH 24/39] [admin-web]add network protocol --- admin-web/src/api/networkAPI.ts | 5 +++++ .../src/view/network/CreateNetworkPage.tsx | 17 ++++++++++++++--- .../src/view/network/NetworkDetailPage.tsx | 14 ++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/admin-web/src/api/networkAPI.ts b/admin-web/src/api/networkAPI.ts index 9f51673..afc10ba 100644 --- a/admin-web/src/api/networkAPI.ts +++ b/admin-web/src/api/networkAPI.ts @@ -1,11 +1,15 @@ import http, {CreatedSuccess, ID, Page} from "./http"; +export enum NetworkProtocol { + TCP, UDP +} export interface NetworkSetting { mtu: number, keepAlive: number, dns?: string, port: number, + protocol: NetworkProtocol, } export interface Network { @@ -31,6 +35,7 @@ export function networkList(name: string | null, page: number = 1, pageSize: num export interface CreateNetwork { name: string, addressRange: string, + protocol: NetworkProtocol, } export function createNetwork(data: CreateNetwork) { diff --git a/admin-web/src/view/network/CreateNetworkPage.tsx b/admin-web/src/view/network/CreateNetworkPage.tsx index 1e79ef4..8ef0e6a 100644 --- a/admin-web/src/view/network/CreateNetworkPage.tsx +++ b/admin-web/src/view/network/CreateNetworkPage.tsx @@ -1,9 +1,9 @@ -import {Button, Form, Input, message} from "antd"; +import {Button, Form, Input, message, Select} from "antd"; import {useIntl} from "react-intl"; -import {CreateNetwork, createNetwork} from "../../api/networkAPI"; +import {CreateNetwork, createNetwork, NetworkProtocol} from "../../api/networkAPI"; import {useNavigate} from "react-router-dom"; - +const {Option} = Select; export function CreateNetworkPage() { const [form] = Form.useForm() const navi = useNavigate() @@ -40,6 +40,17 @@ export function CreateNetworkPage() { > + + + diff --git a/admin-web/src/view/network/NetworkDetailPage.tsx b/admin-web/src/view/network/NetworkDetailPage.tsx index 2ceb40c..ce48966 100644 --- a/admin-web/src/view/network/NetworkDetailPage.tsx +++ b/admin-web/src/view/network/NetworkDetailPage.tsx @@ -1,10 +1,11 @@ import {useEffect} from "react"; -import {getNetwork, Network, updateNetwork} from "../../api/networkAPI"; +import {getNetwork, Network, NetworkProtocol, updateNetwork} from "../../api/networkAPI"; import {useParams} from "react-router-dom"; -import {Button, Col, Form, Input, InputNumber, Row} from "antd"; +import {Button, Col, Form, Input, InputNumber, Row, Select} from "antd"; import {useIntl} from "react-intl"; import {useForm} from "antd/es/form/Form"; +const {Option} = Select; export default function NetworkDetailPage() { const {networkId} = useParams<{ networkId: string }>() @@ -62,6 +63,15 @@ export default function NetworkDetailPage() { max={600}/> + + + + +
From 52605dc269e3c3d12bddb05e589b929bc726bbd5 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Wed, 7 Jun 2023 22:50:59 +0800 Subject: [PATCH 25/39] fix 3, IntID bug fix all --- admin-web/src/api/networkAPI.ts | 2 +- admin-web/src/view/network/CreateNetworkPage.tsx | 3 +++ admin-web/src/view/network/NetworkDetailPage.tsx | 2 +- admin-web/src/view/node/NodeDetailPage.tsx | 1 - .../src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala | 4 ---- backend/src/main/scala/com/timzaak/fornet/di/DI.scala | 2 +- backend/src/main/scala/very/util/web/Controller.scala | 2 +- 7 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala diff --git a/admin-web/src/api/networkAPI.ts b/admin-web/src/api/networkAPI.ts index afc10ba..d52c029 100644 --- a/admin-web/src/api/networkAPI.ts +++ b/admin-web/src/api/networkAPI.ts @@ -42,7 +42,7 @@ export function createNetwork(data: CreateNetwork) { return http.post('/network', data) } -export function updateNetwork(id: number, data: Network) { +export function updateNetwork(id: ID, data: Network) { return http.put(`/network/${id}`, data) } diff --git a/admin-web/src/view/network/CreateNetworkPage.tsx b/admin-web/src/view/network/CreateNetworkPage.tsx index 8ef0e6a..6994ef3 100644 --- a/admin-web/src/view/network/CreateNetworkPage.tsx +++ b/admin-web/src/view/network/CreateNetworkPage.tsx @@ -23,6 +23,9 @@ export function CreateNetworkPage() { form={form} labelCol={{span: 8}} wrapperCol={{span: 16}} + initialValues={{ + protocol: NetworkProtocol.UDP + }} > { const data = await form.validateFields() - await updateNetwork(parseInt(networkId!), data) + await updateNetwork(networkId!, data) } return ( <> diff --git a/admin-web/src/view/node/NodeDetailPage.tsx b/admin-web/src/view/node/NodeDetailPage.tsx index 62c016e..9e5ea93 100644 --- a/admin-web/src/view/node/NodeDetailPage.tsx +++ b/admin-web/src/view/node/NodeDetailPage.tsx @@ -71,7 +71,6 @@ export default function NodeDetailPage() { useEffect(() => { if (networkId && nodeId) { - //const networkIdNum = parseInt(networkId) Promise.all([getNetwork(networkId), getNode(networkId, nodeId)]).then(r => { setNetwork(r[0]) form.setFieldsValue(r[1]) diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala b/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala deleted file mode 100644 index 0464eb2..0000000 --- a/backend/src/main/scala/com/timzaak/fornet/dao/NetworkDao.scala +++ /dev/null @@ -1,4 +0,0 @@ -package com.timzaak.fornet.dao - - - diff --git a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala index 6cae26d..450a3c1 100644 --- a/backend/src/main/scala/com/timzaak/fornet/di/DI.scala +++ b/backend/src/main/scala/com/timzaak/fornet/di/DI.scala @@ -10,7 +10,7 @@ import com.timzaak.fornet.service.* import very.util.keycloak.{JWKPublicKeyLocator, JWKTokenVerifier, KeycloakJWTAuthStrategy} import very.util.web.auth.{AuthStrategy, AuthStrategyProvider, SingleUserAuthStrategy} object DI extends DaoDI { di => - + object appConfig extends AppConfigImpl(config) // connection Manager diff --git a/backend/src/main/scala/very/util/web/Controller.scala b/backend/src/main/scala/very/util/web/Controller.scala index cfb24ef..acf9269 100644 --- a/backend/src/main/scala/very/util/web/Controller.scala +++ b/backend/src/main/scala/very/util/web/Controller.scala @@ -47,6 +47,6 @@ class Controller //(using val jsonFormats: Formats) */ def created(id: very.util.security.ID[_]): ActionResult = { contentType = formats("json") - Created(s"""{"id":${id.secretId}}""") + Created(s"""{"id":"${id.secretId}"}""") } } From edcf323eacd46faf00f2888d4c5ac38622ebfbbe Mon Sep 17 00:00:00 2001 From: Timzaak Date: Wed, 7 Jun 2023 23:14:55 +0800 Subject: [PATCH 26/39] client can run now --- client/lib/src/device/mod.rs | 23 ++++++++++++----------- client/lib/src/device/unix_device.rs | 12 ++++++++++-- client/lib/src/wr_manager.rs | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 0c4df2b..e158d7f 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -341,7 +341,6 @@ pub async fn tcp_peers_timer( let mut interval = time::interval(Duration::from_millis(250)); let mut dst_buf: Vec= vec![0; MAX_UDP_SIZE]; - loop { interval.tick().await; let peer_map = &peers.read().await.by_key; @@ -353,16 +352,18 @@ pub async fn tcp_peers_timer( }; match &mut p.endpoint.tcp_conn { TcpConnection::Nothing| TcpConnection::ConnectedFailure(_) => { - p.endpoint.tcp_conn = TcpConnection::Connecting(SystemTime::now()); - match TcpStream::connect(&endpoint_addr).await { - Ok(conn) => { - tcp_handler(conn, endpoint_addr, key_pair.clone(),rate_limiter.clone(), peers.clone(), iface.clone(), pi); - }, - Err(error) => { - tracing::debug!("connect {endpoint_addr:?} failure, error: {error:?}"); - p.endpoint.tcp_conn = TcpConnection::ConnectedFailure(error) - } - }; + if ip < &p.ip { + p.endpoint.tcp_conn = TcpConnection::Connecting(SystemTime::now()); + match TcpStream::connect(&endpoint_addr).await { + Ok(conn) => { + tcp_handler(conn, endpoint_addr, key_pair.clone(), rate_limiter.clone(), peers.clone(), iface.clone(), pi); + }, + Err(error) => { + tracing::debug!("connect {endpoint_addr:?} failure, error: {error:?}"); + p.endpoint.tcp_conn = TcpConnection::ConnectedFailure(error) + } + }; + } continue; } TcpConnection::Connecting(_) => { diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index 81de621..fcafc74 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -37,6 +37,7 @@ impl Device { run_opt_script(&scripts.pre_up)?; tracing::debug!("begin to create tun"); let (mut iface_reader, iface_writer,pi, name) = create_async_tun(name, mtu, address)?; + tracing::debug!("finish to create tun"); let iface_writer = Arc::new(Mutex::new(iface_writer)); let rate_limiter = Arc::new(RateLimiter::new(&key_pair.1, HANDSHAKE_RATE_LIMIT)); @@ -46,7 +47,6 @@ impl Device { let mut tun_dst_buf: Vec = vec![0; MAX_UDP_SIZE]; let key_pair1 = key_pair.clone(); let peers1 = peers.clone(); - // create tcp/udp server let (port,task) = match protocol { Protocol::Udp => { @@ -80,6 +80,7 @@ impl Device { (port, task) } Protocol::Tcp => { + let ip = address[0].addr.clone(); let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; let port = tcp4.local_addr()?.port(); let tcp6 = create_tcp_server(Some(port), Domain::IPV6, None)?; @@ -89,7 +90,14 @@ impl Device { loop { tokio::select! { _ = device::rate_limiter_timer(&rate_limiter) => {} - _ = device::tcp_peers_timer(&peers) => {} + _ = device::tcp_peers_timer( + &ip, + &peers, + key_pair.clone(), + rate_limiter.clone(), + iface_writer.clone(), + pi, + ) => {} // iface listen Ok(len) = iface_reader.read(&mut tun_src_buf) => { let src_buf = if pi { diff --git a/client/lib/src/wr_manager.rs b/client/lib/src/wr_manager.rs index 7a4c95f..64b3b7c 100644 --- a/client/lib/src/wr_manager.rs +++ b/client/lib/src/wr_manager.rs @@ -56,6 +56,7 @@ impl WRManager { let interface = wr_config.interface.unwrap(); //let address = AllowedIP::from_str(interface.address.as_str()).map_err(|e| anyhow!(e))?; let mut address: Vec = Vec::new(); + for addr in &interface.address { address.push(AllowedIP::from_str(addr).map_err(|e| anyhow!(e))?); } From 7b4ba3f8689bdbc49cd7dd022e55c3f1c90f6ec4 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Thu, 8 Jun 2023 22:22:37 +0800 Subject: [PATCH 27/39] fix 4, IntID bug --- .../com/timzaak/fornet/controller/NodeController.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala index 8452a5c..e91a675 100644 --- a/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/controller/NodeController.scala @@ -154,15 +154,15 @@ trait NodeController( } get("/:networkId/:nodeId/active_code") { + val (_, networkId) = checkAuth + val nodeId = _nodeId nodeDao - .findById(_networkId, _nodeId) + .findById(networkId, nodeId) .filter(_.status == NodeStatus.Waiting) .map { _ => String( Base64.getEncoder.encode( - s"1|${config.getString("server.grpc.endpoint")}|${hashId.encode( - params("networkId").toLong - )}|${hashId.encode(params("nodeId").toLong)}".getBytes + s"1|${config.getString("server.grpc.endpoint")}|${networkId.secretId}|${nodeId.secretId}".getBytes ) ) } From fad145e7ecee706c188bd74c75fad414140c2b8b Mon Sep 17 00:00:00 2001 From: Timzaak Date: Fri, 9 Jun 2023 00:10:04 +0800 Subject: [PATCH 28/39] fix 1: add MQTT acl check --- .../fornet/mqtt/MqttCallbackController.scala | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index cd9aa52..e1e6df9 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -1,6 +1,6 @@ package com.timzaak.fornet.mqtt -import com.timzaak.fornet.dao.{DB, Network, NetworkDao, NodeDao, NodeStatus} +import com.timzaak.fornet.dao.* import com.timzaak.fornet.entity.PublicKey import com.timzaak.fornet.grpc.convert.EntityConvert import com.timzaak.fornet.mqtt.api.RMqttApiClient @@ -8,13 +8,18 @@ import com.timzaak.fornet.protobuf.config.ClientMessage import com.timzaak.fornet.pubsub.MqttConnectionManager import com.timzaak.fornet.service.NodeService import com.typesafe.config.Config +import inet.ipaddr.IPAddress.IPVersion +import inet.ipaddr.IPAddressString +import inet.ipaddr.ipv4.IPv4Address import org.hashids.Hashids -import org.scalatra.{ Forbidden, Ok, ScalatraServlet } +import org.scalatra.{BadRequest, Forbidden, Ok, ScalatraServlet} import very.util.web.LogSupport -import very.util.web.json.{ JsonResponse, ZIOJsonSupport } -import zio.json.{ DeriveJsonDecoder, JsonDecoder, jsonField } +import very.util.web.json.{JsonResponse, ZIOJsonSupport} +import very.util.web.validate.ValidationExtra +import zio.json.{DeriveJsonDecoder, JsonDecoder, jsonField} -import scala.util.{ Failure, Try } +import scala.tools.nsc.backend.jvm.BackendReporting.Invalid +import scala.util.{Failure, Try} case class AuthRequest( clientId: String, // publicKey @@ -32,6 +37,18 @@ case class WebHookCallbackRequest( ) given JsonDecoder[WebHookCallbackRequest] = DeriveJsonDecoder.gen +case class AclRequest( + // 1 = sub, 2 = pub + access: String, + username: String, + ipaddr: String, + @jsonField("clientid") + clientId: String, + topic: String +) + +given JsonDecoder[AclRequest] = DeriveJsonDecoder.gen + class MqttCallbackController( nodeDao: NodeDao, networkDao: NetworkDao, @@ -116,8 +133,25 @@ class MqttCallbackController( Forbidden() } - post("/acl") { - logger.debug(s"mqtt acl does not implement,body: ${request.body}") + jPost("/acl") { (req: AclRequest) => + // logger.debug(s"mqtt acl does not implement,body: ${request.body}") + // pub + if (req.access == "2") { + val isPrivateIP = + Try(IPAddressString(req.ipaddr).toAddress(IPVersion.IPV4).asInstanceOf[IPv4Address].isPrivate) match { + case scala.util.Success(v) => v + case _ => false + } + if (isPrivateIP) { + Ok() + } else { + Forbidden() + } + // sub + } else if (req.access == "1") { + + // TODO: check it can only subscribe self, add hashId to username,and check + } Ok() } } From 4b85a5199f5fbc4d31d056d9e1863332e49a12ca Mon Sep 17 00:00:00 2001 From: timzaak Date: Fri, 9 Jun 2023 17:13:26 +0800 Subject: [PATCH 29/39] fix 2, mqtt add node_id to auth, change topic `client` to `client\{nodeSecretId}` --- .../scala/com/timzaak/fornet/dao/DB.scala | 4 -- .../fornet/grpc/AuthGRPCController.scala | 52 +++++++++++-------- .../fornet/mqtt/MqttCallbackController.scala | 50 +++++++++++++----- .../fornet/pubsub/MqttConnectionManager.scala | 13 ++--- .../scala/very/util/practice/RegexSuite.scala | 20 +++++++ client/lib/src/api/mod.rs | 40 +++++++------- client/lib/src/config.rs | 9 +++- client/lib/src/sc_manager.rs | 14 ++--- protobuf/auth.proto | 13 +++-- protobuf/config.proto | 2 +- 10 files changed, 138 insertions(+), 79 deletions(-) create mode 100644 backend/src/test/scala/very/util/practice/RegexSuite.scala diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala index 86b78ec..dce1cc8 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/DB.scala @@ -3,8 +3,6 @@ package com.timzaak.fornet.dao import io.getquill.* import io.getquill.context.jdbc.{ Decoders, Encoders } import very.util.persistence.quill.{ IDSupport, PageSupport, ZIOJsonSupport } -//import org.json4s.Extraction -//import org.json4s.JsonAST.JValue import very.util.entity.Pagination import java.time.{ LocalDateTime, OffsetDateTime } @@ -21,8 +19,6 @@ class DB extends PostgresJdbcContext(SnakeCase, "database") with ZIOJsonSupport given decodeOffsetDateTime: Decoder[OffsetDateTime] = decoder((index, row, _) => row.getObject(index, classOf[OffsetDateTime])) - // import org.json4s.jvalue2extractable - // private inline def encodeJValueEntity[T]:MappedEncoding[T,JValue] = MappedEncoding[T,JValue](v => Extraction.decompose(v)(formats)) // private inline def decodeJValueEntity[T](implicit mf:scala.reflect.Manifest[T]):MappedEncoding[JValue, T] = MappedEncoding[JValue,T](_.extract[T]) // diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala index 974a0ae..2228560 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/AuthGRPCController.scala @@ -8,7 +8,7 @@ import com.timzaak.fornet.controller.auth.AppAuthStrategyProvider import com.timzaak.fornet.dao.* import com.timzaak.fornet.protobuf.auth.* import com.timzaak.fornet.pubsub.NodeChangeNotifyService -import com.timzaak.fornet.service.{GRPCAuth, NodeAuthService} +import com.timzaak.fornet.service.{ GRPCAuth, NodeAuthService } import com.typesafe.config.Config import com.typesafe.scalalogging.Logger import inet.ipaddr.IPAddress.IPVersion @@ -20,12 +20,12 @@ import very.util.keycloak.KeycloakJWTAuthStrategy import very.util.web.LogSupport import very.util.security.IntID import zio.json.* -import zio.json.ast.{Json, JsonCursor} +import zio.json.ast.{ Json, JsonCursor } import java.net.http.HttpRequest.BodyPublishers -import java.net.http.{HttpClient, HttpRequest} -import java.net.{URI, URLEncoder} -import java.time.{LocalDateTime, OffsetDateTime} +import java.net.http.{ HttpClient, HttpRequest } +import java.net.{ URI, URLEncoder } +import java.time.{ LocalDateTime, OffsetDateTime } import scala.concurrent.Future class AuthGRPCController( @@ -44,10 +44,18 @@ class AuthGRPCController( private val mqttClientUrl = config.get[String]("mqtt.clientUrl") import quill.{ *, given } + private def errorResponse(message: String) = ActionResponse(ActionResponse.Response.Error(message)) + private def successResponse(secretId:String) = ActionResponse( + ActionResponse.Response.Success( + com.timzaak.fornet.protobuf.auth.SuccessResponse(mqttClientUrl, secretId) + ) + ) override def inviteConfirm( request: InviteConfirmRequest ): Future[ActionResponse] = { + + var params = Seq(request.networkId) if (request.nodeId.nonEmpty) { params = params.appended(request.nodeId.get) @@ -86,21 +94,22 @@ class AuthGRPCController( NodeStatus.Waiting, NodeStatus.Normal ) - ActionResponse(true, mqttUrl = Some(mqttClientUrl)) + successResponse(node.id.secretId) + } else { - ActionResponse(message = Some("already active or error response")) + errorResponse("already active or error response") } case None => createNode(networkId, publicKey) match { - case Some(value) => ActionResponse(message = Some(value)) - case None => - ActionResponse(isOk = true, mqttUrl = Some(mqttClientUrl)) + case Left(value) => errorResponse(value) + case Right(id) => + successResponse(id.secretId) } } Future.successful(response) } else { Future.successful( - ActionResponse(message = Some("Illegal Arguments")) + errorResponse("Illegal Arguments") ) } } @@ -120,35 +129,34 @@ class AuthGRPCController( authResult match { case Left(value) => - Future.successful(ActionResponse(message = Some(value))) + Future.successful(errorResponse(value)) case Right(userId) => val publicKey = request.encrypt.get.publicKey val networkId = IntID(request.networkId) logger.info( - s"user:${userId},networkId:${networkId}, publicKey:${request.encrypt.get.publicKey} register device with code:${request.deviceCode}" + s"user:${userId},networkId:${request.networkId}, publicKey:${request.encrypt.get.publicKey} register device with code:${request.deviceCode}" ) Future.successful( createNode(networkId, publicKey) match { - case Some(value) => ActionResponse(message = Some(value)) - case None => - ActionResponse(isOk = true, mqttUrl = Some(mqttClientUrl)) + case Left(value) => errorResponse(value) + case Right(id) => successResponse(id.secretId) } ) } } else { Future.successful( - ActionResponse(message = Some("do not support keycloak now")) + errorResponse("do not support keycloak now") ) } } else { Future.successful( - ActionResponse(message = Some("Illegal Arguments")) + errorResponse("Illegal Arguments") ) } } - private def createNode(networkId: IntID, publicKey: String) = { + private def createNode(networkId: IntID, publicKey: String):Either[String, IntID] = { val network = networkDao.findById(networkId).get // network create node val usedIp = nodeDao @@ -187,11 +195,11 @@ class AuthGRPCController( } } logger.info( - s"new client:$id(${publicKey}) join network ${network.id}" + s"new client:${id.id}(${publicKey}) join network ${network.id}" ) - None + Right(id) case None => - Some("Network has no available IP") + Left("Network has no available IP") } } diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index e1e6df9..94519f9 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -12,14 +12,15 @@ import inet.ipaddr.IPAddress.IPVersion import inet.ipaddr.IPAddressString import inet.ipaddr.ipv4.IPv4Address import org.hashids.Hashids -import org.scalatra.{BadRequest, Forbidden, Ok, ScalatraServlet} +import org.scalatra.{ ActionResult, BadRequest, Forbidden, Ok, ScalatraServlet } import very.util.web.LogSupport -import very.util.web.json.{JsonResponse, ZIOJsonSupport} +import very.util.web.json.{ JsonResponse, ZIOJsonSupport } import very.util.web.validate.ValidationExtra -import zio.json.{DeriveJsonDecoder, JsonDecoder, jsonField} +import very.util.security.IntID.toIntID +import zio.json.{ DeriveJsonDecoder, JsonDecoder, jsonField } -import scala.tools.nsc.backend.jvm.BackendReporting.Invalid -import scala.util.{Failure, Try} +import scala.util.{ Failure, Success, Try } +import scala.util.matching.Regex case class AuthRequest( clientId: String, // publicKey @@ -34,6 +35,7 @@ case class WebHookCallbackRequest( @jsonField("clientid") clientId: String, topic: String, + username: String, ) given JsonDecoder[WebHookCallbackRequest] = DeriveJsonDecoder.gen @@ -49,6 +51,7 @@ case class AclRequest( given JsonDecoder[AclRequest] = DeriveJsonDecoder.gen +private val networkTopicPattern = """^network/(\w+)$""".r class MqttCallbackController( nodeDao: NodeDao, networkDao: NetworkDao, @@ -67,7 +70,7 @@ class MqttCallbackController( val plainText = data.dropRight(1).mkString("-") PublicKey(clientId).validate(plainText, signature) && nodeDao .findByPublicKey(clientId) - .nonEmpty + .exists(_.id.secretId == username) } else { false } @@ -86,7 +89,7 @@ class MqttCallbackController( // {"action":"client_subscribe","clientid":"C5yG28uwzTumy6PpBEGqvvEWLJ8dYzF1uSFGziJG6Q8Jl+DPCRZZX05MPXb/s9GWsuO2JXzADAHz70WVbD2lew==","ipaddress":"127.0.0.1:56588","node":1,"opts":{"qos":1},"topic":"client","username":"undefined"} Try { - if (action == "client_subscribe" && topic == "client") { + if (action == "client_subscribe" && topic == s"client/${req.username}") { // send wr config val nodes = nodeDao @@ -134,13 +137,13 @@ class MqttCallbackController( } jPost("/acl") { (req: AclRequest) => - // logger.debug(s"mqtt acl does not implement,body: ${request.body}") + logger.debug(s"mqtt acl: ${request.body}") // pub - if (req.access == "2") { + val result: ActionResult = if (req.access == "2") { val isPrivateIP = Try(IPAddressString(req.ipaddr).toAddress(IPVersion.IPV4).asInstanceOf[IPv4Address].isPrivate) match { - case scala.util.Success(v) => v - case _ => false + case Success(v) => v + case _ => false } if (isPrivateIP) { Ok() @@ -149,9 +152,30 @@ class MqttCallbackController( } // sub } else if (req.access == "1") { + Try(req.username.toIntID).fold( + _ => Forbidden(), + { id => + req.topic match { + case networkTopicPattern(secretId) => + Try(secretId.toIntID).fold( + _ => Forbidden(), + { networkId => + if (nodeDao.findById(networkId, id).nonEmpty) { + Ok() + } else { + Forbidden() + } + } + ) - // TODO: check it can only subscribe self, add hashId to username,and check + case s"client/${id.secretId}" => Ok() + case _ => Forbidden() + } + } + ) + } else { + Forbidden() } - Ok() + result } } diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala index 7614da1..0f63f8f 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala @@ -1,7 +1,7 @@ package com.timzaak.fornet.pubsub -import com.timzaak.fornet.mqtt.api.{PublishRequest, RMqttApiClient} -import com.timzaak.fornet.protobuf.config.{ClientMessage, NetworkMessage} +import com.timzaak.fornet.mqtt.api.{ PublishRequest, RMqttApiClient } +import com.timzaak.fornet.protobuf.config.{ ClientMessage, NetworkMessage } import org.hashids.Hashids import scalapb.GeneratedMessage import very.util.security.IntID @@ -18,14 +18,15 @@ class MqttConnectionManager( private def encodeMessage(message: GeneratedMessage) = Base64.getEncoder.encodeToString(message.toByteArray) - def sendMessage(networkId: IntID, message: NetworkMessage): Try[Boolean] = { - logTry(s"send message[Network:$networkId] failure")( + def sendMessage(networkId: IntID, message: NetworkMessage, retain: Option[Boolean] = Some(false)): Try[Boolean] = { + logTry(s"send message[Network:${networkId.id}] failure")( mqttApiClient.publish( PublishRequest( payload = encodeMessage(message), qos = Some(1), encoding = Some("base64"), - topic = s"network/${networkId.secretId}" + topic = s"network/${networkId.secretId}", + retain = retain, ) ) ) @@ -44,7 +45,7 @@ class MqttConnectionManager( clientId = Some(publicKey), qos = Some(1), encoding = Some("base64"), - topic = "client", + topic = s"client/${nodeId.secretId}", retain, ) ) diff --git a/backend/src/test/scala/very/util/practice/RegexSuite.scala b/backend/src/test/scala/very/util/practice/RegexSuite.scala new file mode 100644 index 0000000..9363a9e --- /dev/null +++ b/backend/src/test/scala/very/util/practice/RegexSuite.scala @@ -0,0 +1,20 @@ +package very.util.practice + +import munit.FunSuite +import scala.util.matching.Regex + +class RegexSuite extends FunSuite { + private val networkTopicPattern = """^network/(\w+)$""".r + test("regex") { + val ID = "ewf014XF" + val topic = s"network/xxx" + topic match { + case s"network/${ID}" => println(s"xx:$ID") + case networkTopicPattern(secretId) => + println(secretId) + case _ => println("should not come here") + } + + + } +} diff --git a/client/lib/src/api/mod.rs b/client/lib/src/api/mod.rs index 999302c..4a7851e 100644 --- a/client/lib/src/api/mod.rs +++ b/client/lib/src/api/mod.rs @@ -6,9 +6,9 @@ use anyhow::{anyhow, bail}; use serde_derive::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; use tokio::sync::mpsc::Sender; -use crate::config::{Config, Identity, ServerConfig}; +use crate::config::{Config, Identity, NodeInfo, ServerConfig}; use crate::sc_manager::SCManager; -use crate::protobuf::auth::{auth_client::AuthClient, InviteConfirmRequest, OAuthDeviceCodeRequest, SsoLoginInfoRequest}; +use crate::protobuf::auth::{auth_client::AuthClient, InviteConfirmRequest, OAuthDeviceCodeRequest, SsoLoginInfoRequest, SuccessResponse}; use crate::server_api::APISocket; use crate::server_manager::{ServerManager, ServerMessage}; use std::time::Duration; @@ -19,6 +19,7 @@ use tonic::{ Request, }; use crate::{APP_NAME, MAC_OS_PACKAGE_NAME}; +use crate::protobuf::auth::action_response::Response; pub mod command_api; @@ -69,14 +70,12 @@ async fn join_network(server_manager: &mut ServerManager, invite_code: &str, str ) .await { - Ok(mqtt_url) => { - let mut map = HashMap::new(); - map.insert(invite_token.network_id, mqtt_url); + Ok(resp) => { //This must be success let server_config = ServerConfig { server: invite_token.endpoint, - mqtt: map, + info: vec![NodeInfo {network_id: invite_token.network_id, mqtt_url:resp.mqtt_url, node_id: resp.client_id}], }; server_config.save_config(&config_dir)?; change_config_and_init_sync_manger(); @@ -89,12 +88,10 @@ async fn join_network(server_manager: &mut ServerManager, invite_code: &str, str } else if version == 2u32 { // keycloak login let (mut client,sso_login) = SSOLogin::get_login_info(data).await?; match handle_oauth(identity, &mut client, &sso_login, stream).await { - Ok(mqtt_url) => { - let mut map = HashMap::new(); - map.insert(sso_login.network_id, mqtt_url); + Ok(resp) => { let server_config = ServerConfig { server: sso_login.endpoint, - mqtt: map, + info: vec![NodeInfo {network_id: sso_login.network_id.clone(), mqtt_url: resp.mqtt_url, node_id: resp.client_id}], }; server_config.save_config(&config_dir)?; change_config_and_init_sync_manger(); @@ -219,7 +216,7 @@ struct OAuthDeviceJWToken { } // https://github.com/keycloak/keycloak-community/blob/main/design/oauth2-device-authorization-grant.md -async fn handle_oauth(identity: Identity, client:&mut AuthClient, sso_login: &SSOLogin, stream: &mut APISocket) -> anyhow::Result { +async fn handle_oauth(identity: Identity, client:&mut AuthClient, sso_login: &SSOLogin, stream: &mut APISocket) -> anyhow::Result { let network_id = sso_login.network_id.clone(); @@ -252,11 +249,11 @@ async fn handle_oauth(identity: Identity, client:&mut AuthClient, sso_l network_id, encrypt:Some(encrypt), }); - let response= client.oauth_device_code_confirm(request).await?.into_inner(); - return if response.is_ok { - Ok(response.mqtt_url.unwrap().clone()) - } else { - Err(anyhow!(response.message.unwrap())) + let response= client.oauth_device_code_confirm(request).await?.into_inner().response; + return match response { + Some(Response::Error(message)) => Err(anyhow!(message)), + Some(Response::Success(resp))=> Ok(resp), + _ => Err(anyhow!("analyse auth response error")), } } else { tracing::debug!("check login status: not login, will try to check after {} seconds...", response.interval + 1); @@ -271,7 +268,7 @@ async fn server_invite_confirm( endpoint: &String, network_id: &String, node_id: Option, -) -> anyhow::Result { +) -> anyhow::Result { tracing::debug!("endpoint: {endpoint}"); let channel = Channel::from_shared(endpoint.clone())?.connect().await?; let mut client = AuthClient::new(channel); @@ -291,11 +288,10 @@ async fn server_invite_confirm( }); let response = client.invite_confirm(request).await?; - let response = response.into_inner(); - if response.is_ok { - Ok(response.mqtt_url.unwrap().clone()) - } else { - Err(anyhow!(response.message.unwrap())) + match response.into_inner().response { + Some(Response::Error(message)) => Err(anyhow!(message)), + Some(Response::Success(resp))=> Ok(resp), + _ => Err(anyhow!("analyse auth response error")), } } diff --git a/client/lib/src/config.rs b/client/lib/src/config.rs index 90c9826..1284395 100644 --- a/client/lib/src/config.rs +++ b/client/lib/src/config.rs @@ -235,11 +235,18 @@ impl Debug for Identity { ) } } +#[derive(Deserialize, Serialize, Debug)] +pub struct NodeInfo { + pub network_id: String, + pub mqtt_url: String, + pub node_id: String, +} #[derive(Deserialize, Serialize, Debug)] pub struct ServerConfig { pub server: String, - pub mqtt: HashMap + //networkId, mqttUrl, clientId + pub info: Vec } /* impl default for serverconfig { diff --git a/client/lib/src/sc_manager.rs b/client/lib/src/sc_manager.rs index 5e71d70..aa685e6 100644 --- a/client/lib/src/sc_manager.rs +++ b/client/lib/src/sc_manager.rs @@ -35,9 +35,9 @@ impl SCManager { status: None, }; - for (_, mqtt_url) in &config.server_config.mqtt { + for node_info in &config.server_config.info { let mut client = mqtt::CreateOptionsBuilder::new() - .server_uri(mqtt_url) + .server_uri(&node_info.mqtt_url) .client_id( &config.identity.pk_base64, ).create_client()?; @@ -48,6 +48,7 @@ impl SCManager { let conn_ops = mqtt::ConnectOptionsBuilder::new_v5() .properties(mqtt::properties![mqtt::PropertyCode::SessionExpiryInterval => 3600]) + .user_name(&node_info.node_id) .password(password) .finalize(); //client @@ -55,8 +56,9 @@ impl SCManager { //tokio spawn client.connect(conn_ops).await?; - let mut topics = config.server_config.mqtt.iter().map(|(key,_)| format!("network/{key}")).collect::>(); - topics.push("client".to_owned()); + let client_topic = format!("client/{}",&node_info.node_id); + let network_topic = format!("network/{}", &node_info.network_id); + let mut topics = vec!(&client_topic, &network_topic); let sub_opts = vec![mqtt::SubscribeOptions::with_retain_as_published(); topics.len()]; let qos = vec![1i32; topics.len()]; @@ -68,7 +70,7 @@ impl SCManager { if let Some(msg) = msg_opt { tracing::debug!("receive message, topic: {}",msg.topic()); match msg.topic() { - "client" => { + topic if topic == &client_topic => { if let Ok(client_message) = ClientMessage::decode(msg.payload()) { if let Some(info) = client_message.info { match info { @@ -104,7 +106,7 @@ impl SCManager { tracing::warn!("client message can not decode, may should update software"); } } - "network" => { + topic if topic == &network_topic => { if let Ok(network_message) = NetworkMessage::decode(msg.payload()) { if let Some(info) = network_message.info { match info { diff --git a/protobuf/auth.proto b/protobuf/auth.proto index 195005b..132cd81 100644 --- a/protobuf/auth.proto +++ b/protobuf/auth.proto @@ -4,13 +4,18 @@ package auth; option java_package = "com.timzaak.fornet.protobuf"; -import "google/protobuf/empty.proto"; +//import "google/protobuf/empty.proto"; +message SuccessResponse { + string mqtt_url = 1; + string client_id = 2; +} message ActionResponse { - bool isOk = 1; - optional string mqtt_url = 2; - optional string message = 3; + oneof response { + string error = 1; + SuccessResponse success = 2; + } } message EncryptRequest { diff --git a/protobuf/config.proto b/protobuf/config.proto index 58710e5..7026cbf 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package config; -import "google/protobuf/empty.proto"; +//import "google/protobuf/empty.proto"; option java_package = "com.timzaak.fornet.protobuf"; From 12dd21efe6512a363670e316a659020211d430ee Mon Sep 17 00:00:00 2001 From: timzaak Date: Fri, 9 Jun 2023 18:38:25 +0800 Subject: [PATCH 30/39] bak, mqtt reconnect --- client/lib/src/config.rs | 2 +- client/lib/src/sc_manager.rs | 192 +++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 91 deletions(-) diff --git a/client/lib/src/config.rs b/client/lib/src/config.rs index 1284395..a32e965 100644 --- a/client/lib/src/config.rs +++ b/client/lib/src/config.rs @@ -235,7 +235,7 @@ impl Debug for Identity { ) } } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct NodeInfo { pub network_id: String, pub mqtt_url: String, diff --git a/client/lib/src/sc_manager.rs b/client/lib/src/sc_manager.rs index aa685e6..f3861f7 100644 --- a/client/lib/src/sc_manager.rs +++ b/client/lib/src/sc_manager.rs @@ -5,6 +5,7 @@ use paho_mqtt as mqtt; use prost::Message; use tokio::sync::mpsc::Sender; use tokio_stream::StreamExt; +use crate::config::NodeInfo; use crate::protobuf::config::{ClientMessage, NetworkMessage, NetworkStatus, NodeStatus, WrConfig}; use crate::protobuf::config::client_message::Info::{Config, Status}; @@ -29,116 +30,127 @@ impl SCManager { } } - pub async fn mqtt_connect(&mut self, config: Arc) -> anyhow::Result<()> { - let mut deduplication = Duplication { - wr_config: None, - status: None, - }; + async fn mqtt_reconnect(&mut self, node_info: &NodeInfo, deduplication:&mut Duplication) -> anyhow::Result<()> { + let mut client = mqtt::CreateOptionsBuilder::new() + .server_uri(&node_info.mqtt_url) + .client_id( + &config.identity.pk_base64, + ).create_client()?; + let mut stream = client.get_stream(25); + + let encrypt = config.identity.sign2(Vec::new())?; + let password = format!("{}|{}|{}", encrypt.nonce, encrypt.timestamp, encrypt.signature); + + let conn_ops = mqtt::ConnectOptionsBuilder::new_v5() + .properties(mqtt::properties![mqtt::PropertyCode::SessionExpiryInterval => 3600]) + .user_name(&node_info.node_id) + .password(password) + .finalize(); + //client + + //tokio spawn + + client.connect(conn_ops).await?; + let client_topic = format!("client/{}",&node_info.node_id); + let network_topic = format!("network/{}", &node_info.network_id); + let mut topics = vec!(&client_topic, &network_topic); + let sub_opts = vec![mqtt::SubscribeOptions::with_retain_as_published(); topics.len()]; + + let qos = vec![1i32; topics.len()]; + + client.subscribe_many_with_options(&topics, &qos, &sub_opts, None) + .await?; + + while let Some(msg_opt) = stream.next().await { + if let Some(msg) = msg_opt { + tracing::debug!("receive message, topic: {}",msg.topic()); + match msg.topic() { + topic if topic == &client_topic => { + if let Ok(client_message) = ClientMessage::decode(msg.payload()) { + if let Some(info) = client_message.info { + match info { + Config(wr_config) => { + if deduplication.wr_config == Some(wr_config.clone()) { + continue; + } - for node_info in &config.server_config.info { - let mut client = mqtt::CreateOptionsBuilder::new() - .server_uri(&node_info.mqtt_url) - .client_id( - &config.identity.pk_base64, - ).create_client()?; - let mut stream = client.get_stream(25); - - let encrypt = config.identity.sign2(Vec::new())?; - let password = format!("{}|{}|{}", encrypt.nonce, encrypt.timestamp, encrypt.signature); - - let conn_ops = mqtt::ConnectOptionsBuilder::new_v5() - .properties(mqtt::properties![mqtt::PropertyCode::SessionExpiryInterval => 3600]) - .user_name(&node_info.node_id) - .password(password) - .finalize(); - //client - - //tokio spawn - - client.connect(conn_ops).await?; - let client_topic = format!("client/{}",&node_info.node_id); - let network_topic = format!("network/{}", &node_info.network_id); - let mut topics = vec!(&client_topic, &network_topic); - let sub_opts = vec![mqtt::SubscribeOptions::with_retain_as_published(); topics.len()]; - - let qos = vec![1i32; topics.len()]; - - client.subscribe_many_with_options(&topics, &qos, &sub_opts, None) - .await?; - - while let Some(msg_opt) = stream.next().await { - if let Some(msg) = msg_opt { - tracing::debug!("receive message, topic: {}",msg.topic()); - match msg.topic() { - topic if topic == &client_topic => { - if let Ok(client_message) = ClientMessage::decode(msg.payload()) { - if let Some(info) = client_message.info { - match info { - Config(wr_config) => { - if deduplication.wr_config == Some(wr_config.clone()) { + let _ = self.sender.send(ServerMessage::SyncConfig(wr_config.clone())).await; + deduplication.wr_config = Some(wr_config); + } + Status(status) => { + if let Some(node_status) = NodeStatus::from_i32(status) { + if deduplication.status == Some(node_status) { continue; } - - let _ = self.sender.send(ServerMessage::SyncConfig(wr_config.clone())).await; - deduplication.wr_config = Some(wr_config); - } - Status(status) => { - if let Some(node_status) = NodeStatus::from_i32(status) { - if deduplication.status == Some(node_status) { - continue; + match node_status { + NodeStatus::NodeForbid => { + let _ = self.sender.send( + ServerMessage::StopWR("node has been forbid or delete".to_owned()) + ).await; } - match node_status { - NodeStatus::NodeForbid => { - let _ = self.sender.send( - ServerMessage::StopWR("node has been forbid or delete".to_owned()) - ).await; - } - _ => { - // this would conflict with Info::Config message, so ignore this. - } + _ => { + // this would conflict with Info::Config message, so ignore this. } - deduplication.status = Some(node_status) } + deduplication.status = Some(node_status) } } } - } else { - tracing::warn!("client message can not decode, may should update software"); } + } else { + tracing::warn!("client message can not decode, may should update software"); } - topic if topic == &network_topic => { - if let Ok(network_message) = NetworkMessage::decode(msg.payload()) { - if let Some(info) = network_message.info { - match info { - Peer(peer_change) => { - let _ = self.sender.send(ServerMessage::SyncPeers(peer_change)).await; - } - NStatus(status) => { - if let Some(NetworkStatus::NetworkDelete) = NetworkStatus::from_i32(status) { - let _ = self.sender.send( - ServerMessage::StopWR("network has been delete".to_owned()) - ).await; - } + } + topic if topic == &network_topic => { + if let Ok(network_message) = NetworkMessage::decode(msg.payload()) { + if let Some(info) = network_message.info { + match info { + Peer(peer_change) => { + let _ = self.sender.send(ServerMessage::SyncPeers(peer_change)).await; + } + NStatus(status) => { + if let Some(NetworkStatus::NetworkDelete) = NetworkStatus::from_i32(status) { + let _ = self.sender.send( + ServerMessage::StopWR("network has been delete".to_owned()) + ).await; } } } - } else { - tracing::warn!("network message can not decode, may should update software"); } - } - _ => { - tracing::warn!("topic:{} message can not decode, may should update software", msg.topic()); + } else { + tracing::warn!("network message can not decode, may should update software"); } } - } else { - // A "None" means we were disconnected. Try to reconnect... - while let Err(err) = client.reconnect().await { - tracing::debug!("mqtt reconnect error: {}", err); - tokio::time::sleep(Duration::from_secs(2)).await; + _ => { + tracing::warn!("topic:{} message can not decode, may should update software", msg.topic()); } } + } else { + // A "None" means we were disconnected. Try to reconnect... + while let Err(err) = client.reconnect().await { + tracing::debug!("mqtt reconnect error: {}", err); + tokio::time::sleep(Duration::from_secs(2)).await; + } } - break; + } + Ok(()) + } + + pub async fn mqtt_connect(&mut self, config: Arc) -> anyhow::Result<()> { + + for node_info in &config.server_config.info { + let mut deduplication = Duplication { + wr_config: None, + status: None, + }; + let node_info = node_info.clone(); + tokio::spawn(async { + //self.mqtt_reconnect(node_info.clone(),) + }); + loop { + + } + } Ok(()) } From 2d8d6b3312f6ace65d0e44fc54f5f25cba13bd7e Mon Sep 17 00:00:00 2001 From: Timzaak Date: Sat, 10 Jun 2023 22:29:36 +0800 Subject: [PATCH 31/39] mqtt reconnect --- .../src/view/network/NetworkDetailPage.tsx | 3 +- .../fornet/mqtt/MqttCallbackController.scala | 2 +- .../fornet/pubsub/MqttConnectionManager.scala | 12 +++++--- .../pubsub/NodeChangeNotifyService.scala | 16 +++++----- client/lib/src/api/mod.rs | 2 -- client/lib/src/device/mod.rs | 7 ++--- client/lib/src/device/script_run.rs | 2 +- client/lib/src/device/tunnel.rs | 2 +- client/lib/src/flutter_api.rs | 2 +- client/lib/src/sc_manager.rs | 30 +++++++++++-------- client/lib/src/wr_manager.rs | 1 - 11 files changed, 42 insertions(+), 37 deletions(-) diff --git a/admin-web/src/view/network/NetworkDetailPage.tsx b/admin-web/src/view/network/NetworkDetailPage.tsx index fdf3979..9b96a0d 100644 --- a/admin-web/src/view/network/NetworkDetailPage.tsx +++ b/admin-web/src/view/network/NetworkDetailPage.tsx @@ -1,7 +1,7 @@ import {useEffect} from "react"; import {getNetwork, Network, NetworkProtocol, updateNetwork} from "../../api/networkAPI"; import {useParams} from "react-router-dom"; -import {Button, Col, Form, Input, InputNumber, Row, Select} from "antd"; +import {Button, Col, Form, Input, InputNumber, message, Row, Select} from "antd"; import {useIntl} from "react-intl"; import {useForm} from "antd/es/form/Form"; @@ -21,6 +21,7 @@ export default function NetworkDetailPage() { const submit = async () => { const data = await form.validateFields() await updateNetwork(networkId!, data) + message.info(intl.formatMessage({id: 'result.updateSuccess'}, {'0': intl.formatMessage({id: 'nav.network'})})) } return ( <> diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index 94519f9..4584d39 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -110,7 +110,7 @@ class MqttCallbackController( if (node.realStatus(network.status) == NodeStatus.Normal) { val notifyNodes = nodeService.getAllRelativeNodes(node) val network = networks(node.networkId) - mqttConnectionManager.sendMessage( + mqttConnectionManager.sendClientMessage( networkId = node.networkId, node.id, clientId, diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala index 0f63f8f..b758e8b 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/MqttConnectionManager.scala @@ -1,7 +1,7 @@ package com.timzaak.fornet.pubsub -import com.timzaak.fornet.mqtt.api.{ PublishRequest, RMqttApiClient } -import com.timzaak.fornet.protobuf.config.{ ClientMessage, NetworkMessage } +import com.timzaak.fornet.mqtt.api.{PublishRequest, RMqttApiClient} +import com.timzaak.fornet.protobuf.config.{ClientMessage, NetworkMessage} import org.hashids.Hashids import scalapb.GeneratedMessage import very.util.security.IntID @@ -18,7 +18,11 @@ class MqttConnectionManager( private def encodeMessage(message: GeneratedMessage) = Base64.getEncoder.encodeToString(message.toByteArray) - def sendMessage(networkId: IntID, message: NetworkMessage, retain: Option[Boolean] = Some(false)): Try[Boolean] = { + def sendNetworkMessage( + networkId: IntID, + message: NetworkMessage, + retain: Option[Boolean] = Some(false) + ): Try[Boolean] = { logTry(s"send message[Network:${networkId.id}] failure")( mqttApiClient.publish( PublishRequest( @@ -31,7 +35,7 @@ class MqttConnectionManager( ) ) } - def sendMessage( + def sendClientMessage( networkId: IntID, nodeId: IntID, publicKey: String, diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index e97eb29..445df95 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -27,7 +27,7 @@ class NodeChangeNotifyService( val wrConfig: WRConfig = EntityConvert.nodeToWRConfig(fixedNode, network, relativeNodes) - connectionManager.sendMessage( + connectionManager.sendClientMessage( oldNode.networkId, oldNode.id, oldNode.publicKey, @@ -39,7 +39,7 @@ class NodeChangeNotifyService( // only keep alive matter case NodeType.Relay => // notify other nodes in network that relay change. - connectionManager.sendMessage( + connectionManager.sendNetworkMessage( fixedNode.networkId, NetworkMessage( networkId = networkId, @@ -61,7 +61,7 @@ class NodeChangeNotifyService( for ((node, relativeNodes) <- nodeService.getNetworkAllRelativeNodes(nodes)) { val wrConfig = EntityConvert.nodeToWRConfig(node, newNetwork, relativeNodes) // this would trigger all nodes restart. - connectionManager.sendMessage( + connectionManager.sendClientMessage( node.networkId, node.id, node.publicKey, @@ -74,7 +74,7 @@ class NodeChangeNotifyService( // PS: Network would never recover from delete status def networkDeleteNotify(networkId: IntID): Unit = { - connectionManager.sendMessage( + connectionManager.sendNetworkMessage( networkId, NetworkMessage( networkId = networkId.secretId, @@ -91,7 +91,7 @@ class NodeChangeNotifyService( import NodeStatus.* val networkId = node.networkId.secretId // notify self node status change - connectionManager.sendMessage( + connectionManager.sendClientMessage( node.networkId, node.id, node.publicKey, @@ -103,7 +103,7 @@ class NodeChangeNotifyService( (oldStatus, status) match { case (Normal, _) => - connectionManager.sendMessage( + connectionManager.sendNetworkMessage( node.networkId, NetworkMessage( networkId = networkId, @@ -119,7 +119,7 @@ class NodeChangeNotifyService( val network = networkDao.findById(node.networkId).get val peer = EntityConvert.toPeer(node, network) - connectionManager.sendMessage( + connectionManager.sendNetworkMessage( node.networkId, NetworkMessage( networkId = networkId, @@ -131,7 +131,7 @@ class NodeChangeNotifyService( val notifyNodes = nodeService.getAllRelativeNodes(node) - connectionManager.sendMessage( + connectionManager.sendClientMessage( node.networkId, node.id, node.publicKey, diff --git a/client/lib/src/api/mod.rs b/client/lib/src/api/mod.rs index 4a7851e..faf51b6 100644 --- a/client/lib/src/api/mod.rs +++ b/client/lib/src/api/mod.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::env; use std::path::PathBuf; use std::sync::Arc; @@ -12,7 +11,6 @@ use crate::protobuf::auth::{auth_client::AuthClient, InviteConfirmRequest, OAuth use crate::server_api::APISocket; use crate::server_manager::{ServerManager, ServerMessage}; use std::time::Duration; -use auto_launch_extra::AutoLaunchBuilder; use cfg_if::cfg_if; use tonic::{ transport::Channel, diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index e158d7f..10550ac 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -24,14 +24,13 @@ use rand::rngs::OsRng; use std::net::{IpAddr, SocketAddr}; use std::sync::{Arc}; use std::time::{Duration, SystemTime}; -use anyhow::anyhow; use boringtun::noise::errors::WireGuardError; use boringtun::noise::rate_limiter::RateLimiter; use boringtun::noise::{Packet, Tunn, TunnResult}; use boringtun::noise::handshake::parse_handshake_anon; use prost::bytes::BufMut; use tokio::net::{TcpListener, TcpStream, UdpSocket}; -use tokio::sync::{Mutex,MutexGuard, RwLock}; +use tokio::sync::{Mutex, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep use tokio::net::tcp::OwnedWriteHalf; @@ -528,7 +527,7 @@ pub async fn tcp_listener_handler( pi: bool, ) ->anyhow::Result<()> { loop { - let (mut socket, addr) = listener.accept().await?; + let (socket, addr) = listener.accept().await?; let key_pair = key_pair.clone(); let rate_limiter = rate_limiter.clone(); let peers = peers.clone(); @@ -548,7 +547,7 @@ pub fn tcp_handler( ) { tokio::spawn(async move { let (private_key, public_key) = key_pair.as_ref(); - let (mut reader, mut writer ) = socket.into_split(); + let (mut reader, writer ) = socket.into_split(); let mut writer = WriterState::PureWriter(writer); let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; diff --git a/client/lib/src/device/script_run.rs b/client/lib/src/device/script_run.rs index 8e0ca7b..a1e3206 100644 --- a/client/lib/src/device/script_run.rs +++ b/client/lib/src/device/script_run.rs @@ -1,4 +1,4 @@ -use shell_candy::{ShellTask, ShellTaskBehavior, ShellTaskLog, ShellTaskOutput}; +use shell_candy::{ShellTask, ShellTaskBehavior, ShellTaskLog}; use crate::protobuf::config::Interface; #[derive(Default,Debug)] diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs index ef8229e..96fb6bb 100644 --- a/client/lib/src/device/tunnel.rs +++ b/client/lib/src/device/tunnel.rs @@ -1,5 +1,5 @@ use std::net::{SocketAddr}; -use tokio::net::{TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; +use tokio::net::{TcpListener, ToSocketAddrs, UdpSocket}; use socket2::{Type, Protocol, Domain}; pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> anyhow::Result { diff --git a/client/lib/src/flutter_api.rs b/client/lib/src/flutter_api.rs index be35743..8a65c8e 100644 --- a/client/lib/src/flutter_api.rs +++ b/client/lib/src/flutter_api.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use once_cell::sync::OnceCell; -use tokio::runtime::{Handle, Runtime}; +use tokio::runtime::Runtime; use tracing::Level; use crate::{default_config_path, server_manager}; use crate::server_manager::StartMethod; diff --git a/client/lib/src/sc_manager.rs b/client/lib/src/sc_manager.rs index f3861f7..60b9ffc 100644 --- a/client/lib/src/sc_manager.rs +++ b/client/lib/src/sc_manager.rs @@ -5,7 +5,7 @@ use paho_mqtt as mqtt; use prost::Message; use tokio::sync::mpsc::Sender; use tokio_stream::StreamExt; -use crate::config::NodeInfo; +use crate::config::{NodeInfo, Config as AppConfig}; use crate::protobuf::config::{ClientMessage, NetworkMessage, NetworkStatus, NodeStatus, WrConfig}; use crate::protobuf::config::client_message::Info::{Config, Status}; @@ -30,7 +30,7 @@ impl SCManager { } } - async fn mqtt_reconnect(&mut self, node_info: &NodeInfo, deduplication:&mut Duplication) -> anyhow::Result<()> { + async fn mqtt_reconnect(sender:Sender, node_info: &NodeInfo, config:Arc, deduplication:&mut Duplication) -> anyhow::Result<()> { let mut client = mqtt::CreateOptionsBuilder::new() .server_uri(&node_info.mqtt_url) .client_id( @@ -53,7 +53,7 @@ impl SCManager { client.connect(conn_ops).await?; let client_topic = format!("client/{}",&node_info.node_id); let network_topic = format!("network/{}", &node_info.network_id); - let mut topics = vec!(&client_topic, &network_topic); + let topics = vec!(&client_topic, &network_topic); let sub_opts = vec![mqtt::SubscribeOptions::with_retain_as_published(); topics.len()]; let qos = vec![1i32; topics.len()]; @@ -74,7 +74,7 @@ impl SCManager { continue; } - let _ = self.sender.send(ServerMessage::SyncConfig(wr_config.clone())).await; + let _ = sender.send(ServerMessage::SyncConfig(wr_config.clone())).await; deduplication.wr_config = Some(wr_config); } Status(status) => { @@ -84,7 +84,7 @@ impl SCManager { } match node_status { NodeStatus::NodeForbid => { - let _ = self.sender.send( + let _ = sender.send( ServerMessage::StopWR("node has been forbid or delete".to_owned()) ).await; } @@ -106,11 +106,11 @@ impl SCManager { if let Some(info) = network_message.info { match info { Peer(peer_change) => { - let _ = self.sender.send(ServerMessage::SyncPeers(peer_change)).await; + let _ = sender.send(ServerMessage::SyncPeers(peer_change)).await; } NStatus(status) => { if let Some(NetworkStatus::NetworkDelete) = NetworkStatus::from_i32(status) { - let _ = self.sender.send( + let _ = sender.send( ServerMessage::StopWR("network has been delete".to_owned()) ).await; } @@ -137,19 +137,23 @@ impl SCManager { } pub async fn mqtt_connect(&mut self, config: Arc) -> anyhow::Result<()> { - for node_info in &config.server_config.info { let mut deduplication = Duplication { wr_config: None, status: None, }; let node_info = node_info.clone(); - tokio::spawn(async { - //self.mqtt_reconnect(node_info.clone(),) + let _config = config.clone(); + let _sender = self.sender.clone(); + tokio::spawn(async move{ + loop { + let _config = _config.clone(); + let _sender = _sender.clone(); + let _ = SCManager::mqtt_reconnect(_sender, &node_info, _config, &mut deduplication).await; + tracing::debug!("mqtt connect error"); + tokio::time::sleep(Duration::from_secs(10)).await; + } }); - loop { - - } } Ok(()) diff --git a/client/lib/src/wr_manager.rs b/client/lib/src/wr_manager.rs index 64b3b7c..3c34567 100644 --- a/client/lib/src/wr_manager.rs +++ b/client/lib/src/wr_manager.rs @@ -1,6 +1,5 @@ use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; -use std::time::Duration; use anyhow::anyhow; use serde_derive::{Deserialize, Serialize}; use crate::config::{Config, Identity}; From 0ac7a0d469e2c43df8b16a4065967bdbeca0083a Mon Sep 17 00:00:00 2001 From: timzaak Date: Mon, 12 Jun 2023 14:53:11 +0800 Subject: [PATCH 32/39] update rmqtt version --- .../fornet/mqtt/MqttCallbackController.scala | 5 ---- .../simple/config/mqtt/plugin/rmqtt-acl.toml | 2 ++ .../config/mqtt/plugin/rmqtt-auth-http.toml | 21 +--------------- .../docker-compose/simple/docker-compose.yml | 2 +- .../docker/mqtt/config/plugin/rmqtt-acl.toml | 2 ++ .../mqtt/config/plugin/rmqtt-auth-http.toml | 25 +++---------------- command/docker/mqtt/run.sh | 2 +- 7 files changed, 11 insertions(+), 48 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index 4584d39..a1d36c6 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -131,11 +131,6 @@ class MqttCallbackController( Ok() } - post("/superuser") { - logger.debug(s"mqtt super user does not implement ${request.body}") - Forbidden() - } - jPost("/acl") { (req: AclRequest) => logger.debug(s"mqtt acl: ${request.body}") // pub diff --git a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-acl.toml b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-acl.toml index 70472a5..a96c60b 100644 --- a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-acl.toml +++ b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-acl.toml @@ -2,6 +2,8 @@ ## rmqtt-acl ##-------------------------------------------------------------------- +disconnect_if_pub_rejected = true + rules = [ ["allow", { user = "dashboard" }, "subscribe", ["$SYS/#"]], ["allow", { ipaddr = "127.0.0.1" }, "pubsub", ["$SYS/#", "#"]], diff --git a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-auth-http.toml b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-auth-http.toml index a7f3b8a..57544d1 100644 --- a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-auth-http.toml +++ b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-auth-http.toml @@ -5,7 +5,7 @@ http_timeout = "5s" http_headers.accept = "*/*" http_headers.Cache-Control = "no-cache" -http_headers.User-Agent = "RMQTT/0.1.1" +http_headers.User-Agent = "RMQTT/0.2.11" http_headers.Connection = "keep-alive" #Stop the hook chain after successful authentication, including auth, pub-acl and sub-acl @@ -35,25 +35,6 @@ http_auth_req.headers.content-type="application/json" http_auth_req.params = { clientId = "%c", username = "%u", password = "%P" } -##-------------------------------------------------------------------- -## Superuser request. -## -## Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## -## Value: URL -http_super_req.url = "http://backend/mqtt/superuser" -## Value: post | get | put -http_super_req.method = "post" -http_super_req.headers.content-type="application/json" -## Value: Params -http_super_req.params = { clientid = "%c", username = "%u" } - - ##-------------------------------------------------------------------- ## ACL request. ## diff --git a/command/docker-compose/simple/docker-compose.yml b/command/docker-compose/simple/docker-compose.yml index ed2336c..03a7710 100644 --- a/command/docker-compose/simple/docker-compose.yml +++ b/command/docker-compose/simple/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: mqtt: - image: rmqtt/rmqtt:latest + image: rmqtt/rmqtt:0.2.11 container_name: mqtt ports: - 1883:1883 diff --git a/command/docker/mqtt/config/plugin/rmqtt-acl.toml b/command/docker/mqtt/config/plugin/rmqtt-acl.toml index 70472a5..a96c60b 100644 --- a/command/docker/mqtt/config/plugin/rmqtt-acl.toml +++ b/command/docker/mqtt/config/plugin/rmqtt-acl.toml @@ -2,6 +2,8 @@ ## rmqtt-acl ##-------------------------------------------------------------------- +disconnect_if_pub_rejected = true + rules = [ ["allow", { user = "dashboard" }, "subscribe", ["$SYS/#"]], ["allow", { ipaddr = "127.0.0.1" }, "pubsub", ["$SYS/#", "#"]], diff --git a/command/docker/mqtt/config/plugin/rmqtt-auth-http.toml b/command/docker/mqtt/config/plugin/rmqtt-auth-http.toml index 038b72e..88c4b1f 100644 --- a/command/docker/mqtt/config/plugin/rmqtt-auth-http.toml +++ b/command/docker/mqtt/config/plugin/rmqtt-auth-http.toml @@ -5,14 +5,15 @@ http_timeout = "5s" http_headers.accept = "*/*" http_headers.Cache-Control = "no-cache" -http_headers.User-Agent = "RMQTT/0.1.1" +http_headers.User-Agent = "RMQTT/0.2.11" http_headers.Connection = "keep-alive" -#Stop the hook chain after successful authentication, including auth, pub-acl and sub-acl -break_if_allow = true #Disconnect if publishing is rejected disconnect_if_pub_rejected = true +#Return 'Deny' if http request error otherwise 'Ignore' +deny_if_error = true + ##-------------------------------------------------------------------- ## Authentication request. ## @@ -35,24 +36,6 @@ http_auth_req.headers.content-type="application/json" http_auth_req.params = { clientId = "%c", username = "%u", password = "%P" } -##-------------------------------------------------------------------- -## Superuser request. -## -## Variables: -## - %u: username -## - %c: clientid -## - %a: ipaddress -## - %r: protocol -## - %P: password -## -## Value: URL -http_super_req.url = "http://dev.fornetcode.com/mqtt/superuser" -## Value: post | get | put -http_super_req.method = "post" -http_super_req.headers.content-type="application/json" -## Value: Params -http_super_req.params = { clientid = "%c", username = "%u" } - ##-------------------------------------------------------------------- ## ACL request. diff --git a/command/docker/mqtt/run.sh b/command/docker/mqtt/run.sh index 906e046..495a4a8 100755 --- a/command/docker/mqtt/run.sh +++ b/command/docker/mqtt/run.sh @@ -2,5 +2,5 @@ # 1883(mqtt) 6060(http) 5363(grpc) # docker rm -f mqtt -docker run -d --name mqtt --network=host -v $(pwd)/log:/var/log/rmqtt -v $(pwd)/config/rmqtt.toml:/app/rmqtt/rmqtt.toml -v $(pwd)/config/plugin:/app/rmqtt/plugin rmqtt/rmqtt:latest +docker run -d --name mqtt --network=host -v $(pwd)/log:/var/log/rmqtt -v $(pwd)/config/rmqtt.toml:/app/rmqtt/rmqtt.toml -v $(pwd)/config/plugin:/app/rmqtt/plugin rmqtt/rmqtt:0.2.11 # docker logs -f --tail 50 mqtt \ No newline at end of file From b63cdfda9a0967408a2d76f93278c15241f12faa Mon Sep 17 00:00:00 2001 From: Timzaak Date: Mon, 12 Jun 2023 16:43:00 +0800 Subject: [PATCH 33/39] fix mqtt auth plain text combine error --- .../fornet/mqtt/MqttCallbackController.scala | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index a1d36c6..97538e9 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -12,15 +12,15 @@ import inet.ipaddr.IPAddress.IPVersion import inet.ipaddr.IPAddressString import inet.ipaddr.ipv4.IPv4Address import org.hashids.Hashids -import org.scalatra.{ ActionResult, BadRequest, Forbidden, Ok, ScalatraServlet } +import org.scalatra.* +import very.util.security.IntID.toIntID import very.util.web.LogSupport -import very.util.web.json.{ JsonResponse, ZIOJsonSupport } +import very.util.web.json.{JsonResponse, ZIOJsonSupport} import very.util.web.validate.ValidationExtra -import very.util.security.IntID.toIntID -import zio.json.{ DeriveJsonDecoder, JsonDecoder, jsonField } +import zio.json.{DeriveJsonDecoder, JsonDecoder, jsonField} -import scala.util.{ Failure, Success, Try } import scala.util.matching.Regex +import scala.util.{Failure, Success, Try} case class AuthRequest( clientId: String, // publicKey @@ -65,15 +65,16 @@ class MqttCallbackController( jPost("/auth") { (req: AuthRequest) => import req.* val data = password.split('|') - val isOk = if (data.length != 3) { + val isOk = if (data.length == 3) { val signature = data.last - val plainText = data.dropRight(1).mkString("-") + val plainText = data.dropRight(1).mkString("|") PublicKey(clientId).validate(plainText, signature) && nodeDao .findByPublicKey(clientId) .exists(_.id.secretId == username) } else { false } + logger.debug(s"userName:${req.username}, ${req.clientId} auth ${isOk}") if (isOk) { Ok() } else { @@ -141,14 +142,14 @@ class MqttCallbackController( case _ => false } if (isPrivateIP) { - Ok() + Ok("allow") } else { - Forbidden() + Forbidden("deny") } // sub } else if (req.access == "1") { Try(req.username.toIntID).fold( - _ => Forbidden(), + _ => Forbidden("allow"), { id => req.topic match { case networkTopicPattern(secretId) => @@ -156,20 +157,20 @@ class MqttCallbackController( _ => Forbidden(), { networkId => if (nodeDao.findById(networkId, id).nonEmpty) { - Ok() + Ok("allow") } else { - Forbidden() + Forbidden("deny") } } ) - case s"client/${id.secretId}" => Ok() - case _ => Forbidden() + case s"client/${id.secretId}" => Ok("allow") + case _ => Forbidden("deny") } } ) } else { - Forbidden() + Forbidden("deny") } result } From bc93e9dcca1d911f0cacf3807e5fd4d6322c21d9 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Mon, 12 Jun 2023 22:18:12 +0800 Subject: [PATCH 34/39] fix 3, mqtt change channel topic --- backend/src/main/scala/very/util/web/Controller.scala | 3 ++- .../src/test/scala/very/util/practice/RegexSuite.scala | 9 +++++++-- client/lib/src/server_manager.rs | 6 ++---- .../simple/config/mqtt/plugin/rmqtt-web-hook.toml | 2 +- command/docker/mqtt/config/plugin/rmqtt-web-hook.toml | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/src/main/scala/very/util/web/Controller.scala b/backend/src/main/scala/very/util/web/Controller.scala index acf9269..d5124a0 100644 --- a/backend/src/main/scala/very/util/web/Controller.scala +++ b/backend/src/main/scala/very/util/web/Controller.scala @@ -1,5 +1,6 @@ package very.util.web +import com.typesafe.scalalogging.LazyLogging import org.scalatra.json.JacksonJsonSupport import org.scalatra.* //import org.json4s.Formats @@ -14,7 +15,7 @@ class Controller //(using val jsonFormats: Formats) with I18nSupport with ValidationExtra with PaginationSupport - with LogSupport { + with LazyLogging { override def defaultFormat: Symbol = Symbol("txt") def badResponse(msg: String): ActionResult = { contentType = formats("txt") diff --git a/backend/src/test/scala/very/util/practice/RegexSuite.scala b/backend/src/test/scala/very/util/practice/RegexSuite.scala index 9363a9e..4971b26 100644 --- a/backend/src/test/scala/very/util/practice/RegexSuite.scala +++ b/backend/src/test/scala/very/util/practice/RegexSuite.scala @@ -1,9 +1,11 @@ package very.util.practice +import com.typesafe.scalalogging.LazyLogging import munit.FunSuite + import scala.util.matching.Regex -class RegexSuite extends FunSuite { +class RegexSuite extends FunSuite with LazyLogging { private val networkTopicPattern = """^network/(\w+)$""".r test("regex") { val ID = "ewf014XF" @@ -14,7 +16,10 @@ class RegexSuite extends FunSuite { println(secretId) case _ => println("should not come here") } + } - + test("stripSuffix") { + logger.info("bbb") + println("com.timzaak.test$controller".stripSuffix("$")) } } diff --git a/client/lib/src/server_manager.rs b/client/lib/src/server_manager.rs index 66a9866..f4dbb58 100644 --- a/client/lib/src/server_manager.rs +++ b/client/lib/src/server_manager.rs @@ -33,10 +33,8 @@ impl ServerManager { let mut sc_manager = SCManager::new(tx.clone()); let config = config.clone(); let _ = tokio::spawn(async move { - match sc_manager.mqtt_connect(config).await { - Ok(()) => tracing::warn!("sync config manager close, now can not receive any update from server"), - Err(e) => tracing::error!("sync config manager connect server result:{:?}", e), - }; + tracing::debug!("mqtt connect"); + let _ = sc_manager.mqtt_connect(config).await; }); } else { if start_method == StartMethod::CommandLine { diff --git a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-web-hook.toml b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-web-hook.toml index e87363c..fee9c7c 100644 --- a/command/docker-compose/simple/config/mqtt/plugin/rmqtt-web-hook.toml +++ b/command/docker-compose/simple/config/mqtt/plugin/rmqtt-web-hook.toml @@ -32,7 +32,7 @@ retry_multiplier = 2.5 #rule.client_connack = [{action = "client_connack", urls = ["http://127.0.0.1:5656/mqtt/webhook", "http://127.0.0.1:5656/mqtt/webhook"] } ] #rule.client_connected = [{action = "client_connected" } ] #rule.client_disconnected = [{action = "client_disconnected" } ] -rule.client_subscribe = [{action = "client_subscribe", topics=["client"]} ] +rule.client_subscribe = [{action = "client_subscribe", topics=["client/#"]} ] #rule.client_unsubscribe = [{action = "client_unsubscribe", topics=["x/y/z", "foo/#"] } ] #rule.message_publish = [{action = "message_publish" }] diff --git a/command/docker/mqtt/config/plugin/rmqtt-web-hook.toml b/command/docker/mqtt/config/plugin/rmqtt-web-hook.toml index 85d8219..8cdb82d 100644 --- a/command/docker/mqtt/config/plugin/rmqtt-web-hook.toml +++ b/command/docker/mqtt/config/plugin/rmqtt-web-hook.toml @@ -32,7 +32,7 @@ retry_multiplier = 2.5 #rule.client_connack = [{action = "client_connack", urls = ["http://127.0.0.1:5656/mqtt/webhook", "http://127.0.0.1:5656/mqtt/webhook"] } ] #rule.client_connected = [{action = "client_connected" } ] #rule.client_disconnected = [{action = "client_disconnected" } ] -rule.client_subscribe = [{action = "client_subscribe", topics=["client"]} ] +rule.client_subscribe = [{action = "client_subscribe", topics=["client/#"]} ] #rule.client_unsubscribe = [{action = "client_unsubscribe", topics=["x/y/z", "foo/#"] } ] #rule.message_publish = [{action = "message_publish" }] From 886e31002fbee9ce37cf77e0a12a0b847c0d964f Mon Sep 17 00:00:00 2001 From: Timzaak Date: Tue, 13 Jun 2023 13:23:34 +0800 Subject: [PATCH 35/39] fix, tcp server create, bind tcp6 only --- .../src/view/network/NetworkDetailPage.tsx | 1 + .../fornet/mqtt/MqttCallbackController.scala | 3 ++- client/lib/src/device/tunnel.rs | 20 +++++++++++-------- client/lib/src/device/unix_device.rs | 9 +++++---- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/admin-web/src/view/network/NetworkDetailPage.tsx b/admin-web/src/view/network/NetworkDetailPage.tsx index 9b96a0d..5979f89 100644 --- a/admin-web/src/view/network/NetworkDetailPage.tsx +++ b/admin-web/src/view/network/NetworkDetailPage.tsx @@ -81,6 +81,7 @@ export default function NetworkDetailPage() { ) } +// add Nodes Navigator, Invite Code // // // \ No newline at end of file diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index 97538e9..03ff60e 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -8,6 +8,7 @@ import com.timzaak.fornet.protobuf.config.ClientMessage import com.timzaak.fornet.pubsub.MqttConnectionManager import com.timzaak.fornet.service.NodeService import com.typesafe.config.Config +import com.typesafe.scalalogging.LazyLogging import inet.ipaddr.IPAddress.IPVersion import inet.ipaddr.IPAddressString import inet.ipaddr.ipv4.IPv4Address @@ -59,7 +60,7 @@ class MqttCallbackController( mqttConnectionManager: MqttConnectionManager, )(using hashId: Hashids) extends ScalatraServlet - with LogSupport + with LazyLogging with ZIOJsonSupport { jPost("/auth") { (req: AuthRequest) => diff --git a/client/lib/src/device/tunnel.rs b/client/lib/src/device/tunnel.rs index 96fb6bb..4537397 100644 --- a/client/lib/src/device/tunnel.rs +++ b/client/lib/src/device/tunnel.rs @@ -28,29 +28,33 @@ pub fn create_udp_socket(port: Option, domain: Domain, mark:Option) -> socket.bind(&address.into())?; Ok(UdpSocket::from_std(socket.into())?) } - +//TODO: how to bind same port of IPv6 IPv4 pub fn create_tcp_server(port: Option, domain: Domain, mark:Option) ->anyhow::Result{ let socket = socket2::Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; #[cfg(target_os = "linux")] { - socket.set_reuse_address(true)?; // On Linux SO_REUSEPORT won't prefer a connected IPv6 socket if let Some(mark) = mark { socket.set_mark(mark)?; } } - #[cfg(not(any(target_os = "linux", target_os = "windows")))] - socket.set_reuse_port(true)?; let port = port.unwrap_or(0); let address: SocketAddr = match domain { Domain::IPV4 => format!("0.0.0.0:{}", port), - Domain::IPV6 => - format!("[::]:{}", port), - _ => panic!("udp client don't support Domain::Unix") + Domain::IPV6 => { + socket.set_only_v6(false)?; + format!("[::]:{}", port) + }, + _ => panic!("tcp server don't support Domain::Unix") }.parse()?; + + socket.set_nonblocking(true)?; socket.bind(&address.into())?; - let tcp_listener = TcpListener::from_std(socket.into())?; + socket.listen(128)?; + + let tcp_listener = socket.into(); + let tcp_listener = TcpListener::from_std(tcp_listener)?; Ok(tcp_listener) } diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index fcafc74..bbd152d 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -81,9 +81,10 @@ impl Device { } Protocol::Tcp => { let ip = address[0].addr.clone(); - let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; - let port = tcp4.local_addr()?.port(); - let tcp6 = create_tcp_server(Some(port), Domain::IPV6, None)?; + //let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; + //let port = tcp4.local_addr()?.port(); + let tcp6 = create_tcp_server(port, Domain::IPV6, None)?; + let port = tcp6.local_addr()?.port(); let key_pair = Arc::new(key_pair); let task:JoinHandle<()> = tokio::spawn(async move { @@ -107,7 +108,7 @@ impl Device { }; device::tun_read_tcp_handle(&peers, src_buf, &mut tun_dst_buf).await; } - _ = device::tcp_listener_handler(&tcp4, key_pair.clone(), rate_limiter.clone(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} + //_ = device::tcp_listener_handler(&tcp4, key_pair.clone(), rate_limiter.clone(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} _ = device::tcp_listener_handler(&tcp6, key_pair.clone(), rate_limiter.clone(), Arc::clone(&peers), Arc::clone(&iface_writer), pi) => {break} } } From ab56873d82a61b1a1a96c1c5d2b0fa32f671cc8a Mon Sep 17 00:00:00 2001 From: timzaak Date: Tue, 13 Jun 2023 19:37:21 +0800 Subject: [PATCH 36/39] bak, add nodeType to config.proto for TCP --- .../main/scala/com/timzaak/fornet/dao/Node.scala | 14 ++++++++++---- .../fornet/grpc/convert/EntityConvert.scala | 2 +- .../fornet/mqtt/MqttCallbackController.scala | 2 +- .../fornet/pubsub/NodeChangeNotifyService.scala | 8 +++++--- protobuf/config.proto | 11 ++++++++--- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala index d29becc..379f618 100644 --- a/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala +++ b/backend/src/main/scala/com/timzaak/fornet/dao/Node.scala @@ -13,6 +13,14 @@ enum NodeType { // Normal: Fornet Client case Client, Relay + + import com.timzaak.fornet.protobuf.config.NodeType as PNodeType + def gRPCNodeType: PNodeType = { + this match { + case NodeType.Client => PNodeType.NODE_CLIENT + case NodeType.Relay => PNodeType.NODE_RELAY + } + } } object NodeType { @@ -102,7 +110,7 @@ case class Node( } object Node { - import very.util.web.json.{intIDDecoder, intIDEncoder} + import very.util.web.json.{ intIDDecoder, intIDEncoder } given nodeCCodec(using hashId: Hashids): JsonCodec[Node] = DeriveJsonCodec.gen } @@ -175,9 +183,7 @@ class NodeDao(using quill: DB, hashids: Hashids) { def getAllAvailableNodes(networkId: IntID): Seq[Node] = quill.run { quote { - query[Node].filter(n => - n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal) - ) + query[Node].filter(n => n.networkId == lift(networkId) && n.status == lift(NodeStatus.Normal)) } } def getAllAvailableNodes( diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala index 6801306..8329200 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala @@ -44,7 +44,7 @@ object EntityConvert { mtu = Some(setting.mtu.getOrElse(nSetting.mtu)), postUp = setting.postUp, postDown = setting.postDown, - protocol = nSetting.protocol.gRPCProtocol + protocol = nSetting.protocol.gRPCProtocol, ), ), peers = toPeers(relativeNodes.filter(_.id != node.id), network) diff --git a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala index 03ff60e..313089f 100644 --- a/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala +++ b/backend/src/main/scala/com/timzaak/fornet/mqtt/MqttCallbackController.scala @@ -120,7 +120,7 @@ class MqttCallbackController( networkId = node.networkId.secretId, ClientMessage.Info.Config( EntityConvert.nodeToWRConfig(node, network, notifyNodes) - ) + ), ) ) } diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index 445df95..82d4fc3 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -2,7 +2,7 @@ package com.timzaak.fornet.pubsub import com.timzaak.fornet.dao.{NetworkDao, *} import com.timzaak.fornet.grpc.convert.EntityConvert -import com.timzaak.fornet.protobuf.config.{NetworkStatus as PNetworkStatus, NodeStatus as PNodeStatus, *} +import com.timzaak.fornet.protobuf.config.{NetworkStatus as PNetworkStatus, NodeStatus as PNodeStatus, NodeType as PNodeType, *} import com.timzaak.fornet.service.NodeService import org.hashids.Hashids import very.util.security.IntID @@ -97,7 +97,8 @@ class NodeChangeNotifyService( node.publicKey, ClientMessage( networkId = networkId, - ClientMessage.Info.Status(status.gRPCNodeStatus) + ClientMessage.Info.Status(status.gRPCNodeStatus), + `type` = node.nodeType.gRPCNodeType, ) ) @@ -139,7 +140,8 @@ class NodeChangeNotifyService( networkId = networkId, ClientMessage.Info.Config( EntityConvert.nodeToWRConfig(node, network, notifyNodes) - ) + ), + `type` = node.nodeType.gRPCNodeType ) ) case _ => diff --git a/protobuf/config.proto b/protobuf/config.proto index 7026cbf..0509060 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -42,8 +42,10 @@ message PeerChange { } message WRConfig { - Interface interface = 1; - repeated Peer peers = 2; + string network_id = 1; + NodeType type = 2; + Interface interface = 3; + repeated Peer peers = 4; } enum NodeStatus { @@ -54,9 +56,12 @@ enum NodeStatus { enum NetworkStatus { NETWORK_DELETE = 0; } +enum NodeType { + NODE_CLIENT = 0; + NODE_RELAY = 1; +} message ClientMessage { - string network_id = 1; oneof info { WRConfig config = 2; NodeStatus status = 3; From 03b5455ab7f3b865e1e73ba396d77a196d829004 Mon Sep 17 00:00:00 2001 From: Timzaak Date: Tue, 13 Jun 2023 22:27:19 +0800 Subject: [PATCH 37/39] finish add node_type to proto --- .../timzaak/fornet/grpc/convert/EntityConvert.scala | 11 +++++------ .../fornet/pubsub/NodeChangeNotifyService.scala | 2 -- client/lib/src/device/mod.rs | 7 +++++-- client/lib/src/device/unix_device.rs | 9 +++------ client/lib/src/wr_manager.rs | 4 +++- protobuf/config.proto | 8 ++++---- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala index 8329200..5fcdac4 100644 --- a/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala +++ b/backend/src/main/scala/com/timzaak/fornet/grpc/convert/EntityConvert.scala @@ -1,7 +1,7 @@ package com.timzaak.fornet.grpc.convert -import com.timzaak.fornet.dao.{ Network, Node } -import com.timzaak.fornet.protobuf.config.{ Interface, Peer, WRConfig } +import com.timzaak.fornet.dao.{Network, Node} +import com.timzaak.fornet.protobuf.config.{Interface, Peer, WRConfig} object EntityConvert { @@ -16,9 +16,7 @@ object EntityConvert { val defaultKeepAlive = network.setting.keepAlive Peer( - endpoint = nodeSetting.endpoint.map(v => - s"$v:${nodeSetting.port.getOrElse(defaultPort)}" - ), + endpoint = nodeSetting.endpoint.map(v => s"$v:${nodeSetting.port.getOrElse(defaultPort)}"), allowedIp = Seq(node.peerAllowedIp), publicKey = node.publicKey, address = Seq(node.peerAddress), @@ -47,7 +45,8 @@ object EntityConvert { protocol = nSetting.protocol.gRPCProtocol, ), ), - peers = toPeers(relativeNodes.filter(_.id != node.id), network) + peers = toPeers(relativeNodes.filter(_.id != node.id), network), + `type` = node.nodeType.gRPCNodeType, ) } } diff --git a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala index 82d4fc3..dff44a5 100644 --- a/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala +++ b/backend/src/main/scala/com/timzaak/fornet/pubsub/NodeChangeNotifyService.scala @@ -98,7 +98,6 @@ class NodeChangeNotifyService( ClientMessage( networkId = networkId, ClientMessage.Info.Status(status.gRPCNodeStatus), - `type` = node.nodeType.gRPCNodeType, ) ) @@ -141,7 +140,6 @@ class NodeChangeNotifyService( ClientMessage.Info.Config( EntityConvert.nodeToWRConfig(node, network, notifyNodes) ), - `type` = node.nodeType.gRPCNodeType ) ) case _ => diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 10550ac..498418d 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -40,6 +40,7 @@ use peer::{AllowedIP, Peer}; use script_run::Scripts; use crate::device::peer::TcpConnection; use crate::device::script_run::run_opt_script; +use crate::protobuf::config::NodeType; use self::tun::WritePart; const HANDSHAKE_RATE_LIMIT: u64 = 100; // The number of handshakes per second we can tolerate before using cookies @@ -336,7 +337,9 @@ pub async fn tcp_peers_timer( key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, rate_limiter: Arc, iface: Arc>, - pi: bool) { + pi: bool, + node_type: NodeType, +) { let mut interval = time::interval(Duration::from_millis(250)); let mut dst_buf: Vec= vec![0; MAX_UDP_SIZE]; @@ -351,7 +354,7 @@ pub async fn tcp_peers_timer( }; match &mut p.endpoint.tcp_conn { TcpConnection::Nothing| TcpConnection::ConnectedFailure(_) => { - if ip < &p.ip { + if node_type == NodeType::NodeClient || ip < &p.ip { p.endpoint.tcp_conn = TcpConnection::Connecting(SystemTime::now()); match TcpStream::connect(&endpoint_addr).await { Ok(conn) => { diff --git a/client/lib/src/device/unix_device.rs b/client/lib/src/device/unix_device.rs index bbd152d..b5f0a54 100644 --- a/client/lib/src/device/unix_device.rs +++ b/client/lib/src/device/unix_device.rs @@ -14,7 +14,7 @@ use crate::device::script_run::{run_opt_script, Scripts}; use crate::device::tun::create_async_tun; use crate::device::tunnel::{create_tcp_server, create_udp_socket}; use nix::unistd::Uid; -use crate::protobuf::config::Protocol; +use crate::protobuf::config::{Protocol, NodeType}; pub struct Device { @@ -33,6 +33,7 @@ impl Device { mtu: u32, scripts:Scripts, protocol: Protocol, + node_type: NodeType, ) -> anyhow::Result { run_opt_script(&scripts.pre_up)?; tracing::debug!("begin to create tun"); @@ -53,9 +54,6 @@ impl Device { let udp4 = create_udp_socket(port, Domain::IPV4, None)?; let port = udp4.local_addr()?.port(); let udp6 = create_udp_socket(Some(port), Domain::IPV6, None)?; - - - let task:JoinHandle<()> = tokio::spawn(async move { loop { tokio::select! { @@ -81,8 +79,6 @@ impl Device { } Protocol::Tcp => { let ip = address[0].addr.clone(); - //let tcp4 = create_tcp_server(port, Domain::IPV4, None)?; - //let port = tcp4.local_addr()?.port(); let tcp6 = create_tcp_server(port, Domain::IPV6, None)?; let port = tcp6.local_addr()?.port(); let key_pair = Arc::new(key_pair); @@ -98,6 +94,7 @@ impl Device { rate_limiter.clone(), iface_writer.clone(), pi, + node_type, ) => {} // iface listen Ok(len) = iface_reader.read(&mut tun_src_buf) => { diff --git a/client/lib/src/wr_manager.rs b/client/lib/src/wr_manager.rs index 3c34567..50e78fb 100644 --- a/client/lib/src/wr_manager.rs +++ b/client/lib/src/wr_manager.rs @@ -4,7 +4,7 @@ use anyhow::anyhow; use serde_derive::{Deserialize, Serialize}; use crate::config::{Config, Identity}; use crate::device::peer::AllowedIP; -use crate::protobuf::config::{Protocol, WrConfig}; +use crate::protobuf::config::{Protocol, WrConfig, NodeType}; use crate::device::Device; use crate::device::script_run::Scripts; @@ -67,6 +67,7 @@ impl WRManager { tracing::info!("close device before restart"); let tun_name = config.get_tun_name(); let protocol = Protocol::from_i32(interface.protocol).unwrap_or(Protocol::Udp); + let node_type = NodeType::from_i32(wr_config.r#type).unwrap(); let scripts = Scripts::load_from_interface(&interface); let key_pair = (config.identity.x25519_sk.clone(), config.identity.x25519_pk.clone()); @@ -78,6 +79,7 @@ impl WRManager { interface.mtu.unwrap_or(1420) as u32, scripts, protocol, + node_type, )?; self.device = Some(wr_interface); diff --git a/protobuf/config.proto b/protobuf/config.proto index 0509060..9798e6f 100644 --- a/protobuf/config.proto +++ b/protobuf/config.proto @@ -42,10 +42,9 @@ message PeerChange { } message WRConfig { - string network_id = 1; - NodeType type = 2; - Interface interface = 3; - repeated Peer peers = 4; + Interface interface = 1; + repeated Peer peers = 2; + NodeType type = 3; } enum NodeStatus { @@ -62,6 +61,7 @@ enum NodeType { } message ClientMessage { + string network_id = 1; oneof info { WRConfig config = 2; NodeStatus status = 3; From 84efccd851bcb2f71c7f0859e40e582d4ae89ac0 Mon Sep 17 00:00:00 2001 From: timzaak Date: Wed, 14 Jun 2023 15:19:10 +0800 Subject: [PATCH 38/39] fix3, tcp --- client/lib/src/device/mod.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 498418d..4fcf874 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -33,7 +33,7 @@ use tokio::net::{TcpListener, TcpStream, UdpSocket}; use tokio::sync::{Mutex, RwLock}; use tokio::time; use tokio::io::{AsyncReadExt, AsyncWriteExt};//keep -use tokio::net::tcp::OwnedWriteHalf; +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use allowed_ips::AllowedIps; use peer::{AllowedIP, Peer}; @@ -353,12 +353,14 @@ pub async fn tcp_peers_timer( None => continue, }; match &mut p.endpoint.tcp_conn { - TcpConnection::Nothing| TcpConnection::ConnectedFailure(_) => { + TcpConnection::Nothing | TcpConnection::ConnectedFailure(_) => { if node_type == NodeType::NodeClient || ip < &p.ip { p.endpoint.tcp_conn = TcpConnection::Connecting(SystemTime::now()); match TcpStream::connect(&endpoint_addr).await { Ok(conn) => { - tcp_handler(conn, endpoint_addr, key_pair.clone(), rate_limiter.clone(), peers.clone(), iface.clone(), pi); + let (reader, writer) = conn.into_split(); + p.endpoint.tcp_conn = TcpConnection::Connected(writer); + tcp_handler(reader, WriterState::PeerWriter(peer.clone()), endpoint_addr, key_pair.clone(), rate_limiter.clone(), peers.clone(), iface.clone(), pi); }, Err(error) => { tracing::debug!("connect {endpoint_addr:?} failure, error: {error:?}"); @@ -372,15 +374,13 @@ pub async fn tcp_peers_timer( //TODO: add check of time, and reconnect continue; } - TcpConnection::Connected(connection) => { - //connection - - } + _ => {} }; match p.update_timers(&mut dst_buf) { TunnResult::Done => {} TunnResult::Err(WireGuardError::ConnectionExpired) => { - p.shutdown_endpoint(); // close open udp socket + tracing::debug!("connection expired, should shutdown this endpoint"); + p.shutdown_endpoint(); } TunnResult::Err(e) => tracing::error!(message = "Timer error", error = ?e), TunnResult::WriteToNetwork(packet) => { @@ -535,12 +535,15 @@ pub async fn tcp_listener_handler( let rate_limiter = rate_limiter.clone(); let peers = peers.clone(); let iface = iface.clone(); - tcp_handler(socket, addr,key_pair, rate_limiter, peers, iface, pi); + let (reader, writer ) = socket.into_split(); + tcp_handler(reader, WriterState::PureWriter(writer), addr,key_pair, rate_limiter, peers, iface, pi); } //Ok(()) } pub fn tcp_handler( - socket: TcpStream, + //socket: TcpStream, + reader: OwnedReadHalf, + writer: WriterState, addr: SocketAddr, key_pair: Arc<(x25519_dalek::StaticSecret, x25519_dalek::PublicKey)>, rate_limiter: Arc, @@ -550,8 +553,10 @@ pub fn tcp_handler( ) { tokio::spawn(async move { let (private_key, public_key) = key_pair.as_ref(); - let (mut reader, writer ) = socket.into_split(); - let mut writer = WriterState::PureWriter(writer); + let mut writer = writer; + let mut reader = reader; + //let (mut reader, writer ) = socket.into_split(); + //let mut writer = WriterState::PureWriter(writer); let mut src_buf: Vec = vec![0; MAX_UDP_SIZE]; let mut dst_buf: Vec = vec![0; MAX_UDP_SIZE]; while let Ok(size) = reader.read(&mut src_buf).await { From 046fdf5f69187b089fe50e535783862c563c242e Mon Sep 17 00:00:00 2001 From: Timzaak Date: Wed, 14 Jun 2023 22:02:30 +0800 Subject: [PATCH 39/39] 1. disable network protocol change. 2. fix client should change peers when peer add/update notification come. 3. tcp can run now --- .../src/view/network/NetworkDetailPage.tsx | 2 +- backend/src/main/resources/application.conf | 9 +++-- client/lib/src/device/mod.rs | 2 +- client/lib/src/server_manager.rs | 40 ++++++++++++++++--- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/admin-web/src/view/network/NetworkDetailPage.tsx b/admin-web/src/view/network/NetworkDetailPage.tsx index 5979f89..bd099cc 100644 --- a/admin-web/src/view/network/NetworkDetailPage.tsx +++ b/admin-web/src/view/network/NetworkDetailPage.tsx @@ -67,7 +67,7 @@ export default function NetworkDetailPage() { - diff --git a/backend/src/main/resources/application.conf b/backend/src/main/resources/application.conf index ea4a646..e40a8b8 100644 --- a/backend/src/main/resources/application.conf +++ b/backend/src/main/resources/application.conf @@ -31,6 +31,7 @@ mqtt { # should set keycloak or simple. +# please set this in private.conf wehn develop. auth { # ref from keycloak config, you can download it from keycloak/realm/client #keycloak { @@ -42,10 +43,10 @@ auth { # # the user who has client role can login in client, if undefined, anyone in the keycloak of realm can login # clientRole: "client", #} - simple { - token: "adminToken" - userId: "admin" - } + #simple { + # token: "adminToken" + # userId: "admin" + #} } # you can set your private config in private.conf file include "private.conf" \ No newline at end of file diff --git a/client/lib/src/device/mod.rs b/client/lib/src/device/mod.rs index 4fcf874..e7dc2db 100644 --- a/client/lib/src/device/mod.rs +++ b/client/lib/src/device/mod.rs @@ -516,7 +516,7 @@ pub async fn udp_handler(udp: &UdpSocket, } } -enum WriterState { +pub enum WriterState { PureWriter(OwnedWriteHalf), PeerWriter(Arc>), } diff --git a/client/lib/src/server_manager.rs b/client/lib/src/server_manager.rs index f4dbb58..76b90b6 100644 --- a/client/lib/src/server_manager.rs +++ b/client/lib/src/server_manager.rs @@ -80,15 +80,43 @@ impl ServerManager { } ServerMessage::SyncPeers(peer_change_message) => { if let Some(public_key) = peer_change_message.remove_public_key { - match Identity::get_pub_identity_from_base64(&public_key) { - Ok((x_pub_key, _)) => { - server_manager.wr_manager.remove_peer(&x_pub_key).await; - } - Err(_) => { - tracing::warn!("peer identity parse error") + if server_manager.config.map(|x|x.identity.pk_base64 != public_key).unwrap_or_else(true) { + match Identity::get_pub_identity_from_base64(&public_key) { + Ok((x_pub_key, _)) => { + server_manager.wr_manager.remove_peer(&x_pub_key).await; + } + Err(_) => { + tracing::warn!("peer identity parse error") + } } } } + if let Some(peer) = peer_change_message.add_peer { + let ip:IpAddr = peer.address.first().unwrap().parse().unwrap(); + let allowed_ip:Vec = peer.allowed_ip.into_iter().map(|ip| AllowedIP::from_str(&ip).unwrap()).collect(); + server_manager.wr_manager.add_peer( + peer.public_key, + false, + peer.endpoint, + &allowed_ip, + ip, + Some(peer.persistence_keep_alive as u16), + ).await; + } + if let Some(peer) = peer_change_message.change_peer { + if server_manager.config.map(|x|x.identity.pk_base64 != public_key).unwrap_or_else(true) { + let ip:IpAddr = peer.address.first().unwrap().parse().unwrap(); + let allowed_ip:Vec = peer.allowed_ip.into_iter().map(|ip| AllowedIP::from_str(&ip).unwrap()).collect(); + server_manager.wr_manager.add_peer( + peer.public_key, + false, + peer.endpoint, + &allowed_ip, + ip, + Some(peer.persistence_keep_alive as u16), + ).await; + } + } } }; }