Compare commits

..

26 Commits

Author SHA1 Message Date
Jason A. Donenfeld c5c536318f Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 20:18:11 +01:00
Jason A. Donenfeld 6e36c72f96 Importing: simplify
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 20:16:40 +01:00
Roopesh Chander f9dcfc1b9d Importing: Assume imported files without .conf or .zip extensions to be a config file
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-07 00:35:37 +05:30
Roopesh Chander 33edfd3587 DNSResolver: No need to resolve if the endpoint is already an IP address
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 23:59:48 +05:30
Roopesh Chander aa0b6e0c60 Model: Endpoint.hasHostAsIPAddress()
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 23:59:48 +05:30
Jason A. Donenfeld e992030569 PacketTunnelProvider: modernize header
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 19:04:53 +01:00
Jason A. Donenfeld 18a21064b2 Not horribly broken
Instead it's just mostly broken. Maybe someday it will only be partially
broken. Then a bit broken. And then maybe not broken at all? Before, of
course, it's broken again.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 19:02:46 +01:00
Roopesh Chander f6a5dfead4 Global: swiftlint autocorrect --format
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 22:42:53 +05:30
Roopesh Chander 0f4b1c5c1c Global: swiftlint autocorrect
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 22:42:05 +05:30
Roopesh Chander 3496adca86 Importing: Error out on file with unsupported file extension
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 22:32:33 +05:30
Jason A. Donenfeld 0a55a284d5 wireguard-go-bridge: take fd instead of fnptr
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 16:27:25 +01:00
Jason A. Donenfeld 02c31c89f6 Xcode: enable more warnings
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-06 16:27:25 +01:00
Roopesh Chander 95a4419b20 Tunnel edit: TunnelEditTableViewKeyValueCell need not support a read-only mode now
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 19:01:13 +05:30
Roopesh Chander 6c9fc8bcb1 Tunnel edit: A new cell class for the public key field, to make the value scrollable
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 17:05:03 +05:30
Roopesh Chander 1a43ad6e39 Tunnel detail: Refactor out the label scrolling into a separate UI class
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 16:49:09 +05:30
Roopesh Chander a62f7fb988 Tunnel view model: Peers in a configuation may not share the same public key
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 08:17:56 +05:30
Roopesh Chander 636aa98b79 Parser: Peers in a configuation may not share the same public key
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-06 08:17:56 +05:30
Jason A. Donenfeld 24575a5cf9 Version bump
We update the build number, not the version number, so that test flight
doesn't need a new review.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 21:44:00 +01:00
Roopesh Chander 34a8d70a45 Parser: Address, DNS and AllowedIPs can have mutliple entries
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-05 23:53:00 +05:30
Roopesh Chander 733c29021e QR Code: Dismiss the QR code screen when the name prompt is cancelled
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-05 23:38:41 +05:30
Roopesh Chander f102051e22 Tunnel view model: No need to save if we already have a validated configuration object
Signed-off-by: Roopesh Chander <roop@roopc.net>
2018-11-05 22:55:09 +05:30
Jason A. Donenfeld e228ac9d99 global: convert to Swift 4.2
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 06:31:25 +01:00
Jason A. Donenfeld 8860abc89e TunnelsManager: account for no or many endpoints
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 06:23:26 +01:00
Jason A. Donenfeld 03cce06292 wireguard-go-bridge: work around DWARF generation issue
Golang bug 25148 upstream.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 04:47:11 +01:00
Jason A. Donenfeld a08a9ba4c4 PacketTunnelProvider: clamp v6 addresses to /120
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 03:11:11 +01:00
Jason A. Donenfeld 8ec38e1d2b Settings: account for bottom padding on iPhone X
Reported-by: Jeffrey A. Donenfeld <jad@runblue.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2018-11-05 02:09:40 +01:00
27 changed files with 348 additions and 339 deletions
-5
View File
@@ -1,8 +1,3 @@
# ***DO NOT USE: CODEBASE HORRIBLY BROKEN***
----
# [WireGuard](https://www.wireguard.com/) for iOS
## Building
+11 -1
View File
@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
6BB8400421892C920003598F /* CopyableLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB8400321892C920003598F /* CopyableLabelTableViewCell.swift */; };
6F0068572191AFD200419BE9 /* ScrollableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0068562191AFD200419BE9 /* ScrollableLabel.swift */; };
6F5D0C1521832391000F85AD /* DNSResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1421832391000F85AD /* DNSResolver.swift */; };
6F5D0C1D218352EF000F85AD /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F5D0C1C218352EF000F85AD /* PacketTunnelProvider.swift */; };
6F5D0C22218352EF000F85AD /* WireGuardNetworkExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 6F5D0C1A218352EF000F85AD /* WireGuardNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -78,6 +79,7 @@
/* Begin PBXFileReference section */
6BB8400321892C920003598F /* CopyableLabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CopyableLabelTableViewCell.swift; sourceTree = "<group>"; };
6F0068562191AFD200419BE9 /* ScrollableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollableLabel.swift; sourceTree = "<group>"; };
6F5D0C1421832391000F85AD /* DNSResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSResolver.swift; sourceTree = "<group>"; };
6F5D0C1A218352EF000F85AD /* WireGuardNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WireGuardNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
6F5D0C1C218352EF000F85AD /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
@@ -205,6 +207,7 @@
6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */,
6FDEF805218725D200D8FBF6 /* SettingsTableViewController.swift */,
6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */,
6F0068562191AFD200419BE9 /* ScrollableLabel.swift */,
);
path = iOS;
sourceTree = "<group>";
@@ -393,7 +396,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1000;
LastUpgradeCheck = 0940;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "WireGuard LLC";
TargetAttributes = {
6F5D0C19218352EF000F85AD = {
@@ -500,6 +503,7 @@
6F5D0C482183C6A3000F85AD /* PacketTunnelOptionsGenerator.swift in Sources */,
6BB8400421892C920003598F /* CopyableLabelTableViewCell.swift in Sources */,
6F693A562179E556008551C1 /* Endpoint.swift in Sources */,
6F0068572191AFD200419BE9 /* ScrollableLabel.swift in Sources */,
6FDEF7E62185EFB200D8FBF6 /* QRScanViewController.swift in Sources */,
6F6899A62180447E0012E523 /* x25519.c in Sources */,
6F5D0C452183BCDA000F85AD /* PacketTunnelOptionKey.swift in Sources */,
@@ -646,7 +650,9 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -706,7 +712,9 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -751,6 +759,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "WireGuard/WireGuard-Bridging-Header.h";
SWIFT_VERSION = 4.2;
};
name = Debug;
};
@@ -770,6 +779,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "WireGuard/WireGuard-Bridging-Header.h";
SWIFT_VERSION = 4.2;
};
name = Release;
};
+1 -1
View File
@@ -1,2 +1,2 @@
VERSION_NAME = 0.0.20181104
VERSION_ID = 1
VERSION_ID = 3
@@ -16,6 +16,7 @@ class WgQuickConfigFileParser {
case noInterface
case invalidInterface
case multipleInterfaces
case multiplePeersWithSamePublicKey
case invalidPeer
}
@@ -114,7 +115,12 @@ class WgQuickConfigFileParser {
// Line contains an attribute
let key = line[..<equalsIndex].trimmingCharacters(in: .whitespaces)
let value = line[line.index(equalsIndex, offsetBy: 1)...].trimmingCharacters(in: .whitespaces)
attributes[key] = value
let keysWithMultipleEntriesAllowed: Set<String> = ["Address", "AllowedIPs", "DNS"]
if let presentValue = attributes[key], keysWithMultipleEntriesAllowed.contains(key) {
attributes[key] = presentValue + "," + value
} else {
attributes[key] = value
}
} else {
if (lowercasedLine != "[interface]" && lowercasedLine != "[peer]") {
throw ParseError.invalidLine(line)
@@ -144,6 +150,12 @@ class WgQuickConfigFileParser {
}
}
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if (peerPublicKeysArray.count != peerPublicKeysSet.count) {
throw ParseError.multiplePeersWithSamePublicKey
}
if let interfaceConfiguration = interfaceConfiguration {
let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration)
tunnelConfiguration.peers = peerConfigurations
+13
View File
@@ -75,3 +75,16 @@ extension Endpoint: Codable {
case invalidData
}
}
extension Endpoint {
func hasHostAsIPAddress() -> Bool {
switch (host) {
case .name(_, _):
return false
case .ipv4(_):
return true
case .ipv6(_):
return true
}
}
}
@@ -19,7 +19,7 @@ extension IPAddressRange {
let addressString = String(string[string.startIndex ..< endOfIPAddress])
let address: IPAddress
if let addr = IPv4Address(addressString) {
address = addr
address = addr
} else if let addr = IPv6Address(addressString) {
address = addr
} else {
@@ -96,6 +96,10 @@ class TunnelViewModel {
}
func save() -> SaveResult<InterfaceConfiguration> {
if let validatedConfiguration = validatedConfiguration {
// It's already validated and saved
return .saved(validatedConfiguration)
}
fieldsWithError.removeAll()
guard let name = scratchpad[.name]?.trimmingCharacters(in: .whitespacesAndNewlines), (!name.isEmpty) else {
fieldsWithError.insert(.name)
@@ -234,6 +238,10 @@ class TunnelViewModel {
}
func save() -> SaveResult<PeerConfiguration> {
if let validatedConfiguration = validatedConfiguration {
// It's already validated and saved
return .saved(validatedConfiguration)
}
fieldsWithError.removeAll()
guard let publicKeyString = scratchpad[.publicKey] else {
fieldsWithError.insert(.publicKey)
@@ -426,6 +434,13 @@ class TunnelViewModel {
peerConfigurations.append(peerConfiguration)
}
}
let peerPublicKeysArray = peerConfigurations.map { $0.publicKey }
let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
if (peerPublicKeysArray.count != peerPublicKeysSet.count) {
return .error("Two or more peers cannot have the same public key")
}
let tunnelConfiguration = TunnelConfiguration(interface: interfaceConfiguration)
tunnelConfiguration.peers = peerConfigurations
return .saved(tunnelConfiguration)
+2 -2
View File
@@ -11,7 +11,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var mainVC: MainViewController?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.backgroundColor = UIColor.white
@@ -26,7 +26,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
defer {
do {
try FileManager.default.removeItem(at: url)
@@ -14,7 +14,7 @@ class CopyableLabelTableViewCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
self.addGestureRecognizer(gestureRecognizer)
@@ -21,8 +21,6 @@ class ErrorPresenter {
return ("Unable to remove tunnel", "Internal error")
// TunnelActivationError
case TunnelActivationError.noEndpoint:
return ("Endpoint missing", "There must be at least one peer with an endpoint")
case TunnelActivationError.dnsResolutionFailed:
return ("DNS resolution failure", "One or more endpoint domains could not be resolved")
case TunnelActivationError.tunnelActivationFailed:
@@ -114,7 +114,9 @@ class QRScanViewController: UIViewController {
let alert = UIAlertController(title: NSLocalizedString("Please name the scanned tunnel", comment: ""), message: nil, preferredStyle: .alert)
alert.addTextField(configurationHandler: nil)
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: { [weak self] _ in
self?.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: NSLocalizedString("Save", comment: ""), style: .default, handler: { [weak self] _ in
let title = alert.textFields?[0].text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if (title.isEmpty) { return }
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import UIKit
class ScrollableLabel: UIScrollView {
var text: String {
get { return label.text ?? "" }
set(value) { label.text = value }
}
var textColor: UIColor {
get { return label.textColor }
set(value) { label.textColor = value }
}
private let label: UILabel
init() {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .right
self.label = label
super.init(frame: CGRect.zero)
self.isDirectionalLockEnabled = true
self.showsHorizontalScrollIndicator = false
self.showsVerticalScrollIndicator = false
addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leftAnchor.constraint(equalTo: self.contentLayoutGuide.leftAnchor),
label.topAnchor.constraint(equalTo: self.contentLayoutGuide.topAnchor),
label.bottomAnchor.constraint(equalTo: self.contentLayoutGuide.bottomAnchor),
label.rightAnchor.constraint(equalTo: self.contentLayoutGuide.rightAnchor),
label.heightAnchor.constraint(equalTo: self.heightAnchor),
])
// If label has less content, it should expand to fit the scrollView,
// so that right-alignment works in the label.
let expandToFitValueLabelConstraint = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal,
toItem: self, attribute: .width, multiplier: 1, constant: 0)
expandToFitValueLabelConstraint.priority = .defaultLow + 1
expandToFitValueLabelConstraint.isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@@ -12,7 +12,7 @@ class SettingsTableViewController: UITableViewController {
case exportZipArchive = "Export zip archive"
}
let settingsFieldsBySection : [[SettingsFields]] = [
let settingsFieldsBySection: [[SettingsFields]] = [
[.iosAppVersion, .goBackendVersion],
[.exportZipArchive]
]
@@ -47,15 +47,15 @@ class SettingsTableViewController: UITableViewController {
logo.frame = CGRect(x: 0, y: 0, width: width, height: height)
logo.bounds = logo.frame.insetBy(dx: 2, dy: 2)
self.tableView.tableFooterView = logo
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let logo = self.tableView.tableFooterView else { return }
let fullHeight = max(self.tableView.contentSize.height, self.tableView.bounds.size.height - self.tableView.layoutMargins.top)
let bottomPadding = max(self.tableView.layoutMargins.bottom, CGFloat(10))
let fullHeight = max(self.tableView.contentSize.height, self.tableView.bounds.size.height - self.tableView.layoutMargins.top - bottomPadding)
let e = logo.frame
let padding = CGFloat(10)
logo.frame = CGRect(x: e.minX, y: fullHeight - e.height - padding, width: e.width, height: e.height)
logo.frame = CGRect(x: e.minX, y: fullHeight - e.height, width: e.width, height: e.height)
}
@objc func doneTapped() {
@@ -172,7 +172,7 @@ class TunnelSettingsTableViewKeyValueCell: UITableViewCell {
set(value) { detailTextLabel?.text = value }
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: TunnelSettingsTableViewKeyValueCell.id)
}
@@ -197,7 +197,7 @@ class TunnelSettingsTableViewButtonCell: UITableViewCell {
let button: UIButton
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
button = UIButton(type: .system)
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(button)
@@ -9,7 +9,7 @@ class TunnelDetailTableViewController: UITableViewController {
let interfaceFields: [TunnelViewModel.InterfaceField] = [
.name, .publicKey, .addresses,
.listenPort, .mtu, .dns
.listenPort, .mtu, .dns
]
let peerFields: [TunnelViewModel.PeerField] = [
@@ -125,7 +125,7 @@ extension TunnelDetailTableViewController {
return "Status"
} else if (section == 1) {
// Interface
return "Interface"
return "Interface"
} else if ((numberOfPeerSections > 0) && (section < (2 + numberOfPeerSections))) {
// Peer
return "Peer"
@@ -227,7 +227,7 @@ class TunnelDetailTableViewStatusCell: UITableViewCell {
let statusSwitch: UISwitch
private var statusObservervationToken: AnyObject?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
statusSwitch = UISwitch()
super.init(style: .default, reuseIdentifier: TunnelDetailTableViewKeyValueCell.id)
accessoryView = statusSwitch
@@ -296,7 +296,7 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
set(value) { keyLabel.text = value }
}
var value: String {
get { return valueLabel.text ?? "" }
get { return valueLabel.text }
set(value) { valueLabel.text = value }
}
@@ -305,39 +305,17 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
}
let keyLabel: UILabel
let valueLabel: UILabel
let valueScroller: UIScrollView
let valueLabel: ScrollableLabel
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
keyLabel = UILabel()
valueLabel = UILabel()
valueScroller = UIScrollView()
valueLabel = ScrollableLabel()
keyLabel.textColor = UIColor.black
valueLabel.textColor = UIColor.gray
valueScroller.isDirectionalLockEnabled = true
valueScroller.showsHorizontalScrollIndicator = false
valueScroller.showsVerticalScrollIndicator = false
super.init(style: style, reuseIdentifier: reuseIdentifier)
valueScroller.addSubview(valueLabel)
valueLabel.translatesAutoresizingMaskIntoConstraints = false
valueLabel.textAlignment = .right
NSLayoutConstraint.activate([
valueLabel.leftAnchor.constraint(equalTo: valueScroller.contentLayoutGuide.leftAnchor),
valueLabel.topAnchor.constraint(equalTo: valueScroller.contentLayoutGuide.topAnchor),
valueLabel.bottomAnchor.constraint(equalTo: valueScroller.contentLayoutGuide.bottomAnchor),
valueLabel.rightAnchor.constraint(equalTo: valueScroller.contentLayoutGuide.rightAnchor),
valueLabel.heightAnchor.constraint(equalTo: valueScroller.heightAnchor),
])
// Value label should expand to fit the scrollView, so that the right-alignment works
let expandToFitValueLabelConstraint = NSLayoutConstraint(item: valueLabel, attribute: .width, relatedBy: .equal,
toItem: valueScroller, attribute: .width, multiplier: 1, constant: 0)
expandToFitValueLabelConstraint.priority = .defaultLow + 1
expandToFitValueLabelConstraint.isActive = true
contentView.addSubview(keyLabel)
keyLabel.translatesAutoresizingMaskIntoConstraints = false
keyLabel.textAlignment = .left
@@ -346,19 +324,19 @@ class TunnelDetailTableViewKeyValueCell: CopyableLabelTableViewCell {
keyLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
])
contentView.addSubview(valueScroller)
valueScroller.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(valueLabel)
valueLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
valueScroller.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
valueScroller.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
valueScroller.leftAnchor.constraint(equalTo: keyLabel.rightAnchor, constant: 8)
valueLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
valueLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
valueLabel.leftAnchor.constraint(equalTo: keyLabel.rightAnchor, constant: 8)
])
// Key label should never appear truncated
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal)
// Key label should hug it's content; value label should not.
keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
valueScroller.setContentHuggingPriority(.defaultLow, for: .horizontal)
valueLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
}
required init?(coder aDecoder: NSCoder) {
@@ -387,7 +365,7 @@ class TunnelDetailTableViewButtonCell: UITableViewCell {
let button: UIButton
var buttonStandardTintColor: UIColor
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
button = UIButton(type: .system)
buttonStandardTintColor = button.tintColor
super.init(style: style, reuseIdentifier: reuseIdentifier)
@@ -61,6 +61,7 @@ class TunnelEditTableViewController: UITableViewController {
self.tableView.allowsSelection = false
self.tableView.register(TunnelEditTableViewKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewKeyValueCell.id)
self.tableView.register(TunnelEditTableViewReadOnlyKeyValueCell.self, forCellReuseIdentifier: TunnelEditTableViewReadOnlyKeyValueCell.id)
self.tableView.register(TunnelEditTableViewButtonCell.self, forCellReuseIdentifier: TunnelEditTableViewButtonCell.id)
self.tableView.register(TunnelEditTableViewSwitchCell.self, forCellReuseIdentifier: TunnelEditTableViewSwitchCell.id)
}
@@ -185,6 +186,11 @@ extension TunnelEditTableViewController {
}
}
return cell
} else if (field == .publicKey) {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewReadOnlyKeyValueCell.id, for: indexPath) as! TunnelEditTableViewReadOnlyKeyValueCell
cell.key = field.rawValue
cell.value = interfaceData[field]
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: TunnelEditTableViewKeyValueCell.id, for: indexPath) as! TunnelEditTableViewKeyValueCell
// Set key
@@ -206,10 +212,6 @@ extension TunnelEditTableViewController {
case .publicKey: break
case .generateKeyPair: break
}
// Set editable
if (field == .publicKey) {
cell.isValueEditable = false
}
// Set keyboardType
if (field == .mtu || field == .listenPort) {
cell.keyboardType = .numberPad
@@ -403,7 +405,7 @@ extension TunnelEditTableViewController {
}
}
class TunnelEditTableViewKeyValueCell: CopyableLabelTableViewCell {
class TunnelEditTableViewKeyValueCell: UITableViewCell {
static let id: String = "TunnelEditTableViewKeyValueCell"
var key: String {
get { return keyLabel.text ?? "" }
@@ -417,19 +419,10 @@ class TunnelEditTableViewKeyValueCell: CopyableLabelTableViewCell {
get { return valueTextField.placeholder ?? "" }
set(value) { valueTextField.placeholder = value }
}
var isValueEditable: Bool {
get { return valueTextField.isEnabled }
set(value) {
super.copyableGesture = !value
valueTextField.isEnabled = value
keyLabel.textColor = value ? UIColor.black : UIColor.gray
valueTextField.textColor = value ? UIColor.black : UIColor.gray
}
}
var isValueValid: Bool = true {
didSet {
if (isValueValid) {
keyLabel.textColor = isValueEditable ? UIColor.black : UIColor.gray
keyLabel.textColor = UIColor.black
} else {
keyLabel.textColor = UIColor.red
}
@@ -448,18 +441,17 @@ class TunnelEditTableViewKeyValueCell: CopyableLabelTableViewCell {
private var textFieldValueOnBeginEditing: String = ""
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
keyLabel = UILabel()
valueTextField = UITextField()
super.init(style: style, reuseIdentifier: reuseIdentifier)
isValueEditable = true
contentView.addSubview(keyLabel)
keyLabel.translatesAutoresizingMaskIntoConstraints = false
keyLabel.textAlignment = .right
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width,
relatedBy: .equal,
toItem: self, attribute: .width,
multiplier: 0.4, constant: 0)
relatedBy: .equal,
toItem: self, attribute: .width,
multiplier: 0.4, constant: 0)
// The "Persistent Keepalive" key doesn't fit into 0.4 * width on the iPhone SE,
// so set a CR priority > the 0.4-constraint's priority.
widthRatioConstraint.priority = .defaultHigh + 1
@@ -483,10 +475,6 @@ class TunnelEditTableViewKeyValueCell: CopyableLabelTableViewCell {
valueTextField.spellCheckingType = .no
}
override var textToCopy: String? {
return self.valueTextField.text
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@@ -496,7 +484,6 @@ class TunnelEditTableViewKeyValueCell: CopyableLabelTableViewCell {
key = ""
value = ""
placeholderText = ""
isValueEditable = true
isValueValid = true
keyboardType = .default
onValueChanged = nil
@@ -524,6 +511,70 @@ extension TunnelEditTableViewKeyValueCell: UITextFieldDelegate {
}
}
class TunnelEditTableViewReadOnlyKeyValueCell: CopyableLabelTableViewCell {
static let id: String = "TunnelEditTableViewReadOnlyKeyValueCell"
var key: String {
get { return keyLabel.text ?? "" }
set(value) {keyLabel.text = value }
}
var value: String {
get { return valueLabel.text }
set(value) { valueLabel.text = value }
}
let keyLabel: UILabel
let valueLabel: ScrollableLabel
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
keyLabel = UILabel()
valueLabel = ScrollableLabel()
super.init(style: style, reuseIdentifier: reuseIdentifier)
keyLabel.textColor = UIColor.gray
valueLabel.textColor = UIColor.gray
contentView.addSubview(keyLabel)
keyLabel.translatesAutoresizingMaskIntoConstraints = false
keyLabel.textAlignment = .right
let widthRatioConstraint = NSLayoutConstraint(item: keyLabel, attribute: .width,
relatedBy: .equal,
toItem: self, attribute: .width,
multiplier: 0.4, constant: 0)
// In case the key doesn't fit into 0.4 * width,
// so set a CR priority > the 0.4-constraint's priority.
widthRatioConstraint.priority = .defaultHigh + 1
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 2, for: .horizontal)
NSLayoutConstraint.activate([
keyLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
keyLabel.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
widthRatioConstraint
])
contentView.addSubview(valueLabel)
valueLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
valueLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
valueLabel.leftAnchor.constraint(equalTo: keyLabel.rightAnchor, constant: 16),
valueLabel.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
])
}
override var textToCopy: String? {
return self.valueLabel.text
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
key = ""
value = ""
}
}
class TunnelEditTableViewButtonCell: UITableViewCell {
static let id: String = "TunnelEditTableViewButtonCell"
var buttonText: String {
@@ -539,7 +590,7 @@ class TunnelEditTableViewButtonCell: UITableViewCell {
let button: UIButton
var buttonStandardTintColor: UIColor
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
button = UIButton(type: .system)
buttonStandardTintColor = button.tintColor
super.init(style: style, reuseIdentifier: reuseIdentifier)
@@ -590,7 +641,7 @@ class TunnelEditTableViewSwitchCell: UITableViewCell {
let switchView: UISwitch
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
switchView = UISwitch()
super.init(style: .default, reuseIdentifier: reuseIdentifier)
accessoryView = switchView
@@ -25,7 +25,7 @@ class TunnelsListTableViewController: UIViewController {
self.navigationItem.leftBarButtonItem = settingsButtonItem
// Set up the busy indicator
let busyIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
let busyIndicator = UIActivityIndicatorView(style: .gray)
busyIndicator.hidesWhenStopped = true
// Add the busyIndicator, centered
@@ -176,20 +176,7 @@ class TunnelsListTableViewController: UIViewController {
}
func importFromFile(url: URL) {
// Import configurations from a .conf or a .zip file
if (url.pathExtension == "conf") {
let fileBaseName = url.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines)
if let fileContents = try? String(contentsOf: url),
let tunnelConfiguration = try? WgQuickConfigFileParser.parse(fileContents, name: fileBaseName) {
tunnelsManager?.add(tunnelConfiguration: tunnelConfiguration) { (_, error) in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
}
}
} else {
showErrorAlert(title: "Unable to import tunnel", message: "An error occured when importing the tunnel configuration.")
}
} else if (url.pathExtension == "zip") {
if (url.pathExtension == "zip") {
var unarchivedFiles: [(fileName: String, contents: Data)] = []
do {
unarchivedFiles = try ZipArchive.unarchive(url: url, requiredFileExtensions: ["conf"])
@@ -240,6 +227,18 @@ class TunnelsListTableViewController: UIViewController {
self?.showErrorAlert(title: "Created \(numberSuccessful) tunnels",
message: "Created \(numberSuccessful) of \(unarchivedFiles.count) tunnels from zip archive")
}
} else /* if (url.pathExtension == "conf") -- we assume everything else is a conf */ {
let fileBaseName = url.deletingPathExtension().lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines)
if let fileContents = try? String(contentsOf: url),
let tunnelConfiguration = try? WgQuickConfigFileParser.parse(fileContents, name: fileBaseName) {
tunnelsManager?.add(tunnelConfiguration: tunnelConfiguration) { (_, error) in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: self)
}
}
} else {
showErrorAlert(title: "Unable to import tunnel", message: "An error occured when importing the tunnel configuration.")
}
}
}
}
@@ -248,9 +247,7 @@ class TunnelsListTableViewController: UIViewController {
extension TunnelsListTableViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if let url = urls.first {
importFromFile(url: url)
}
urls.forEach(importFromFile)
}
}
@@ -258,7 +255,7 @@ extension TunnelsListTableViewController: UIDocumentPickerDelegate {
extension TunnelsListTableViewController: QRScanViewControllerDelegate {
func addScannedQRCode(tunnelConfiguration: TunnelConfiguration, qrScanViewController: QRScanViewController,
completionHandler: (() -> Void)?) {
completionHandler: (() -> Void)?) {
tunnelsManager?.add(tunnelConfiguration: tunnelConfiguration) { (_, error) in
if let error = error {
ErrorPresenter.showErrorAlert(error: error, from: qrScanViewController, onDismissal: completionHandler)
@@ -322,7 +319,7 @@ extension TunnelsListTableViewController: UITableViewDelegate {
}
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Delete", handler: { [weak self] (_, _, completionHandler) in
guard let tunnelsManager = self?.tunnelsManager else { return }
let tunnel = tunnelsManager.tunnel(at: indexPath.row)
@@ -386,9 +383,9 @@ class TunnelsListTableViewCell: UITableViewCell {
private var statusObservervationToken: AnyObject?
private var nameObservervationToken: AnyObject?
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
nameLabel = UILabel()
busyIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
busyIndicator = UIActivityIndicatorView(style: .gray)
busyIndicator.hidesWhenStopped = true
statusSwitch = UISwitch()
super.init(style: style, reuseIdentifier: reuseIdentifier)
+6 -2
View File
@@ -20,7 +20,9 @@ class DNSResolver {
var resolvedEndpoints: [Endpoint?] = Array<Endpoint?>(repeating: nil, count: endpoints.count)
for (i, endpoint) in self.endpoints.enumerated() {
guard let endpoint = endpoint else { continue }
if let resolvedEndpointStringInCache = DNSResolver.cache.object(forKey: endpoint.stringRepresentation() as NSString),
if (endpoint.hasHostAsIPAddress()) {
resolvedEndpoints[i] = endpoint
} else if let resolvedEndpointStringInCache = DNSResolver.cache.object(forKey: endpoint.stringRepresentation() as NSString),
let resolvedEndpointInCache = Endpoint(from: resolvedEndpointStringInCache as String) {
resolvedEndpoints[i] = resolvedEndpointInCache
} else {
@@ -38,7 +40,9 @@ class DNSResolver {
var isResolvedByDNSRequest: [Bool] = Array<Bool>(repeating: false, count: endpoints.count)
for (i, endpoint) in self.endpoints.enumerated() {
guard let endpoint = endpoint else { continue }
if let resolvedEndpointStringInCache = DNSResolver.cache.object(forKey: endpoint.stringRepresentation() as NSString),
if (endpoint.hasHostAsIPAddress()) {
resolvedEndpoints[i] = endpoint
} else if let resolvedEndpointStringInCache = DNSResolver.cache.object(forKey: endpoint.stringRepresentation() as NSString),
let resolvedEndpointInCache = Endpoint(from: resolvedEndpointStringInCache as String) {
resolvedEndpoints[i] = resolvedEndpointInCache
} else {
@@ -48,19 +48,23 @@ class PacketTunnelOptionsGenerator {
// Remote address
let remoteAddress: String
if let firstEndpoint = resolvedEndpoints.compactMap({ $0 }).first {
switch (firstEndpoint.host) {
/* iOS requires a tunnel endpoint, whereas in WireGuard it's valid for
* a tunnel to have no endpoint, or for there to be many endpoints, in
* which case, displaying a single one in settings doesn't really
* make sense. So, we fill it in with this placeholder, which is not
* a valid IP address that will actually route over the Internet.
*/
var remoteAddress: String = "0.0.0.0"
let endpointsCompact = resolvedEndpoints.compactMap({ $0 })
if endpointsCompact.count == 1 {
switch (endpointsCompact.first!.host) {
case .ipv4(let address):
remoteAddress = "\(address)"
case .ipv6(let address):
remoteAddress = "\(address)"
default:
fatalError("Endpoint must be resolved")
break
}
} else {
// We don't have any peer with an endpoint
remoteAddress = ""
}
options[.remoteAddress] = remoteAddress as NSObject
+15 -19
View File
@@ -13,7 +13,6 @@ protocol TunnelsManagerDelegate: class {
}
enum TunnelActivationError: Error {
case noEndpoint
case dnsResolutionFailed
case tunnelActivationFailed
case attemptingActivationWhenAnotherTunnelIsBusy(otherTunnelStatus: TunnelStatus)
@@ -214,14 +213,20 @@ extension NETunnelProviderProtocol {
self.init()
let appId = Bundle.main.bundleIdentifier!
let firstValidEndpoint = tunnelConfiguration.peers.first(where: { $0.endpoint != nil })?.endpoint
providerBundleIdentifier = "\(appId).network-extension"
providerConfiguration = [
"tunnelConfiguration": serializedTunnelConfiguration,
"tunnelConfigurationVersion": 1
]
serverAddress = firstValidEndpoint?.stringRepresentation() ?? "Unspecified"
let endpoints = tunnelConfiguration.peers.compactMap({$0.endpoint})
if endpoints.count == 1 {
serverAddress = endpoints.first!.stringRepresentation()
} else if endpoints.isEmpty {
serverAddress = "Unspecified"
} else {
serverAddress = "Multiple endpoints"
}
username = tunnelConfiguration.interface.name
}
@@ -262,15 +267,6 @@ class TunnelContainer: NSObject {
guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() }
let endpoints = tunnelConfiguration.peers.map { $0.endpoint }
// Ensure there's a tunner server address we can give to iOS
guard (endpoints.contains(where: { $0 != nil })) else {
DispatchQueue.main.async { [weak self] in
self?.status = .inactive
completionHandler(TunnelActivationError.noEndpoint)
}
return
}
// Resolve DNS and start the tunnel
let dnsResolver = DNSResolver(endpoints: endpoints)
let resolvedEndpoints = dnsResolver.resolveWithoutNetworkRequests()
@@ -293,8 +289,8 @@ class TunnelContainer: NSObject {
return
}
s.startActivation(tunnelConfiguration: tunnelConfiguration,
resolvedEndpoints: resolvedEndpoints,
completionHandler: completionHandler)
resolvedEndpoints: resolvedEndpoints,
completionHandler: completionHandler)
}
}
}
@@ -360,10 +356,10 @@ class TunnelContainer: NSObject {
return
}
guard (vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale) else {
os_log("Failed to activate tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive
completionHandler(error)
return
os_log("Failed to activate tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error)")
status = .inactive
completionHandler(error)
return
}
assert(vpnError.code == NEVPNError.configurationInvalid || vpnError.code == NEVPNError.configurationStale)
os_log("startActivation: Error says: %{public}@", log: OSLog.default, type: .debug,
@@ -1,6 +1,5 @@
//
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
//
// SPDX-License-Identifier: MIT
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
import NetworkExtension
import os.log
@@ -17,7 +16,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// MARK: Properties
private var wgHandle: Int32?
private var wgContext: WireGuardContext?
// MARK: NEPacketTunnelProvider
@@ -64,9 +62,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
configureLogger()
wgContext = WireGuardContext(packetFlow: self.packetFlow)
let handle = connect(interfaceName: interfaceName, settings: wireguardSettings, mtu: mtu.uint16Value)
let fd = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
if fd < 0 {
os_log("Starting tunnel failed: Could not determine file descriptor", log: OSLog.default, type: .error)
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotStartWireGuard)
return
}
let handle = connect(interfaceName: interfaceName, settings: wireguardSettings, fd: fd)
if handle < 0 {
os_log("Starting tunnel failed: Could not start WireGuard", log: OSLog.default, type: .error)
@@ -92,7 +95,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
networkSettings.ipv4Settings = ipv4Settings
// IPv6 settings
let ipv6Settings = NEIPv6Settings(addresses: ipv6Addresses, networkPrefixLengths: ipv6NetworkPrefixLengths)
/* Big fat ugly hack for broken iOS networking stack: the smallest prefix that will have
* any effect on iOS is a /120, so we clamp everything above to /120. This is potentially
* very bad, if various network parameters were actually relying on that subnet being
* intentionally small. TODO: talk about this with upstream iOS devs.
*/
let ipv6Settings = NEIPv6Settings(addresses: ipv6Addresses, networkPrefixLengths: ipv6NetworkPrefixLengths.map { NSNumber(value: min(120, $0.intValue)) })
assert(ipv6IncludedRouteAddresses.count == ipv6IncludedRouteNetworkPrefixLengths.count)
ipv6Settings.includedRoutes = zip(ipv6IncludedRouteAddresses, ipv6IncludedRouteNetworkPrefixLengths).map {
NEIPv6Route(destinationAddress: $0.0, networkPrefixLength: $0.1)
@@ -108,9 +117,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// MTU
if (mtu == 0) {
// 0 imples automatic MTU, where we set overhead as 95 bytes,
// 80 for WireGuard and the 15 to make sure WireGuard's padding will work.
networkSettings.tunnelOverheadBytes = 95
// 0 imples automatic MTU, where we set overhead as 80 bytes, which is the worst case for WireGuard
networkSettings.tunnelOverheadBytes = 80
} else {
networkSettings.mtu = mtu
}
@@ -128,7 +136,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
/// Begin the process of stopping the tunnel.
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
os_log("Stopping tunnel", log: OSLog.default, type: .info)
wgContext?.closeTunnel()
if let handle = wgHandle {
wgTurnOff(handle)
}
@@ -153,99 +160,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
}
private func connect(interfaceName: String, settings: String, mtu: UInt16) -> Int32 { // swiftlint:disable:this cyclomatic_complexity
private func connect(interfaceName: String, settings: String, fd: Int32) -> Int32 { // swiftlint:disable:this cyclomatic_complexity
return withStringsAsGoStrings(interfaceName, settings) { (nameGoStr, settingsGoStr) -> Int32 in
return withUnsafeMutablePointer(to: &wgContext) { (wgCtxPtr) -> Int32 in
return wgTurnOn(nameGoStr, settingsGoStr, mtu, { (wgCtxPtr, buf, len) -> Int in
autoreleasepool {
// read_fn: Read from the TUN interface and pass it on to WireGuard
guard let wgCtxPtr = wgCtxPtr else { return 0 }
guard let buf = buf else { return 0 }
let wgContext = wgCtxPtr.bindMemory(to: WireGuardContext.self, capacity: 1).pointee
var isTunnelClosed = false
let packet = wgContext.readPacket(isTunnelClosed: &isTunnelClosed)
if isTunnelClosed { return -1 }
guard let packetData = packet?.data else { return 0 }
if packetData.count <= len {
packetData.copyBytes(to: buf, count: packetData.count)
return packetData.count
}
return 0
}
}, { (wgCtxPtr, buf, len) -> Int in
autoreleasepool {
// write_fn: Receive packets from WireGuard and write to the TUN interface
guard let wgCtxPtr = wgCtxPtr else { return 0 }
guard let buf = buf else { return 0 }
guard len > 0 else { return 0 }
let wgContext = wgCtxPtr.bindMemory(to: WireGuardContext.self, capacity: 1).pointee
let ipVersionBits = (buf[0] & 0xf0) >> 4
let ipVersion: sa_family_t? = {
if ipVersionBits == 4 { return sa_family_t(AF_INET) } // IPv4
if ipVersionBits == 6 { return sa_family_t(AF_INET6) } // IPv6
return nil
}()
guard let protocolFamily = ipVersion else { fatalError("Unknown IP version") }
let packet = NEPacket(data: Data(bytes: buf, count: len), protocolFamily: protocolFamily)
var isTunnelClosed = false
let isWritten = wgContext.writePacket(packet: packet, isTunnelClosed: &isTunnelClosed)
if isTunnelClosed { return -1 }
if isWritten {
return len
}
return 0
}
},
wgCtxPtr)
}
return wgTurnOn(nameGoStr, settingsGoStr, fd)
}
}
}
class WireGuardContext {
private var packetFlow: NEPacketTunnelFlow
private var outboundPackets: [NEPacket] = []
private var isTunnelClosed: Bool = false
private var readPacketCondition = NSCondition()
init(packetFlow: NEPacketTunnelFlow) {
self.packetFlow = packetFlow
}
func closeTunnel() {
isTunnelClosed = true
readPacketCondition.signal()
}
func packetsRead(packets: [NEPacket]) {
readPacketCondition.lock()
outboundPackets.append(contentsOf: packets)
readPacketCondition.unlock()
readPacketCondition.signal()
}
func readPacket(isTunnelClosed: inout Bool) -> NEPacket? {
if outboundPackets.isEmpty {
readPacketCondition.lock()
packetFlow.readPacketObjects(completionHandler: packetsRead)
while outboundPackets.isEmpty && !self.isTunnelClosed {
readPacketCondition.wait()
}
readPacketCondition.unlock()
}
isTunnelClosed = self.isTunnelClosed
if !outboundPackets.isEmpty {
return outboundPackets.removeFirst()
}
return nil
}
func writePacket(packet: NEPacket, isTunnelClosed: inout Bool) -> Bool {
isTunnelClosed = self.isTunnelClosed
return packetFlow.writePacketObjects([packet])
}
}
private func withStringsAsGoStrings<R>(_ str1: String, _ str2: String, closure: (gostring_t, gostring_t) -> R) -> R {
return str1.withCString { (s1cStr) -> R in
let gstr1 = gostring_t(p: s1cStr, n: str1.utf8.count)
+2 -2
View File
@@ -28,7 +28,7 @@ version-header: $(DESTDIR)/wireguard-go-version.h
GOBUILDARCH := $(GOARCH_$(shell uname -m))
GOBUILDOS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
GOBUILDVERSION := 1.11.1
GOBUILDVERSION := 1.11.2
GOBUILDTARBALL := https://dl.google.com/go/go$(GOBUILDVERSION).$(GOBUILDOS)-$(GOBUILDARCH).tar.gz
GOBUILDVERSION_NEEDED := go version go$(GOBUILDVERSION) $(GOBUILDOS)/$(GOBUILDARCH)
export GOROOT := $(BUILDDIR)/goroot
@@ -68,7 +68,7 @@ $(BUILDDIR)/libwg-go-$(1).a: $(BUILDDIR)/.prepared
CGO_CFLAGS="$(CGO_FLAGS_$(1))" \
CGO_LDFLAGS="$(CGO_FLAGS_$(1))" \
GOARCH="$(GOARCH_$(1))" \
go build -tags ios -v -o "$(BUILDDIR)/libwg-go-$(1).a" -buildmode c-archive && go version > "$(BUILDDIR)/.gobuildversion"; \
go build -tags ios -ldflags=-w -v -o "$(BUILDDIR)/libwg-go-$(1).a" -buildmode c-archive && go version > "$(BUILDDIR)/.gobuildversion"; \
ret=$$$$?; \
rm -f "$(BUILDDIR)/libwg-go-$(1).h"; \
exit $$$$ret
+7 -5
View File
@@ -15,6 +15,7 @@ import "C"
import (
"bufio"
"errors"
"git.zx2c4.com/wireguard-go/tun"
"golang.org/x/sys/unix"
"io/ioutil"
@@ -25,7 +26,6 @@ import (
"runtime"
"strings"
"unsafe"
"errors"
)
var loggerFunc unsafe.Pointer
@@ -75,7 +75,7 @@ func wgSetLogger(loggerFn uintptr) {
}
//export wgTurnOn
func wgTurnOn(ifnameRef string, settings string, mtu uint16, readFn uintptr, writeFn uintptr, ctx uintptr) int32 {
func wgTurnOn(ifnameRef string, settings string, tunFd int32) int32 {
interfaceName := string([]byte(ifnameRef))
logger := &Logger{
@@ -86,12 +86,14 @@ func wgTurnOn(ifnameRef string, settings string, mtu uint16, readFn uintptr, wri
logger.Debug.Println("Debug log enabled")
tun := tun.CreateTUN(mtu, unsafe.Pointer(readFn), unsafe.Pointer(writeFn), unsafe.Pointer(ctx))
tun, _, err := tun.CreateTUNFromFD(int(tunFd))
if err != nil {
logger.Error.Println(err)
return -1
}
logger.Info.Println("Attaching to interface")
device := NewDevice(tun, logger)
logger.Debug.Println("Interface has MTU", device.tun.mtu)
bufferedSettings := bufio.NewReadWriter(bufio.NewReader(strings.NewReader(settings)), bufio.NewWriter(ioutil.Discard))
setError := ipcSetOperation(device, bufferedSettings)
if setError != nil {
+5 -5
View File
@@ -8,9 +8,9 @@ package main
/* Fit within memory limits for iOS */
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = 1700
PreallocatedBuffersPerPool = 1024
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MaxSegmentSize = 1700
PreallocatedBuffersPerPool = 1024
)
+52
View File
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package tun
import (
"git.zx2c4.com/wireguard-go/rwcancel"
"golang.org/x/sys/unix"
"net"
"os"
)
func CreateTUNFromFD(tunFd int) (TUNDevice, string, error) {
file := os.NewFile(uintptr(tunFd), "/dev/tun")
tun := &nativeTun{
tunFile: file,
fd: file.Fd(),
events: make(chan TUNEvent, 5),
errors: make(chan error, 5),
}
var err error
tun.rwcancel, err = rwcancel.NewRWCancel(tunFd)
if err != nil {
return nil, "", err
}
name, err := tun.Name()
if err != nil {
tun.rwcancel.Cancel()
return nil, "", err
}
tunIfindex, err := func() (int, error) {
iface, err := net.InterfaceByName(name)
if err != nil {
return -1, err
}
return iface.Index, nil
}()
if err != nil {
tun.tunFile.Close()
return nil, "", err
}
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
if err != nil {
tun.tunFile.Close()
return nil, "", err
}
go tun.routineRouteListener(tunIfindex)
return tun, name, nil
}
-90
View File
@@ -1,90 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package tun
// #include <sys/types.h>
// static ssize_t callFnWithCtx(const void *func, const void *ctx, const void *buffer, size_t len)
// {
// return ((ssize_t(*)(const void *, const unsigned char *, size_t))func)(ctx, buffer, len);
// }
import "C"
import (
"os"
"syscall"
"unsafe"
)
type nativeTun struct {
events chan TUNEvent
mtu int
readFn unsafe.Pointer
writeFn unsafe.Pointer
ctx unsafe.Pointer
}
func CreateTUN(mtu uint16, readFn unsafe.Pointer, writeFn unsafe.Pointer, ctx unsafe.Pointer) TUNDevice {
if mtu == 0 {
/* 0 means automatic MTU, which iOS makes outerMTU-80-15. The 80 is for
* WireGuard and the 15 ensures our padding will work. Therefore, it's
* safe to have this code assume a massive MTU.
*/
mtu = ^mtu
}
tun := &nativeTun{
events: make(chan TUNEvent, 10),
mtu: int(mtu),
readFn: readFn,
writeFn: writeFn,
ctx: ctx,
}
tun.events <- TUNEventUp
return tun
}
func (tun *nativeTun) Name() (string, error) {
return "tun", nil
}
func (tun *nativeTun) File() *os.File {
return nil
}
func (tun *nativeTun) Events() chan TUNEvent {
return tun.events
}
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
ret := C.callFnWithCtx(tun.readFn, tun.ctx, unsafe.Pointer(&buff[offset]), C.size_t(len(buff) - offset))
if ret < 0 {
return 0, syscall.Errno(-ret)
}
return int(ret), nil
}
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
ret := C.callFnWithCtx(tun.writeFn, tun.ctx, unsafe.Pointer(&buff[offset]), C.size_t(len(buff) - offset))
if ret < 0 {
return 0, syscall.Errno(-ret)
}
return int(ret), nil
}
func (tun *nativeTun) Close() error {
if tun.events != nil {
close(tun.events)
}
return nil
}
func (tun *nativeTun) setMTU(n int) error {
tun.mtu = n
return nil
}
func (tun *nativeTun) MTU() (int, error) {
return tun.mtu, nil
}
+1 -2
View File
@@ -10,10 +10,9 @@
#include <stdint.h>
typedef struct { const char *p; size_t n; } gostring_t;
typedef ssize_t(*read_write_fn_t)(void *ctx, unsigned char *buf, size_t len);
typedef void(*logger_fn_t)(int level, const char *msg);
extern void wgSetLogger(logger_fn_t logger_fn);
extern int wgTurnOn(gostring_t ifname, gostring_t settings, uint16_t mtu, read_write_fn_t read_fn, read_write_fn_t write_fn, void *ctx);
extern int wgTurnOn(gostring_t ifname, gostring_t settings, int32_t tun_fd);
extern void wgTurnOff(int handle);
extern char *wgVersion();