// // PolicyService.swift // punchnet // // Created by 安礼成 on 2026/5/19. // import Foundation final class ExposedServiceSnapshot: Snapshot { let acl: SDLConfiguration.ACL init(acl: SDLConfiguration.ACL) { self.acl = acl } } actor PolicyService { // 处理权限控制 private let policyRuleStore: PolicyRuleStore nonisolated private let snapshotPublisher: SnapshotPublisher nonisolated private let aclPublisher: SnapshotPublisher nonisolated private let flowSessionTable = FlowSessionTable() // 当前节点的identityId值 let identityId: UInt32 private var latestExposedServiceRequestVersion: UInt32 = 0 init(identityId: UInt32, acl: SDLConfiguration.ACL) { self.identityId = identityId // 权限控制 let snapshotPublisher = SnapshotPublisher(initial: PolicyRuleSnapshot.empty()) self.policyRuleStore = PolicyRuleStore(publisher: snapshotPublisher) self.snapshotPublisher = snapshotPublisher self.aclPublisher = SnapshotPublisher(initial: ExposedServiceSnapshot(acl: acl)) } nonisolated func policyRuntime() -> PolicyRuntime { return PolicyRuntime(policyRuleSnapshot: self.snapshotPublisher.current(), flowSessionTable: self.flowSessionTable, acl: self.aclPublisher.current().acl) } nonisolated func recordOutboundFlow(ipPacket: IPPacketView) { guard let flowSession = ipPacket.flowSession() else { return } switch ipPacket.transportPacket { case .tcp(_, _, let flags): self.flowSessionTable.recordOutboundTCP(flowSession, flags: flags) case .udp(_, let dstPort, _): self.flowSessionTable.recordOutboundUDP(flowSession, isDNS: dstPort == 53) default: return } } func makePolicyRequest(srcIdentityID: UInt32) async -> Data? { return await self.policyRuleStore.makePolicyRequest(srcIdentityId: srcIdentityID, dstIdentityId: self.identityId) } func updatePolicy(superServiceProxy: SDLSuperServiceProxy) async { let requests = await self.policyRuleStore.makeBatchPolicyRequests(dstIdentityID: self.identityId) for request in requests { await superServiceProxy.send(type: .policyRequest, data: request) } } func applyPolicyResponse(_ policyResponse: SDLPolicyResponse) async { guard policyResponse.dstIdentityID == self.identityId else { SDLLogger.log("[PolicyService] ignore policy response, dstIdentityID mismatch: \(policyResponse.dstIdentityID), expected: \(self.identityId)", for: .debug) return } await self.policyRuleStore.applyPolicyResponse(policyResponse) } func makeExposedServiceRequest() -> Data? { var request = SDLExposedServiceRequest() self.latestExposedServiceRequestVersion = Self.nextVersion(after: self.latestExposedServiceRequestVersion) request.version = self.latestExposedServiceRequestVersion SDLLogger.log("[PolicyService] make exposed service request, version: \(request.version)", for: .debug) return try? request.serializedData() } func applyExposedServiceResponse(_ response: SDLExposedServiceResponse) -> SDLConfiguration.ACL? { guard response.version == self.latestExposedServiceRequestVersion else { SDLLogger.log("[PolicyService] ignore exposed service response, version: \(response.version), latest request version: \(self.latestExposedServiceRequestVersion)", for: .debug) return nil } let acl = SDLConfiguration.ACL(response: response) self.aclPublisher.publish(ExposedServiceSnapshot(acl: acl)) SDLLogger.log("[PolicyService] apply exposed service response, version: \(response.version), tcp: \(acl.tcpPorts.count), udp: \(acl.udpPorts.count)", for: .debug) return acl } func clear() async { self.flowSessionTable.clear() await self.policyRuleStore.clear() } deinit { SDLLogger.log("[PolicyService] deinit") } private static func nextVersion(after version: UInt32) -> UInt32 { if version == UInt32.max { return 1 } return version + 1 } }