Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68d928192b | |||
| 028e76eb3f | |||
| cb0c965294 | |||
| d7ce621cb2 | |||
| a1ca4f6eb5 | |||
| 437f0dc46d |
@@ -1,2 +1,2 @@
|
||||
VERSION_NAME = 0.0.20191015
|
||||
VERSION_ID = 15
|
||||
VERSION_NAME = 0.0.20191105
|
||||
VERSION_ID = 16
|
||||
|
||||
@@ -20,17 +20,23 @@ protocol TunnelsManagerActivationDelegate: class {
|
||||
}
|
||||
|
||||
class TunnelsManager {
|
||||
private var tunnels: [TunnelContainer]
|
||||
fileprivate var tunnels: [TunnelContainer]
|
||||
weak var tunnelsListDelegate: TunnelsManagerListDelegate?
|
||||
weak var activationDelegate: TunnelsManagerActivationDelegate?
|
||||
private var statusObservationToken: AnyObject?
|
||||
private var waiteeObservationToken: AnyObject?
|
||||
private var configurationsObservationToken: AnyObject?
|
||||
private var catalinaWorkaround: Any?
|
||||
|
||||
init(tunnelProviders: [NETunnelProviderManager]) {
|
||||
tunnels = tunnelProviders.map { TunnelContainer(tunnel: $0) }.sorted { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) }
|
||||
startObservingTunnelStatuses()
|
||||
startObservingTunnelConfigurations()
|
||||
#if os(macOS)
|
||||
if #available(macOS 10.15, *) {
|
||||
self.catalinaWorkaround = CatalinaWorkaround(tunnelsManager: self)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static func create(completionHandler: @escaping (Result<TunnelsManager, TunnelsManagerError>) -> Void) {
|
||||
@@ -75,7 +81,15 @@ class TunnelsManager {
|
||||
tunnelManagers.remove(at: index)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
if #available(macOS 10.15, *) {
|
||||
// Don't delete orphaned keychain refs. We need them to restore tunnels as a workaround.
|
||||
} else {
|
||||
Keychain.deleteReferences(except: refs)
|
||||
}
|
||||
#else
|
||||
Keychain.deleteReferences(except: refs)
|
||||
#endif
|
||||
#if os(iOS)
|
||||
RecentTunnelsTracker.cleanupTunnels(except: tunnelNames)
|
||||
#endif
|
||||
@@ -622,7 +636,7 @@ class TunnelContainer: NSObject {
|
||||
}
|
||||
|
||||
extension NETunnelProviderManager {
|
||||
private static var cachedConfigKey: UInt8 = 0
|
||||
fileprivate static var cachedConfigKey: UInt8 = 0
|
||||
|
||||
var tunnelConfiguration: TunnelConfiguration? {
|
||||
if let cached = objc_getAssociatedObject(self, &NETunnelProviderManager.cachedConfigKey) as? TunnelConfiguration {
|
||||
@@ -645,3 +659,148 @@ extension NETunnelProviderManager {
|
||||
return localizedDescription == tunnel.name && tunnelConfiguration == tunnel.tunnelConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
@available(macOS 10.15, *)
|
||||
class CatalinaWorkaround {
|
||||
|
||||
// In macOS Catalina, for some users, the tunnels get deleted arbitrarily
|
||||
// by the OS. It's not clear what triggers that.
|
||||
|
||||
// As a workaround, in macOS Catalina, when we realize that tunnels have been
|
||||
// deleted outside the app, we reinstate those tunnels using the information
|
||||
// in the keychain.
|
||||
|
||||
unowned let tunnelsManager: TunnelsManager
|
||||
private var configChangeSubscriber: Any?
|
||||
|
||||
struct ReinstationData {
|
||||
let tunnelConfiguration: TunnelConfiguration
|
||||
let keychainPasswordRef: Data
|
||||
}
|
||||
|
||||
init(tunnelsManager: TunnelsManager) {
|
||||
self.tunnelsManager = tunnelsManager
|
||||
|
||||
// Attempt reinstation when there's a change in tunnel configurations,
|
||||
// which indicates that tunnels may have been deleted outside the app.
|
||||
// We use debounce to wait for all change notifications to arrive
|
||||
// before attempting to reinstate, so that we don't have saveToPreferences
|
||||
// being called while another saveToPreferences is in progress.
|
||||
self.configChangeSubscriber = NotificationCenter.default
|
||||
.publisher(for: .NEVPNConfigurationChange, object: nil)
|
||||
.debounce(for: .seconds(1), scheduler: RunLoop.main)
|
||||
.subscribe(on: RunLoop.main)
|
||||
.sink { [weak self] _ in
|
||||
self?.reinstateTunnelsDeletedOutsideApp()
|
||||
}
|
||||
|
||||
// Attempt reinstation on app launch
|
||||
reinstateTunnelsDeletedOutsideApp()
|
||||
}
|
||||
|
||||
func reinstateTunnelsDeletedOutsideApp() {
|
||||
let rd = reinstationDataForTunnelsDeletedOutsideApp()
|
||||
reinstateTunnels(ArraySlice(rd), completionHandler: nil)
|
||||
}
|
||||
|
||||
private func reinstateTunnels(_ rdArray: ArraySlice<ReinstationData>, completionHandler: (() -> Void)?) {
|
||||
guard let head = rdArray.first else {
|
||||
completionHandler?()
|
||||
return
|
||||
}
|
||||
let tail = rdArray.dropFirst()
|
||||
self.tunnelsManager.reinstateTunnel(reinstationData: head) { _ in
|
||||
DispatchQueue.main.async {
|
||||
self.reinstateTunnels(tail, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func reinstationDataForTunnelsDeletedOutsideApp() -> [ReinstationData] {
|
||||
let knownRefs: [Data] = self.tunnelsManager.tunnels
|
||||
.compactMap { $0.tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol }
|
||||
.compactMap { $0.passwordReference }
|
||||
let knownRefsSet: Set<Data> = Set(knownRefs)
|
||||
var result: CFTypeRef?
|
||||
let ret = SecItemCopyMatching([kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: Bundle.main.bundleIdentifier as Any,
|
||||
kSecMatchLimit as String: kSecMatchLimitAll,
|
||||
kSecReturnAttributes as String: true,
|
||||
kSecReturnPersistentRef as String: true] as CFDictionary,
|
||||
&result)
|
||||
guard ret == errSecSuccess, let resultDicts = result as? [[String: Any]] else { return [] }
|
||||
let labelPrefix = "WireGuard Tunnel: "
|
||||
var reinstationData: [ReinstationData] = []
|
||||
for resultDict in resultDicts {
|
||||
guard let ref = resultDict[kSecValuePersistentRef as String] as? Data else { continue }
|
||||
guard let label = resultDict[kSecAttrLabel as String] as? String else { continue }
|
||||
guard label.hasPrefix(labelPrefix) else { continue }
|
||||
if !knownRefsSet.contains(ref) {
|
||||
let tunnelName = String(label.dropFirst(labelPrefix.count))
|
||||
if let configStr = Keychain.openReference(called: ref),
|
||||
let config = try? TunnelConfiguration(fromWgQuickConfig: configStr, called: tunnelName) {
|
||||
reinstationData.append(ReinstationData(tunnelConfiguration: config, keychainPasswordRef: ref))
|
||||
}
|
||||
}
|
||||
}
|
||||
return reinstationData
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
@available(macOS 10.15, *)
|
||||
extension TunnelsManager {
|
||||
fileprivate func reinstateTunnel(reinstationData: CatalinaWorkaround.ReinstationData, completionHandler: @escaping (Bool) -> Void) {
|
||||
let tunnelName = reinstationData.tunnelConfiguration.name ?? ""
|
||||
if tunnelName.isEmpty {
|
||||
completionHandler(false)
|
||||
return
|
||||
}
|
||||
|
||||
if tunnels.contains(where: { $0.name == tunnelName }) {
|
||||
completionHandler(false)
|
||||
return
|
||||
}
|
||||
|
||||
let tunnelProviderProtocol = NETunnelProviderProtocol()
|
||||
guard let appId = Bundle.main.bundleIdentifier else { fatalError() }
|
||||
tunnelProviderProtocol.providerBundleIdentifier = "\(appId).network-extension"
|
||||
tunnelProviderProtocol.passwordReference = reinstationData.keychainPasswordRef
|
||||
tunnelProviderProtocol.providerConfiguration = ["UID": getuid()]
|
||||
tunnelProviderProtocol.serverAddress = {
|
||||
let endpoints = reinstationData.tunnelConfiguration.peers.compactMap { $0.endpoint }
|
||||
if endpoints.count == 1 {
|
||||
return endpoints[0].stringRepresentation
|
||||
} else if endpoints.isEmpty {
|
||||
return "Unspecified"
|
||||
} else {
|
||||
return "Multiple endpoints"
|
||||
}
|
||||
}()
|
||||
|
||||
let tunnelProvider = NETunnelProviderManager()
|
||||
tunnelProvider.localizedDescription = tunnelName
|
||||
tunnelProvider.protocolConfiguration = tunnelProviderProtocol
|
||||
objc_setAssociatedObject(tunnelProvider, &NETunnelProviderManager.cachedConfigKey, reinstationData.tunnelConfiguration, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
tunnelProvider.isEnabled = true
|
||||
|
||||
tunnelProvider.saveToPreferences { [weak self] error in
|
||||
guard error == nil else {
|
||||
wg_log(.error, message: "Reinstate: Saving configuration failed: \(error!)")
|
||||
completionHandler(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let self = self else { return }
|
||||
|
||||
let tunnel = TunnelContainer(tunnel: tunnelProvider)
|
||||
self.tunnels.append(tunnel)
|
||||
self.tunnels.sort { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) }
|
||||
self.tunnelsListDelegate?.tunnelAdded(at: self.tunnels.firstIndex(of: tunnel)!)
|
||||
completionHandler(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -21,7 +21,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
}
|
||||
|
||||
let window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window.backgroundColor = .white
|
||||
if #available(iOS 13.0, *) {
|
||||
window.backgroundColor = .systemBackground
|
||||
} else {
|
||||
window.backgroundColor = .white
|
||||
}
|
||||
self.window = window
|
||||
|
||||
let mainVC = MainViewController()
|
||||
|
||||
@@ -41,7 +41,11 @@ class LogViewController: UIViewController {
|
||||
|
||||
override func loadView() {
|
||||
view = UIView()
|
||||
view.backgroundColor = .white
|
||||
if #available(iOS 13.0, *) {
|
||||
view.backgroundColor = .systemBackground
|
||||
} else {
|
||||
view.backgroundColor = .white
|
||||
}
|
||||
|
||||
view.addSubview(textView)
|
||||
textView.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -87,12 +91,18 @@ class LogViewController: UIViewController {
|
||||
let richText = NSMutableAttributedString()
|
||||
let bodyFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
|
||||
let captionFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.caption1)
|
||||
let lightGrayColor = UIColor(white: 0.88, alpha: 1.0)
|
||||
|
||||
for logEntry in fetchedLogEntries {
|
||||
let bgColor = self.isNextLineHighlighted ? lightGrayColor : UIColor.white
|
||||
let timestampText = NSAttributedString(string: logEntry.timestamp + "\n", attributes: [.font: captionFont, .backgroundColor: bgColor, .paragraphStyle: self.paragraphStyle])
|
||||
let messageText = NSAttributedString(string: logEntry.message + "\n", attributes: [.font: bodyFont, .backgroundColor: bgColor, .paragraphStyle: self.paragraphStyle])
|
||||
var bgColor: UIColor
|
||||
var fgColor: UIColor
|
||||
if #available(iOS 13.0, *) {
|
||||
bgColor = self.isNextLineHighlighted ? .systemGray3 : .systemBackground
|
||||
fgColor = .label
|
||||
} else {
|
||||
bgColor = self.isNextLineHighlighted ? UIColor(white: 0.88, alpha: 1.0) : UIColor.white
|
||||
fgColor = .black
|
||||
}
|
||||
let timestampText = NSAttributedString(string: logEntry.timestamp + "\n", attributes: [.font: captionFont, .backgroundColor: bgColor, .foregroundColor: fgColor, .paragraphStyle: self.paragraphStyle])
|
||||
let messageText = NSAttributedString(string: logEntry.message + "\n", attributes: [.font: bodyFont, .backgroundColor: bgColor, .foregroundColor: fgColor, .paragraphStyle: self.paragraphStyle])
|
||||
richText.append(timestampText)
|
||||
richText.append(messageText)
|
||||
self.isNextLineHighlighted.toggle()
|
||||
|
||||
@@ -11,7 +11,11 @@ class MainViewController: UISplitViewController {
|
||||
|
||||
init() {
|
||||
let detailVC = UIViewController()
|
||||
detailVC.view.backgroundColor = .white
|
||||
if #available(iOS 13.0, *) {
|
||||
detailVC.view.backgroundColor = .systemBackground
|
||||
} else {
|
||||
detailVC.view.backgroundColor = .white
|
||||
}
|
||||
let detailNC = UINavigationController(rootViewController: detailVC)
|
||||
|
||||
let masterVC = TunnelsListTableViewController()
|
||||
|
||||
@@ -52,7 +52,11 @@ class TunnelsListTableViewController: UIViewController {
|
||||
|
||||
override func loadView() {
|
||||
view = UIView()
|
||||
view.backgroundColor = .white
|
||||
if #available(iOS 13.0, *) {
|
||||
view.backgroundColor = .systemBackground
|
||||
} else {
|
||||
view.backgroundColor = .white
|
||||
}
|
||||
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
@@ -395,7 +399,11 @@ extension TunnelsListTableViewController: TunnelsManagerListDelegate {
|
||||
(splitViewController.viewControllers[0] as? UINavigationController)?.popToRootViewController(animated: false)
|
||||
} else {
|
||||
let detailVC = UIViewController()
|
||||
detailVC.view.backgroundColor = .white
|
||||
if #available(iOS 13.0, *) {
|
||||
detailVC.view.backgroundColor = .systemBackground
|
||||
} else {
|
||||
detailVC.view.backgroundColor = .white
|
||||
}
|
||||
let detailNC = UINavigationController(rootViewController: detailVC)
|
||||
splitViewController.showDetailViewController(detailNC, sender: self)
|
||||
}
|
||||
|
||||
@@ -99,10 +99,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
||||
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
||||
// sufficient quantities of users.
|
||||
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
|
||||
if osVersion.majorVersion <= 10 && osVersion.minorVersion <= 14 {
|
||||
exit(0)
|
||||
}
|
||||
exit(0)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ $(GOROOT)/.prepared:
|
||||
[ -n "$(REAL_GOROOT)" ]
|
||||
mkdir -p "$(GOROOT)"
|
||||
rsync -a --delete --exclude=pkg/obj/go-build "$(REAL_GOROOT)/" "$(GOROOT)/"
|
||||
patch -p1 -f -N -r- -d "$(GOROOT)" < goruntime-boottime-over-monotonic.diff
|
||||
cat goruntime-*.diff | patch -p1 -f -N -r- -d "$(GOROOT)"
|
||||
touch "$@"
|
||||
|
||||
define libwg-go-a
|
||||
|
||||
@@ -3,7 +3,8 @@ module golang.zx2c4.com/wireguard/ios
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954 // indirect
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be
|
||||
golang.zx2c4.com/wireguard v0.0.20190909-0.20191008144818-222f0f8000e8
|
||||
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59 // indirect
|
||||
golang.org/x/net v0.0.0-20191105084925-a882066a44e0 // indirect
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1
|
||||
)
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59 h1:PyXRxSVbvzDGuqYXjHndV7xDzJ7w2K8KD9Ef8GB7KOE=
|
||||
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823 h1:Ypyv6BNJh07T1pUSrehkLemqPKXhus2MkfktJ91kRh4=
|
||||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954 h1:JGZucVF/L/TotR719NbujzadOZ2AgnYlqphQGHDCKaU=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191105084925-a882066a44e0 h1:QPlSTtPE2k6PZPasQUbzuK3p9JbS+vMXYVto8g/yrsg=
|
||||
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c h1:6Zx7DRlKXf79yfxuQ/7GqV3w2y7aDsk6bGg0MzF5RVU=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 h1:ZJUmhYTp8GbGC0ViZRc2U+MIYQ8xx9MscsdXnclfIhw=
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.zx2c4.com/wireguard v0.0.20190909-0.20191008144818-222f0f8000e8 h1:BqfQHKZLrdq0j5Z/R9coISbr1nYcSE+3BdyF5LidO+g=
|
||||
golang.zx2c4.com/wireguard v0.0.20190909-0.20191008144818-222f0f8000e8/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 h1:KxtBKNgJUQG8vwZzJKkwBGOcqp95xLu6A6KIMde1kl0=
|
||||
golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
|
||||
Reference in New Issue
Block a user