From e0c7006dbc2339933d410010859bee5d854673c6 Mon Sep 17 00:00:00 2001 From: Roopesh Chander Date: Wed, 24 Oct 2018 19:18:05 +0530 Subject: [PATCH] Config file: Parsing a wg-quick config file Signed-off-by: Roopesh Chander --- WireGuard/WireGuard.xcodeproj/project.pbxproj | 12 ++ .../Parsing/WgQuickConfigFileParser.swift | 157 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 WireGuard/WireGuard/Parsing/WgQuickConfigFileParser.swift diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index e7edbe4..dec0177 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 6F628C41217F47DB003482A3 /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */; }; 6F6899A62180447E0012E523 /* x25519.c in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A52180447E0012E523 /* x25519.c */; }; 6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A7218044FC0012E523 /* Curve25519.swift */; }; + 6F6899AC218099F00012E523 /* WgQuickConfigFileParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899AB218099F00012E523 /* WgQuickConfigFileParser.swift */; }; 6F693A562179E556008551C1 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F693A552179E556008551C1 /* Endpoint.swift */; }; 6F7774E1217181B1006A79B3 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774DF217181B1006A79B3 /* MainViewController.swift */; }; 6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7774E0217181B1006A79B3 /* AppDelegate.swift */; }; @@ -33,6 +34,7 @@ 6F6899A42180447E0012E523 /* x25519.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x25519.h; sourceTree = ""; }; 6F6899A52180447E0012E523 /* x25519.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x25519.c; sourceTree = ""; }; 6F6899A7218044FC0012E523 /* Curve25519.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Curve25519.swift; sourceTree = ""; }; + 6F6899AB218099F00012E523 /* WgQuickConfigFileParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WgQuickConfigFileParser.swift; sourceTree = ""; }; 6F693A552179E556008551C1 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; 6F7774DF217181B1006A79B3 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 6F7774E0217181B1006A79B3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -72,6 +74,14 @@ path = Crypto; sourceTree = ""; }; + 6F6899AA218099D00012E523 /* Parsing */ = { + isa = PBXGroup; + children = ( + 6F6899AB218099F00012E523 /* WgQuickConfigFileParser.swift */, + ); + path = Parsing; + sourceTree = ""; + }; 6F7774DD217181B1006A79B3 /* UI */ = { isa = PBXGroup; children = ( @@ -133,6 +143,7 @@ isa = PBXGroup; children = ( 6F6899A32180445A0012E523 /* Crypto */, + 6F6899AA218099D00012E523 /* Parsing */, 6F7774E6217201E0006A79B3 /* Model */, 6F7774DD217181B1006A79B3 /* UI */, 6F7774ED21722D0C006A79B3 /* VPN */, @@ -230,6 +241,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6F6899AC218099F00012E523 /* WgQuickConfigFileParser.swift in Sources */, 6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */, 6F7774EF21722D97006A79B3 /* TunnelsManager.swift in Sources */, 6F693A562179E556008551C1 /* Endpoint.swift in Sources */, diff --git a/WireGuard/WireGuard/Parsing/WgQuickConfigFileParser.swift b/WireGuard/WireGuard/Parsing/WgQuickConfigFileParser.swift new file mode 100644 index 0000000..b2688e3 --- /dev/null +++ b/WireGuard/WireGuard/Parsing/WgQuickConfigFileParser.swift @@ -0,0 +1,157 @@ +// 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 invalidPeer + } + + // Based on the parser written by Eric Kuck in commit 5ef1656 + + static func parse(_ text: String, name: String) throws -> TunnelConfiguration { + + assert(!name.isEmpty) + + func collate(interfaceAttributes attributes: [String:String]) -> InterfaceConfiguration? { + // required wg fields + guard let privateKeyString = attributes["PrivateKey"] else { return nil } + guard let privateKey = Data(base64Encoded: privateKeyString), privateKey.count == 32 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.split(separator: ",") { + let trimmedString = addressString.trimmingCharacters(in: .whitespaces) + guard let address = IPAddressRange(from: trimmedString) else { return nil } + addresses.append(address) + } + interface.addresses = addresses + } + if let dnsString = attributes["DNS"] { + var dnsServers: [DNSServer] = [] + for dnsServerString in dnsString.split(separator: ",") { + let trimmedString = dnsServerString.trimmingCharacters(in: .whitespaces) + guard let dnsServer = DNSServer(from: trimmedString) 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 + } + + 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 == 32 else { return nil } + var peer = PeerConfiguration(publicKey: publicKey) + // wg fields + if let preSharedKeyString = attributes["PreSharedKey"] { + guard let preSharedKey = Data(base64Encoded: preSharedKeyString), preSharedKey.count == 32 else { return nil } + peer.preSharedKey = preSharedKey + } + if let allowedIPsString = attributes["AllowedIPs"] { + var allowedIPs: [IPAddressRange] = [] + for allowedIPString in allowedIPsString.split(separator: ",") { + let trimmedString = allowedIPString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + guard let allowedIP = IPAddressRange(from: trimmedString) 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 + } + + var interfaceConfiguration: InterfaceConfiguration? = nil + 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[.. 0 else { continue } + let lowercasedLine = line.lowercased() + + if let equalsIndex = line.firstIndex(of: "=") { + // Line contains an attribute + let key = line[..