import Foundation struct WGPeerConfig: Decodable { let serverPublicKey: String let presharedKey: String? let allowedIPs: [String] let hostName: String let port: Int let persistentKeepAlive: String? enum CodingKeys: String, CodingKey { case serverPublicKey = "server_pub_key" case presharedKey = "psk_key" case allowedIPs = "allowed_ips" case hostName case port case persistentKeepAlive = "persistent_keep_alive" } } struct WGConfig: Decodable { let initPacketMagicHeader, responsePacketMagicHeader: String? let underloadPacketMagicHeader, transportPacketMagicHeader: String? let junkPacketCount, junkPacketMinSize, junkPacketMaxSize: String? let initPacketJunkSize, responsePacketJunkSize, cookieReplyPacketJunkSize, transportPacketJunkSize: String? let specialJunk1, specialJunk2, specialJunk3, specialJunk4, specialJunk5: String? let dns1: String let dns2: String let mtu: String let hostName: String let port: Int let clientIP: String let clientPrivateKey: String let serverPublicKey: String let presharedKey: String? var allowedIPs: [String] var persistentKeepAlive: String let splitTunnelType: Int let splitTunnelSites: [String] let peers: [WGPeerConfig]? enum CodingKeys: String, CodingKey { case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2" case underloadPacketMagicHeader = "H3", transportPacketMagicHeader = "H4" case junkPacketCount = "Jc", junkPacketMinSize = "Jmin", junkPacketMaxSize = "Jmax" case initPacketJunkSize = "S1", responsePacketJunkSize = "S2", cookieReplyPacketJunkSize = "S3", transportPacketJunkSize = "S4" case specialJunk1 = "I1", specialJunk2 = "I2", specialJunk3 = "I3", specialJunk4 = "I4", specialJunk5 = "I5" case dns1 case dns2 case mtu case hostName case port case clientIP = "client_ip" case clientPrivateKey = "client_priv_key" case serverPublicKey = "server_pub_key" case presharedKey = "psk_key" case allowedIPs = "allowed_ips" case persistentKeepAlive = "persistent_keep_alive" case splitTunnelType case splitTunnelSites case peers } var settings: String { func trimmed(_ value: String?) -> String? { guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else { return nil } return value } guard let junkPacketCount = trimmed(junkPacketCount), let junkPacketMinSize = trimmed(junkPacketMinSize), let junkPacketMaxSize = trimmed(junkPacketMaxSize), let initPacketJunkSize = trimmed(initPacketJunkSize), let responsePacketJunkSize = trimmed(responsePacketJunkSize), let initPacketMagicHeader = trimmed(initPacketMagicHeader), let responsePacketMagicHeader = trimmed(responsePacketMagicHeader), let underloadPacketMagicHeader = trimmed(underloadPacketMagicHeader), let transportPacketMagicHeader = trimmed(transportPacketMagicHeader) else { return "" } var settingsLines: [String] = [] // Required parameters when junkPacketCount is present settingsLines.append("Jc = \(junkPacketCount)") settingsLines.append("Jmin = \(junkPacketMinSize)") settingsLines.append("Jmax = \(junkPacketMaxSize)") settingsLines.append("S1 = \(initPacketJunkSize)") settingsLines.append("S2 = \(responsePacketJunkSize)") settingsLines.append("H1 = \(initPacketMagicHeader)") settingsLines.append("H2 = \(responsePacketMagicHeader)") settingsLines.append("H3 = \(underloadPacketMagicHeader)") settingsLines.append("H4 = \(transportPacketMagicHeader)") // Optional parameters - only add if not nil and not empty if let s3 = trimmed(cookieReplyPacketJunkSize) { settingsLines.append("S3 = \(s3)") } if let s4 = trimmed(transportPacketJunkSize) { settingsLines.append("S4 = \(s4)") } if let i1 = trimmed(specialJunk1) { settingsLines.append("I1 = \(i1)") } if let i2 = trimmed(specialJunk2) { settingsLines.append("I2 = \(i2)") } if let i3 = trimmed(specialJunk3) { settingsLines.append("I3 = \(i3)") } if let i4 = trimmed(specialJunk4) { settingsLines.append("I4 = \(i4)") } if let i5 = trimmed(specialJunk5) { settingsLines.append("I5 = \(i5)") } return settingsLines.joined(separator: "\n") } private var interfaceSection: String { """ [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) MTU = \(mtu) PrivateKey = \(clientPrivateKey) \(settings) """ } var str: String { if let peers = peers, !peers.isEmpty { let peerSections = peers.map { peer -> String in var lines = ["[Peer]", "PublicKey = \(peer.serverPublicKey)"] if let psk = peer.presharedKey, !psk.isEmpty { lines.append("PresharedKey = \(psk)") } lines.append("AllowedIPs = \(peer.allowedIPs.joined(separator: ", "))") lines.append("Endpoint = \(peer.hostName):\(peer.port)") if let ka = peer.persistentKeepAlive { lines.append("PersistentKeepalive = \(ka)") } return lines.joined(separator: "\n") }.joined(separator: "\n") return interfaceSection + "\n" + peerSections } return """ \(interfaceSection) [Peer] PublicKey = \(serverPublicKey) \((presharedKey?.isEmpty ?? true) ? "" : "PresharedKey = \(presharedKey!)") AllowedIPs = \(allowedIPs.joined(separator: ", ")) Endpoint = \(hostName):\(port) PersistentKeepalive = \(persistentKeepAlive) """ } var redux: String { let peerCount = peers?.count ?? 1 let peerInfo = peers.map { peers in peers.enumerated().map { i, peer in "[Peer \(i + 1)] Endpoint = \(peer.hostName):\(peer.port), AllowedIPs = \(peer.allowedIPs.joined(separator: ", "))" }.joined(separator: "\n") } ?? "Endpoint = \(hostName):\(port), AllowedIPs = \(allowedIPs.joined(separator: ", "))" return """ [Interface] Address = \(clientIP) DNS = \(dns1), \(dns2) MTU = \(mtu) PrivateKey = *** \(settings) PeerCount = \(peerCount) \(peerInfo) SplitTunnelType = \(splitTunnelType) SplitTunnelSites = \(splitTunnelSites.joined(separator: ", ")) """ } }