// SPDX-License-Identifier: MIT // Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. import Foundation extension TunnelConfiguration { convenience init(fromUapiConfig uapiConfig: String, basedOn base: TunnelConfiguration? = nil) throws { var interfaceConfiguration: InterfaceConfiguration? var peerConfigurations = [PeerConfiguration]() var lines = uapiConfig.split(separator: "\n") lines.append("") var parserState = ParserState.inInterfaceSection var attributes = [String: String]() for line in lines { var key = "" var value = "" if !line.isEmpty { guard let equalsIndex = line.firstIndex(of: "=") else { throw ParseError.invalidLine(line) } key = String(line[.. = ["private_key", "listen_port", "fwmark"] let peerSectionKeys: Set = ["public_key", "preshared_key", "allowed_ip", "endpoint", "persistent_keepalive_interval", "last_handshake_time_sec", "last_handshake_time_nsec", "rx_bytes", "tx_bytes", "protocol_version"] if parserState == .inInterfaceSection { guard interfaceSectionKeys.contains(key) else { throw ParseError.interfaceHasUnrecognizedKey(key) } } if parserState == .inPeerSection { guard peerSectionKeys.contains(key) else { throw ParseError.peerHasUnrecognizedKey(key) } } } let peerPublicKeysArray = peerConfigurations.map { $0.publicKey } let peerPublicKeysSet = Set(peerPublicKeysArray) if peerPublicKeysArray.count != peerPublicKeysSet.count { throw ParseError.multiplePeersWithSamePublicKey } interfaceConfiguration?.addresses = base?.interface.addresses ?? [] interfaceConfiguration?.dns = base?.interface.dns ?? [] interfaceConfiguration?.mtu = base?.interface.mtu if let interfaceConfiguration = interfaceConfiguration { self.init(name: base?.name, interface: interfaceConfiguration, peers: peerConfigurations) } else { throw ParseError.noInterface } } private static func collate(interfaceAttributes attributes: [String: String]) throws -> InterfaceConfiguration { guard let privateKeyString = attributes["private_key"] else { throw ParseError.interfaceHasNoPrivateKey } guard let privateKey = Data(hexKey: privateKeyString), privateKey.count == TunnelConfiguration.keyLength else { throw ParseError.interfaceHasInvalidPrivateKey(privateKeyString) } var interface = InterfaceConfiguration(privateKey: privateKey) if let listenPortString = attributes["listen_port"] { guard let listenPort = UInt16(listenPortString) else { throw ParseError.interfaceHasInvalidListenPort(listenPortString) } if listenPort != 0 { interface.listenPort = listenPort } } return interface } private static func collate(peerAttributes attributes: [String: String]) throws -> PeerConfiguration { guard let publicKeyString = attributes["public_key"] else { throw ParseError.peerHasNoPublicKey } guard let publicKey = Data(hexKey: publicKeyString), publicKey.count == TunnelConfiguration.keyLength else { throw ParseError.peerHasInvalidPublicKey(publicKeyString) } var peer = PeerConfiguration(publicKey: publicKey) if let preSharedKeyString = attributes["preshared_key"] { guard let preSharedKey = Data(hexKey: preSharedKeyString), preSharedKey.count == TunnelConfiguration.keyLength else { throw ParseError.peerHasInvalidPreSharedKey(preSharedKeyString) } // TODO(zx2c4): does the compiler optimize this away? var accumulator: UInt8 = 0 for index in 0..