Compare commits

..

7 Commits

Author SHA1 Message Date
Jason A. Donenfeld b954e9a4fd Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-04-23 06:49:16 +02:00
Roopesh Chander 76894fba68 Xcode: Use dwarf for debug and dwarf-with-dsym for release
Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-04-21 18:22:31 +05:30
Roopesh Chander 89a564ce62 Swift 5 migration: Make use of Result type
Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-04-21 17:51:42 +05:30
Roopesh Chander 178fe86d36 macOS: Detect when updating from the App Store
And show an alert when tunnels are active during updation -- that
might cause the update to not work correctly.

Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-04-21 15:43:10 +05:30
Jason A. Donenfeld 571349bb3d Version bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-04-12 10:32:06 +02:00
Roopesh Chander 98ebd55208 Log view: Don't use a global array to store log entries
Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-04-10 17:57:36 +05:30
Roopesh Chander 83d0d34411 macOS: Log view: Stop updating the log once the log view is dismissed
Signed-off-by: Roopesh Chander <roop@roopc.net>
2019-04-10 15:42:39 +05:30
18 changed files with 168 additions and 68 deletions
+2 -2
View File
@@ -107,7 +107,7 @@ err:
return ret;
}
uint32_t view_lines_from_cursor(const struct log *input_log, uint32_t cursor, void(*cb)(const char *, uint64_t))
uint32_t view_lines_from_cursor(const struct log *input_log, uint32_t cursor, void *ctx, void(*cb)(const char *, uint64_t, void *))
{
struct log *log;
uint32_t l, i = cursor;
@@ -132,7 +132,7 @@ uint32_t view_lines_from_cursor(const struct log *input_log, uint32_t cursor, vo
else
break;
}
cb(line->line, line->time_ns);
cb(line->line, line->time_ns, ctx);
cursor = (i + 1) % MAX_LINES;
}
free(log);
+1 -1
View File
@@ -11,7 +11,7 @@
struct log;
void write_msg_to_log(struct log *log, const char *tag, const char *msg);
int write_log_to_file(const char *file_name, const struct log *input_log);
uint32_t view_lines_from_cursor(const struct log *input_log, uint32_t cursor, void(*)(const char *, uint64_t));
uint32_t view_lines_from_cursor(const struct log *input_log, uint32_t cursor, void *ctx, void(*)(const char *, uint64_t, void *));
struct log *open_log(const char *file_name);
void close_log(struct log *log);
@@ -52,6 +52,7 @@
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0F44CA222D55FD00B0FF04 /* EditableTextCell.swift */; };
6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */; };
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
@@ -298,6 +299,7 @@
6F0F44CA222D55FD00B0FF04 /* EditableTextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTextCell.swift; sourceTree = "<group>"; };
6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteTunnelsConfirmationAlert.swift; sourceTree = "<group>"; };
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
@@ -632,6 +634,7 @@
6F89E17B21F090CC00C97BB9 /* TunnelsTracker.swift */,
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */,
6FB1BD6121D2607E00A991BF /* Assets.xcassets */,
6FB1BD6621D2607E00A991BF /* Info.plist */,
6FB1BD6721D2607E00A991BF /* WireGuard.entitlements */,
@@ -1304,6 +1307,7 @@
6FBA104321D6BC250051C35F /* ErrorPresenter.swift in Sources */,
6FB1BDC521D50F0300A991BF /* Endpoint.swift in Sources */,
6FB1BDC621D50F0300A991BF /* DNSServer.swift in Sources */,
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */,
6FB1BDC721D50F0300A991BF /* InterfaceConfiguration.swift in Sources */,
6F907C9D224663A2003CED21 /* LogViewHelper.swift in Sources */,
6FB1BDC821D50F0300A991BF /* PeerConfiguration.swift in Sources */,
@@ -1687,6 +1691,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -1752,6 +1757,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -405,3 +405,9 @@
"macUnusableTunnelMessage" = "The configuration for this tunnel cannot be found in the keychain.";
"macUnusableTunnelInfo" = "In case this tunnel was created by another user, only that user can view, edit, or activate this tunnel.";
"macUnusableTunnelButtonTitleDeleteTunnel" = "Delete tunnel";
// Mac App Store updating alert
"macAppStoreUpdatingAlertMessage" = "App Store would like to update WireGuard";
"macAppStoreUpdatingAlertInfoWithOnDemand (%@)" = "Please disable on-demand for tunnel %@, deactivate it, and then continue updating in App Store.";
"macAppStoreUpdatingAlertInfoWithoutOnDemand (%@)" = "Please deactivate tunnel %@ and then continue updating in App Store.";
+2 -2
View File
@@ -1,2 +1,2 @@
VERSION_NAME = 0.0.20190409
VERSION_ID = 6
VERSION_NAME = 0.0.20190423
VERSION_ID = 8
@@ -33,7 +33,7 @@ class TunnelsManager {
startObservingTunnelConfigurations()
}
static func create(completionHandler: @escaping (WireGuardResult<TunnelsManager>) -> Void) {
static func create(completionHandler: @escaping (Result<TunnelsManager, TunnelsManagerError>) -> Void) {
#if targetEnvironment(simulator)
completionHandler(.success(TunnelsManager(tunnelProviders: MockTunnels.createMockTunnels())))
#else
@@ -104,7 +104,7 @@ class TunnelsManager {
}
}
func add(tunnelConfiguration: TunnelConfiguration, onDemandOption: ActivateOnDemandOption = .off, completionHandler: @escaping (WireGuardResult<TunnelContainer>) -> Void) {
func add(tunnelConfiguration: TunnelConfiguration, onDemandOption: ActivateOnDemandOption = .off, completionHandler: @escaping (Result<TunnelContainer, TunnelsManagerError>) -> Void) {
let tunnelName = tunnelConfiguration.name ?? ""
if tunnelName.isEmpty {
completionHandler(.failure(TunnelsManagerError.tunnelNameEmpty))
@@ -167,9 +167,15 @@ class TunnelsManager {
let tail = tunnelConfigurations.dropFirst()
add(tunnelConfiguration: head) { [weak self, tail] result in
DispatchQueue.main.async {
let numberSuccessful = numberSuccessful + (result.isSuccess ? 1 : 0)
let lastError = lastError ?? (result.error as? TunnelsManagerError)
self?.addMultiple(tunnelConfigurations: tail, numberSuccessful: numberSuccessful, lastError: lastError, completionHandler: completionHandler)
var numberSuccessfulCount = numberSuccessful
var lastError: TunnelsManagerError?
switch result {
case .failure(let error):
lastError = error
case .success:
numberSuccessfulCount = numberSuccessful + 1
}
self?.addMultiple(tunnelConfigurations: tail, numberSuccessful: numberSuccessfulCount, lastError: lastError, completionHandler: completionHandler)
}
}
}
+9 -5
View File
@@ -21,7 +21,9 @@ public class LogViewHelper {
}
}
static var logEntries = [LogEntry]()
class LogEntries {
var entries: [LogEntry] = []
}
init?(logFilePath: String?) {
guard let logFilePath = logFilePath else { return nil }
@@ -34,19 +36,21 @@ public class LogViewHelper {
}
func fetchLogEntriesSinceLastFetch(completion: @escaping ([LogViewHelper.LogEntry]) -> Void) {
LogViewHelper.logEntries = []
var logEntries = LogEntries()
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else { return }
let newCursor = view_lines_from_cursor(self.log, self.cursor) { cStr, timestamp in
let newCursor = view_lines_from_cursor(self.log, self.cursor, &logEntries) { cStr, timestamp, ctx in
let message = cStr != nil ? String(cString: cStr!) : ""
let date = Date(timeIntervalSince1970: Double(timestamp) / 1000000000)
let dateString = ISO8601DateFormatter.string(from: date, timeZone: TimeZone.current, formatOptions: LogViewHelper.formatOptions)
LogViewHelper.logEntries.append(LogEntry(timestamp: dateString, message: message))
if let logEntries = ctx?.bindMemory(to: LogEntries.self, capacity: 1) {
logEntries.pointee.entries.append(LogEntry(timestamp: dateString, message: message))
}
}
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.cursor = newCursor
completion(LogViewHelper.logEntries)
completion(logEntries.entries)
}
}
}
+3 -3
View File
@@ -16,10 +16,10 @@ class TunnelImporter {
if url.pathExtension.lowercased() == "zip" {
dispatchGroup.enter()
ZipImporter.importConfigFiles(from: url) { result in
if let error = result.error {
switch result {
case .failure(let error):
lastFileImportErrorText = error.alertText
}
if let configsInZip = result.value {
case .success(let configsInZip):
configs.append(contentsOf: configsInZip)
}
dispatchGroup.leave()
@@ -42,19 +42,18 @@ class MainViewController: UISplitViewController {
TunnelsManager.create { [weak self] result in
guard let self = self else { return }
if let error = result.error {
switch result {
case .failure(let error):
ErrorPresenter.showErrorAlert(error: error, from: self)
return
case .success(let tunnelsManager):
self.tunnelsManager = tunnelsManager
self.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager)
tunnelsManager.activationDelegate = self
self.onTunnelsManagerReady?(tunnelsManager)
self.onTunnelsManagerReady = nil
}
let tunnelsManager: TunnelsManager = result.value!
self.tunnelsManager = tunnelsManager
self.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager)
tunnelsManager.activationDelegate = self
self.onTunnelsManagerReady?(tunnelsManager)
self.onTunnelsManagerReady = nil
}
}
@@ -127,10 +127,10 @@ class TunnelEditTableViewController: UITableViewController {
} else {
// We're adding a new tunnel
tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] result in
if let error = result.error {
switch result {
case .failure(let error):
ErrorPresenter.showErrorAlert(error: error, from: self)
} else {
let tunnel: TunnelContainer = result.value!
case .success(let tunnel):
self?.dismiss(animated: true, completion: nil)
self?.delegate?.tunnelSaved(tunnel: tunnel)
}
@@ -264,9 +264,10 @@ extension TunnelsListTableViewController: QRScanViewControllerDelegate {
func addScannedQRCode(tunnelConfiguration: TunnelConfiguration, qrScanViewController: QRScanViewController,
completionHandler: (() -> Void)?) {
tunnelsManager?.add(tunnelConfiguration: tunnelConfiguration) { result in
if let error = result.error {
switch result {
case .failure(let error):
ErrorPresenter.showErrorAlert(error: error, from: qrScanViewController, onDismissal: completionHandler)
} else {
case .success:
completionHandler?()
}
}
+47 -18
View File
@@ -20,26 +20,25 @@ class AppDelegate: NSObject, NSApplicationDelegate {
TunnelsManager.create { [weak self] result in
guard let self = self else { return }
if let error = result.error {
switch result {
case .failure(let error):
ErrorPresenter.showErrorAlert(error: error, from: nil)
return
case .success(let tunnelsManager):
let statusMenu = StatusMenu(tunnelsManager: tunnelsManager)
statusMenu.windowDelegate = self
let statusItemController = StatusItemController()
statusItemController.statusItem.menu = statusMenu
let tunnelsTracker = TunnelsTracker(tunnelsManager: tunnelsManager)
tunnelsTracker.statusMenu = statusMenu
tunnelsTracker.statusItemController = statusItemController
self.tunnelsManager = tunnelsManager
self.tunnelsTracker = tunnelsTracker
self.statusItemController = statusItemController
}
let tunnelsManager: TunnelsManager = result.value!
let statusMenu = StatusMenu(tunnelsManager: tunnelsManager)
statusMenu.windowDelegate = self
let statusItemController = StatusItemController()
statusItemController.statusItem.menu = statusMenu
let tunnelsTracker = TunnelsTracker(tunnelsManager: tunnelsManager)
tunnelsTracker.statusMenu = statusMenu
tunnelsTracker.statusItemController = statusItemController
self.tunnelsManager = tunnelsManager
self.tunnelsTracker = tunnelsTracker
self.statusItemController = statusItemController
}
}
@@ -68,6 +67,36 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSApp.terminate(nil)
}
}
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
if UserDefaults.standard.bool(forKey: "shouldSuppressAppStoreUpdateDetection") {
wg_log(.debug, staticMessage: "App Store update detection is suppressed")
return .terminateNow
}
guard let currentTunnel = tunnelsTracker?.currentTunnel, currentTunnel.status == .active || currentTunnel.status == .activating else {
return .terminateNow
}
guard let appleEvent = NSAppleEventManager.shared().currentAppleEvent else {
return .terminateNow
}
guard MacAppStoreUpdateDetector.isUpdatingFromMacAppStore(quitAppleEvent: appleEvent) else {
return .terminateNow
}
let alert = NSAlert()
alert.messageText = tr("macAppStoreUpdatingAlertMessage")
if currentTunnel.isActivateOnDemandEnabled {
alert.informativeText = tr(format: "macAppStoreUpdatingAlertInfoWithOnDemand (%@)", currentTunnel.name)
} else {
alert.informativeText = tr(format: "macAppStoreUpdatingAlertInfoWithoutOnDemand (%@)", currentTunnel.name)
}
NSApp.activate(ignoringOtherApps: true)
if let manageWindow = manageTunnelsWindowObject {
alert.beginSheetModal(for: manageWindow) { _ in }
} else {
alert.runModal()
}
return .terminateCancel
}
}
extension AppDelegate: StatusMenuWindowDelegate {
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
import Cocoa
class MacAppStoreUpdateDetector {
static func isUpdatingFromMacAppStore(quitAppleEvent: NSAppleEventDescriptor) -> Bool {
guard isQuitEvent(quitAppleEvent) else { return false }
guard let senderPIDDescriptor = quitAppleEvent.attributeDescriptor(forKeyword: keySenderPIDAttr) else { return false }
let pid = senderPIDDescriptor.int32Value
guard let executablePath = getExecutablePath(from: pid) else { return false }
wg_log(.debug, message: "aevt/quit Apple event received from: \(executablePath)")
if executablePath.hasPrefix("/System/Library/") {
let executableName = URL(fileURLWithPath: executablePath, isDirectory: false).lastPathComponent
return executableName == "com.apple.CommerceKit.StoreAEService"
}
return false
}
}
private func isQuitEvent(_ event: NSAppleEventDescriptor) -> Bool {
if let eventClassDescriptor = event.attributeDescriptor(forKeyword: keyEventClassAttr),
let eventIdDescriptor = event.attributeDescriptor(forKeyword: keyEventIDAttr) {
return eventClassDescriptor.typeCodeValue == kCoreEventClass && eventIdDescriptor.typeCodeValue == kAEQuitApplication
}
return false
}
private func getExecutablePath(from pid: pid_t) -> String? {
let bufferSize = Int(PATH_MAX)
var buffer = Data(capacity: bufferSize)
return buffer.withUnsafeMutableBytes { (ptr: UnsafeMutableRawBufferPointer) -> String? in
if let basePtr = ptr.baseAddress {
let byteCount = proc_pidpath(pid, basePtr, UInt32(bufferSize))
return byteCount > 0 ? String(cString: basePtr.bindMemory(to: CChar.self, capacity: bufferSize)) : nil
}
return nil
}
}
@@ -187,6 +187,16 @@ class LogViewController: NSViewController {
RunLoop.main.add(timer, forMode: .common)
}
func stopUpdatingLogEntries() {
updateLogEntriesTimer?.invalidate()
updateLogEntriesTimer = nil
}
override func viewWillDisappear() {
super.viewWillDisappear()
stopUpdatingLogEntries()
}
@objc func saveClicked() {
let savePanel = NSSavePanel()
savePanel.prompt = tr("macSheetButtonExportLog")
@@ -247,13 +247,13 @@ class TunnelEditViewController: NSViewController {
// We're creating a new tunnel
self.tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] result in
self?.setUserInteractionEnabled(true)
if let error = result.error {
switch result {
case .failure(let error):
ErrorPresenter.showErrorAlert(error: error, from: self)
return
case .success(let tunnel):
self?.dismiss(self)
self?.delegate?.tunnelSaved(tunnel: tunnel)
}
let tunnel: TunnelContainer = result.value!
self?.dismiss(self)
self?.delegate?.tunnelSaved(tunnel: tunnel)
}
}
}
@@ -5,3 +5,8 @@
#include "ringlogger.h"
#include "highlighter.h"
#include "key.h"
#import "TargetConditionals.h"
#if TARGET_OS_OSX
#include <libproc.h>
#endif
@@ -7,6 +7,7 @@ enum ZipArchiveError: WireGuardAppError {
case cantOpenInputZipFile
case cantOpenOutputZipFileForWriting
case badArchive
case noTunnelsInZipArchive
var alertText: AlertText {
switch self {
@@ -16,6 +17,8 @@ enum ZipArchiveError: WireGuardAppError {
return (tr("alertCantOpenOutputZipFileForWritingTitle"), tr("alertCantOpenOutputZipFileForWritingMessage"))
case .badArchive:
return (tr("alertBadArchiveTitle"), tr("alertBadArchiveMessage"))
case .noTunnelsInZipArchive:
return (tr("alertNoTunnelsInImportedZipArchiveTitle"), tr("alertNoTunnelsInImportedZipArchiveMessage"))
}
}
}
@@ -3,16 +3,8 @@
import Foundation
enum ZipImporterError: WireGuardAppError {
case noTunnelsInZipArchive
var alertText: AlertText {
return (tr("alertNoTunnelsInImportedZipArchiveTitle"), tr("alertNoTunnelsInImportedZipArchiveMessage"))
}
}
class ZipImporter {
static func importConfigFiles(from url: URL, completion: @escaping (WireGuardResult<[TunnelConfiguration?]>) -> Void) {
static func importConfigFiles(from url: URL, completion: @escaping (Result<[TunnelConfiguration?], ZipArchiveError>) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
var unarchivedFiles: [(fileBaseName: String, contents: Data)]
do {
@@ -28,9 +20,9 @@ class ZipImporter {
}
if unarchivedFiles.isEmpty {
throw ZipImporterError.noTunnelsInZipArchive
throw ZipArchiveError.noTunnelsInZipArchive
}
} catch let error as WireGuardAppError {
} catch let error as ZipArchiveError {
DispatchQueue.main.async { completion(.failure(error)) }
return
} catch {