Skip to content

Commit

Permalink
Implement VPN control through UDS (#2767)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1203108348835387/1207203883170230/f

## Description

Implement VPN control through UDS
  • Loading branch information
diegoreymendez authored Jun 12, 2024
1 parent be3a02d commit 39d9c41
Show file tree
Hide file tree
Showing 51 changed files with 1,454 additions and 89 deletions.
18 changes: 18 additions & 0 deletions Configuration/AppStore.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,21 @@ DBP_APP_GROUP[config=CI][sdk=*] = $(DBP_BASE_APP_GROUP).debug
DBP_APP_GROUP[config=Review][sdk=*] = $(DBP_BASE_APP_GROUP).review
DBP_APP_GROUP[config=Debug][sdk=*] = $(DBP_BASE_APP_GROUP).debug
DBP_APP_GROUP[config=Release][sdk=*] = $(DBP_BASE_APP_GROUP)

// IPC

// IMPORTANT: The reason this app group was created is because IPC through
// Unix Domain Sockets requires the socket file path to be no longer than
// 108 characters. Sandboxing requirements force us to place said socket
// within an app group container.
//
// Name coding:
// - ipc.a = ipc app store release
// - ipc.a.d = ipc app store debug
// - ipc.a.r = ipc app store review
//
IPC_APP_GROUP_BASE = $(DEVELOPMENT_TEAM).com.ddg.ipc.a
IPC_APP_GROUP[config=CI][sdk=*] = $(IPC_APP_GROUP_BASE).d
IPC_APP_GROUP[config=Review][sdk=*] = $(IPC_APP_GROUP_BASE).r
IPC_APP_GROUP[config=Debug][sdk=*] = $(IPC_APP_GROUP_BASE).d
IPC_APP_GROUP[config=Release][sdk=*] = $(IPC_APP_GROUP_BASE)
18 changes: 18 additions & 0 deletions Configuration/DeveloperID.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,21 @@ DBP_APP_GROUP[config=CI][sdk=*] = $(DBP_BASE_APP_GROUP).debug
DBP_APP_GROUP[config=Review][sdk=*] = $(DBP_BASE_APP_GROUP).review
DBP_APP_GROUP[config=Debug][sdk=*] = $(DBP_BASE_APP_GROUP).debug
DBP_APP_GROUP[config=Release][sdk=*] = $(DBP_BASE_APP_GROUP)

// IPC

// IMPORTANT: The reason this app group was created is because IPC through
// Unix Domain Sockets requires the socket file path to be no longer than
// 108 characters. Sandboxing requirements force us to place said socket
// within an app group container.
//
// Name coding:
// - ipc.d = ipc developer id release
// - ipc.d.d = ipc developer id debug
// - ipc.d.r = ipc developer id review
//
IPC_APP_GROUP_BASE = $(DEVELOPMENT_TEAM).com.ddg.ipc
IPC_APP_GROUP[config=CI][sdk=*] = $(IPC_APP_GROUP_BASE).d
IPC_APP_GROUP[config=Review][sdk=*] = $(IPC_APP_GROUP_BASE).r
IPC_APP_GROUP[config=Debug][sdk=*] = $(IPC_APP_GROUP_BASE).d
IPC_APP_GROUP[config=Release][sdk=*] = $(IPC_APP_GROUP_BASE)
37 changes: 37 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@
ReferencedContainer = "container:LocalPackages/DataBrokerProtection">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UDSHelperTests"
BuildableName = "UDSHelperTests"
BlueprintName = "UDSHelperTests"
ReferencedContainer = "container:LocalPackages/UDSHelper">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down Expand Up @@ -201,6 +215,16 @@
ReferencedContainer = "container:LocalPackages/DataBrokerProtection">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UDSHelperTests"
BuildableName = "UDSHelperTests"
BlueprintName = "UDSHelperTests"
ReferencedContainer = "container:LocalPackages/UDSHelper">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
14 changes: 11 additions & 3 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
public let subscriptionManager: SubscriptionManaging
public let vpnSettings = VPNSettings(defaults: .netP)

// MARK: - VPN

private var networkProtectionSubscriptionEventHandler: NetworkProtectionSubscriptionEventHandler?

private var vpnXPCClient: VPNControllerXPCClient {
VPNControllerXPCClient.shared
}

// MARK: - DBP

#if DBP
private lazy var dataBrokerProtectionSubscriptionEventHandler: DataBrokerProtectionSubscriptionEventHandler = {
let authManager = DataBrokerAuthenticationManagerBuilder.buildAuthenticationManager(subscriptionManager: subscriptionManager)
Expand Down Expand Up @@ -235,9 +244,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
appIconChanger = AppIconChanger(internalUserDecider: internalUserDecider)

// Configure Event handlers
let ipcClient = TunnelControllerIPCClient()
let tunnelController = NetworkProtectionIPCTunnelController(ipcClient: ipcClient)
let vpnUninstaller = VPNUninstaller(ipcClient: ipcClient)
let tunnelController = NetworkProtectionIPCTunnelController(ipcClient: vpnXPCClient)
let vpnUninstaller = VPNUninstaller(ipcClient: vpnXPCClient)

networkProtectionSubscriptionEventHandler = NetworkProtectionSubscriptionEventHandler(subscriptionManager: subscriptionManager,
tunnelController: tunnelController,
Expand Down
17 changes: 17 additions & 0 deletions DuckDuckGo/Common/Extensions/BundleExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,19 @@ extension Bundle {
static let notificationsAgentProductName = "NOTIFICATIONS_AGENT_PRODUCT_NAME"
#endif

static let ipcAppGroup = "IPC_APP_GROUP"

#if DBP
static let dbpBackgroundAgentBundleId = "DBP_BACKGROUND_AGENT_BUNDLE_ID"
static let dbpBackgroundAgentProductName = "DBP_BACKGROUND_AGENT_PRODUCT_NAME"
#endif
}

var buildNumber: String {
// swiftlint:disable:next force_cast
object(forInfoDictionaryKey: Keys.buildNumber) as! String
}

var versionNumber: String? {
object(forInfoDictionaryKey: Keys.versionNumber) as? String
}
Expand Down Expand Up @@ -104,6 +111,13 @@ extension Bundle {
return appGroup
}

var ipcAppGroupName: String {
guard let appGroup = object(forInfoDictionaryKey: Keys.ipcAppGroup) as? String else {
fatalError("Info.plist is missing \(Keys.ipcAppGroup)")
}
return appGroup
}

var isInApplicationsDirectory: Bool {
let directoryPaths = NSSearchPathForDirectoriesInDomains(.applicationDirectory, .localDomainMask, true)

Expand All @@ -129,13 +143,16 @@ extension Bundle {

enum BundleGroup {
case netP
case ipc
case dbp
case subs

var appGroupKey: String {
switch self {
case .dbp:
return "DBP_APP_GROUP"
case .ipc:
return "IPC_APP_GROUP"
case .netP:
return "NETP_APP_GROUP"
case .subs:
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGo.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(IPC_APP_GROUP)</string>
<string>$(DBP_APP_GROUP)</string>
<string>$(NETP_APP_GROUP)</string>
<string>$(SUBSCRIPTION_APP_GROUP)</string>
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGoAppStore.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(IPC_APP_GROUP)</string>
<string>$(DBP_APP_GROUP)</string>
<string>$(NETP_APP_GROUP)</string>
<string>$(SUBSCRIPTION_APP_GROUP)</string>
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGoAppStoreCI.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(IPC_APP_GROUP)</string>
<string>$(NETP_APP_GROUP)</string>
<string>$(DBP_APP_GROUP)</string>
<string>$(SUBSCRIPTION_APP_GROUP)</string>
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/DuckDuckGoDebug.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(IPC_APP_GROUP)</string>
<string>$(DBP_APP_GROUP)</string>
<string>$(NETP_APP_GROUP)</string>
<string>$(SUBSCRIPTION_APP_GROUP)</string>
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -550,5 +550,7 @@
<integer>10800</integer>
<key>ViewBridgeService</key>
<false/>
<key>IPC_APP_GROUP</key>
<string>$(IPC_APP_GROUP)</string>
</dict>
</plist>
2 changes: 2 additions & 0 deletions DuckDuckGo/LoginItems/LoginItemsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ protocol LoginItemsManaging {
func throwingEnableLoginItems(_ items: Set<LoginItem>, log: OSLog) throws
func disableLoginItems(_ items: Set<LoginItem>)
func restartLoginItems(_ items: Set<LoginItem>, log: OSLog)

func isAnyEnabled(_ items: Set<LoginItem>) -> Bool
}

/// Class to manage the login items for the VPN and DBP
Expand Down
23 changes: 13 additions & 10 deletions DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ final class MainViewController: NSViewController {
fatalError("MainViewController: Bad initializer")
}

init(tabCollectionViewModel: TabCollectionViewModel? = nil, bookmarkManager: BookmarkManager = LocalBookmarkManager.shared, autofillPopoverPresenter: AutofillPopoverPresenter) {
init(tabCollectionViewModel: TabCollectionViewModel? = nil,
bookmarkManager: BookmarkManager = LocalBookmarkManager.shared,
autofillPopoverPresenter: AutofillPopoverPresenter,
vpnXPCClient: VPNControllerXPCClient = .shared) {

let tabCollectionViewModel = tabCollectionViewModel ?? TabCollectionViewModel()
self.tabCollectionViewModel = tabCollectionViewModel
self.isBurner = tabCollectionViewModel.isBurner
Expand All @@ -70,14 +74,14 @@ final class MainViewController: NSViewController {
}
#endif

let ipcClient = TunnelControllerIPCClient()
ipcClient.register { error in
vpnXPCClient.register { error in
NetworkProtectionKnownFailureStore().lastKnownFailure = KnownFailure(error)
}
let vpnUninstaller = VPNUninstaller(ipcClient: ipcClient)

let vpnUninstaller = VPNUninstaller(ipcClient: vpnXPCClient)

return NetworkProtectionNavBarPopoverManager(
ipcClient: ipcClient,
ipcClient: vpnXPCClient,
vpnUninstaller: vpnUninstaller)
}()
let networkProtectionStatusReporter: NetworkProtectionStatusReporter = {
Expand All @@ -92,14 +96,13 @@ final class MainViewController: NSViewController {
connectivityIssuesObserver = connectivityIssuesObserver ?? DisabledConnectivityIssueObserver()
controllerErrorMessageObserver = controllerErrorMessageObserver ?? ControllerErrorMesssageObserverThroughDistributedNotifications()

let ipcClient = networkProtectionPopoverManager.ipcClient
return DefaultNetworkProtectionStatusReporter(
statusObserver: ipcClient.ipcStatusObserver,
serverInfoObserver: ipcClient.ipcServerInfoObserver,
connectionErrorObserver: ipcClient.ipcConnectionErrorObserver,
statusObserver: vpnXPCClient.ipcStatusObserver,
serverInfoObserver: vpnXPCClient.ipcServerInfoObserver,
connectionErrorObserver: vpnXPCClient.ipcConnectionErrorObserver,
connectivityIssuesObserver: connectivityIssuesObserver,
controllerErrorMessageObserver: controllerErrorMessageObserver,
dataVolumeObserver: ipcClient.ipcDataVolumeObserver,
dataVolumeObserver: vpnXPCClient.ipcDataVolumeObserver,
knownFailureObserver: KnownFailureObserverThroughDistributedNotifications()
)
}()
Expand Down
1 change: 0 additions & 1 deletion DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ protocol PopoverPresenter {
}

protocol NetPPopoverManager: AnyObject {
var ipcClient: NetworkProtectionIPCClient { get }
var isShown: Bool { get }

func show(positionedBelow view: NSView, withDelegate delegate: NSPopoverDelegate) -> NSPopover
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// VPNIPCResources.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

struct VPNIPCResources {
public static let socketFileURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.appGroup(bundle: .ipc))!.appendingPathComponent("vpn.ipc")
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ final class ErrorInformation: NSObject, Codable {
///
final class VPNOperationErrorHistory {

private let ipcClient: TunnelControllerIPCClient
private let ipcClient: VPNControllerXPCClient
private let defaults: UserDefaults

init(ipcClient: TunnelControllerIPCClient,
init(ipcClient: VPNControllerXPCClient,
defaults: UserDefaults = .netP) {

self.ipcClient = ipcClient
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,3 @@ extension NetworkProtectionLocationListCompositeRepository {
)
}
}

extension TunnelControllerIPCClient {

convenience init() {
self.init(machServiceName: Bundle.main.vpnMenuAgentBundleId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import NetworkProtectionIPC
///
final class NetworkProtectionDebugUtilities {

private let ipcClient: TunnelControllerIPCClient
private let ipcClient: VPNControllerXPCClient
private let vpnUninstaller: VPNUninstaller

// MARK: - Login Items Management
Expand All @@ -46,7 +46,7 @@ final class NetworkProtectionDebugUtilities {
self.loginItemsManager = loginItemsManager
self.settings = settings

let ipcClient = TunnelControllerIPCClient()
let ipcClient = VPNControllerXPCClient.shared

self.ipcClient = ipcClient
self.vpnUninstaller = VPNUninstaller(ipcClient: ipcClient)
Expand All @@ -66,7 +66,7 @@ final class NetworkProtectionDebugUtilities {

func removeSystemExtensionAndAgents() async throws {
try await vpnUninstaller.removeSystemExtension()
vpnUninstaller.disableLoginItems()
vpnUninstaller.removeAgents()
}

func sendTestNotificationRequest() async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protocol NetworkProtectionIPCClient {
func stop(completion: @escaping (Error?) -> Void)
}

extension TunnelControllerIPCClient: NetworkProtectionIPCClient {
extension VPNControllerXPCClient: NetworkProtectionIPCClient {
public var ipcStatusObserver: any NetworkProtection.ConnectionStatusObserver { connectionStatusObserver }
public var ipcServerInfoObserver: any NetworkProtection.ConnectionServerInfoObserver { serverInfoObserver }
public var ipcConnectionErrorObserver: any NetworkProtection.ConnectionErrorObserver { connectionErrorObserver }
Expand All @@ -49,7 +49,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
let ipcClient: NetworkProtectionIPCClient
let vpnUninstaller: VPNUninstalling

init(ipcClient: TunnelControllerIPCClient,
init(ipcClient: VPNControllerXPCClient,
vpnUninstaller: VPNUninstalling) {
self.ipcClient = ipcClient
self.vpnUninstaller = vpnUninstaller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ final class TunnelControllerProvider {
let tunnelController: NetworkProtectionIPCTunnelController

private init() {
let ipcClient = TunnelControllerIPCClient()
let ipcClient = VPNControllerXPCClient.shared
ipcClient.register { error in
NetworkProtectionKnownFailureStore().lastKnownFailure = KnownFailure(error)
}

tunnelController = NetworkProtectionIPCTunnelController(ipcClient: ipcClient)
}

Expand Down
Loading

0 comments on commit 39d9c41

Please sign in to comment.