// // SDLConfiguration.swift // sdlan // // Created by 安礼成 on 2025/7/14. // import Foundation import NIOCore // 配置项目 public class SDLConfiguration { // 网络地址信息 public struct NetworkAddress { public let networkId: UInt32 public let ip: UInt32 public let maskLen: UInt8 public let mac: Data public let networkDomain: String // ip地址 var ipAddress: String { return SDLUtil.int32ToIp(self.ip) } // 掩码 var maskAddress: String { let len0 = 32 - maskLen let num: UInt32 = (0xFFFFFFFF >> len0) << len0 return SDLUtil.int32ToIp(num) } // 网络地址 var netAddress: String { let len0 = 32 - maskLen let mask: UInt32 = (0xFFFFFFFF >> len0) << len0 return SDLUtil.int32ToIp(self.ip & mask) } } // 网络出口 public struct ExitNode { let exitNodeIp: UInt32 } public struct ACL: Sendable { let tcpPorts: Set let udpPorts: Set static let empty = ACL(tcpPorts: [], udpPorts: []) } // 解析的服务器地址信息 public struct ResolvedServerEndpoint: Sendable { let host: String let ip: String } // 当前的客户端版本 let version: Int let serverEndpoint: ResolvedServerEndpoint let stunSocketAddress: SocketAddress // 网络探测地址信息 let stunProbeSocketAddressArray: [[SocketAddress]] let clientId: String let networkAddress: NetworkAddress let hostname: String let accessToken: String let identityId: UInt32 let acl: ACL var exitNode: ExitNode? public init(version: Int, serverEndpoint: ResolvedServerEndpoint, stunServers: [String], clientId: String, networkAddress: NetworkAddress, hostname: String, accessToken: String, identityId: UInt32, acl: ACL, exitNode: ExitNode?) { self.version = version self.serverEndpoint = serverEndpoint let stunHosts = stunServers.isEmpty ? [serverEndpoint.ip] : stunServers self.stunSocketAddress = Self.makeStunSocketAddress(host: stunHosts[0], port: 1365) self.stunProbeSocketAddressArray = stunHosts.map { stunServer in [ Self.makeStunSocketAddress(host: stunServer, port: 1365), Self.makeStunSocketAddress(host: stunServer, port: 1366) ] } self.clientId = clientId self.networkAddress = networkAddress self.accessToken = accessToken self.identityId = identityId self.acl = acl self.hostname = hostname self.exitNode = exitNode } } private extension SDLConfiguration { static func resolveHostAddress(host: String, port: Int) -> String { let address = try! SocketAddress.makeAddressResolvingHost(host, port: port) return address.ipAddress! } static func makeStunSocketAddress(host: String, port: Int) -> SocketAddress { return try! SocketAddress.makeAddressResolvingHost(host, port: port) } } // 解析配置文件 extension SDLConfiguration { static func parse(options: [String: NSObject]) -> SDLConfiguration? { guard let version = options["version"] as? Int, let serverHost = options["server_host"] as? String, let stunAssistHost = options["stun_assist_host"] as? String, let accessToken = options["access_token"] as? String, let identityId = options["identity_id"] as? UInt32, let clientId = options["client_id"] as? String, let hostname = options["hostname"] as? String, let networkAddressDict = options["network_address"] as? [String: NSObject] else { return nil } guard let networkAddress = parseNetworkAddress(networkAddressDict) else { return nil } let acl = parseACL(options["acl"]) // 网络出口配置是可选的 var exitNode: ExitNode? = nil if let exitNodeIpStr = options["exit_node_ip"] as? String, let exitNodeIp = SDLUtil.ipv4StrToInt32(exitNodeIpStr) { exitNode = .init(exitNodeIp: exitNodeIp) } let serverEndpoint = ResolvedServerEndpoint( host: serverHost, ip: Self.resolveHostAddress(host: serverHost, port: 1443) ) return SDLConfiguration(version: version, serverEndpoint: serverEndpoint, stunServers: [serverHost, stunAssistHost], clientId: clientId, networkAddress: networkAddress, hostname: hostname, accessToken: accessToken, identityId: identityId, acl: acl, exitNode: exitNode) } private static func parseNetworkAddress(_ config: [String: NSObject]) -> SDLConfiguration.NetworkAddress? { guard let networkId = config["network_id"] as? UInt32, let ipStr = config["ip"] as? String, let ip = SDLUtil.ipv4StrToInt32(ipStr), let maskLen = config["mask_len"] as? UInt8, let mac = config["mac"] as? Data, let networkDomain = config["network_domain"] as? String else { return nil } return .init(networkId: networkId, ip: ip, maskLen: maskLen, mac: mac, networkDomain: networkDomain) } private static func parseACL(_ value: NSObject?) -> SDLConfiguration.ACL { guard let dict = value as? [String: NSObject] else { return .empty } return .init( tcpPorts: parsePortSet(dict["tcp"]), udpPorts: parsePortSet(dict["udp"]) ) } private static func parsePortSet(_ value: NSObject?) -> Set { let ports: [UInt32] if let values = value as? [UInt32] { ports = values } else if let values = value as? [Int] { ports = values.compactMap { $0 >= 0 ? UInt32($0) : nil } } else if let values = value as? [NSNumber] { ports = values.map(\.uint32Value) } else if let values = value as? NSArray { ports = values.compactMap { item in if let value = item as? UInt32 { return value } if let value = item as? Int, value >= 0 { return UInt32(value) } if let value = item as? NSNumber { return value.uint32Value } return nil } } else { ports = [] } return Set(ports.compactMap { port in guard port > 0, port <= UInt32(UInt16.max) else { return nil } return UInt16(port) }) } }