// SPDX-License-Identifier: MIT // Copyright © 2018 WireGuard LLC. All Rights Reserved. import Foundation class WgQuickConfigFileParser { enum ParserState { case inInterfaceSection case inPeerSection case notInASection } enum ParseError: Error { case invalidLine(_ line: String.SubSequence) case noInterface case invalidInterface case multipleInterfaces case multiplePeersWithSamePublicKey case invalidPeer } //swiftlint:disable:next cyclomatic_complexity function_body_length static func parse(_ text: String, name: String) throws -> TunnelConfiguration { assert(!name.isEmpty) var interfaceConfiguration: InterfaceConfiguration? var peerConfigurations = [PeerConfiguration]() let lines = text.split(separator: "\n") var parserState = ParserState.notInASection var attributes = [String: String]() for (lineIndex, line) in lines.enumerated() { var trimmedLine: String if let commentRange = line.range(of: "#") { trimmedLine = String(line[..(peerPublicKeysArray) if peerPublicKeysArray.count != peerPublicKeysSet.count { throw ParseError.multiplePeersWithSamePublicKey } if let interfaceConfiguration = interfaceConfiguration { let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration, peers: peerConfigurations) return tunnelConfiguration } else { throw ParseError.noInterface } } //swiftlint:disable:next cyclomatic_complexity private static func collate(interfaceAttributes attributes: [String: String], name: String) -> InterfaceConfiguration? { // required wg fields guard let privateKeyString = attributes["privatekey"] else { return nil } guard let privateKey = Data(base64Encoded: privateKeyString), privateKey.count == TunnelConfiguration.keyLength else { return nil } var interface = InterfaceConfiguration(name: name, privateKey: privateKey) // other wg fields if let listenPortString = attributes["listenport"] { guard let listenPort = UInt16(listenPortString) else { return nil } interface.listenPort = listenPort } // wg-quick fields if let addressesString = attributes["address"] { var addresses = [IPAddressRange]() for addressString in addressesString.splitToArray(trimmingCharacters: .whitespaces) { guard let address = IPAddressRange(from: addressString) else { return nil } addresses.append(address) } interface.addresses = addresses } if let dnsString = attributes["dns"] { var dnsServers = [DNSServer]() for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespaces) { guard let dnsServer = DNSServer(from: dnsServerString) else { return nil } dnsServers.append(dnsServer) } interface.dns = dnsServers } if let mtuString = attributes["mtu"] { guard let mtu = UInt16(mtuString) else { return nil } interface.mtu = mtu } return interface } //swiftlint:disable:next cyclomatic_complexity private static func collate(peerAttributes attributes: [String: String]) -> PeerConfiguration? { // required wg fields guard let publicKeyString = attributes["publickey"] else { return nil } guard let publicKey = Data(base64Encoded: publicKeyString), publicKey.count == TunnelConfiguration.keyLength else { return nil } var peer = PeerConfiguration(publicKey: publicKey) // wg fields if let preSharedKeyString = attributes["presharedkey"] { guard let preSharedKey = Data(base64Encoded: preSharedKeyString), preSharedKey.count == TunnelConfiguration.keyLength else { return nil } peer.preSharedKey = preSharedKey } if let allowedIPsString = attributes["allowedips"] { var allowedIPs = [IPAddressRange]() for allowedIPString in allowedIPsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) { guard let allowedIP = IPAddressRange(from: allowedIPString) else { return nil } allowedIPs.append(allowedIP) } peer.allowedIPs = allowedIPs } if let endpointString = attributes["endpoint"] { guard let endpoint = Endpoint(from: endpointString) else { return nil } peer.endpoint = endpoint } if let persistentKeepAliveString = attributes["persistentkeepalive"] { guard let persistentKeepAlive = UInt16(persistentKeepAliveString) else { return nil } peer.persistentKeepAlive = persistentKeepAlive } return peer } }