Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add XPC communication to Network Extension#29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 20 commits intomainfromcolin/xpc
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
20 commits
Select commitHold shift + click to select a range
d8ad698
feat: add XPC communication to Network Extension
coadlerJan 23, 2025
12e803c
fixup
coadlerJan 23, 2025
38a9388
fmt
coadlerJan 23, 2025
90e352d
fix xpc target
coadlerJan 23, 2025
61e0a29
fix xpc module
coadlerJan 23, 2025
c0e5813
change to framework
coadlerJan 23, 2025
11a79a9
test
coadlerJan 23, 2025
a5305cd
Merge branch 'main' into colin/xpc
coadlerJan 23, 2025
dc56a96
XPC fixes
coadlerJan 23, 2025
b43a407
fixes
coadlerJan 23, 2025
a48b691
add app group entitlement
ethanndicksonJan 24, 2025
a439536
Merge branch 'main' into colin/xpc
coadlerJan 24, 2025
c4c9f3f
working now
coadlerJan 30, 2025
e3eba6b
Merge branch 'main' into colin/xpc
coadlerJan 30, 2025
7724bc6
lint
coadlerJan 30, 2025
44f191f
remove unused import
coadlerJan 30, 2025
31f0b3b
fix import
ethanndicksonJan 30, 2025
deb773a
Merge branch 'main' into colin/xpc
ethanndicksonJan 30, 2025
207da24
force review
ethanndicksonJan 30, 2025
139157d
fixup
ethanndicksonJan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletionsCoder Desktop/Coder Desktop/Coder_Desktop.entitlements
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,10 @@
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>$(TeamIdentifierPrefix)com.coder.Coder-Desktop</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
Expand Down
3 changes: 1 addition & 2 deletionsCoder Desktop/Coder Desktop/Coder_DesktopApp.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,8 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
Task {
await vpn.stop()
NSApp.reply(toApplicationShouldTerminate: true)
await vpn.quit()
}
return .terminateLater
}
Expand Down
11 changes: 11 additions & 0 deletionsCoder Desktop/Coder Desktop/Info.plist
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NetworkExtension</key>
<dict>
<key>NEMachServiceName</key>
<string>$(TeamIdentifierPrefix)com.coder.Coder-Desktop.VPN</string>
</dict>
</dict>
</plist>
89 changes: 61 additions & 28 deletionsCoder Desktop/Coder Desktop/VPNService.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
import NetworkExtension
import os
import SwiftUI
import VPNLib
import VPNXPC

@MainActor
protocol VPNService: ObservableObject {
Expand DownExpand Up@@ -43,6 +45,9 @@ enum VPNServiceError: Error, Equatable {
@MainActor
final class CoderVPNService: NSObject, VPNService {
var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "vpn")
lazy var xpc: VPNXPCInterface = .init(vpn: self)
var terminating = false

@Published var tunnelState: VPNServiceState = .disabled
@Published var sysExtnState: SystemExtensionState = .uninstalled
@Published var neState: NetworkExtensionState = .unconfigured
Expand DownExpand Up@@ -71,46 +76,45 @@ final class CoderVPNService: NSObject, VPNService {
}
}

var startTask: Task<Void, Never>?
func start() async {
if await startTask?.value != nil {
switch tunnelState {
case .disabled, .failed:
break
default:
return
}
startTask = Task {
tunnelState = .connecting
await enableNetworkExtension()

// TODO: enable communication with the NetworkExtension to track state and agents. For
// now, just pretend it worked...
tunnelState = .connected
}
defer { startTask = nil }
await startTask?.value
// this ping is somewhat load bearing since it causes xpc to init
xpc.ping()
tunnelState = .connecting
await enableNetworkExtension()
logger.debug("network extension enabled")
}

var stopTask: Task<Void, Never>?
func stop() async {
// Wait for a start operation to finish first
await startTask?.value
guard state == .connected else { return }
if await stopTask?.value != nil {
return
}
stopTask = Task {
tunnelState = .disconnecting
await disableNetworkExtension()
guard tunnelState == .connected else { return }
tunnelState = .disconnecting
await disableNetworkExtension()
logger.info("network extension stopped")
}

// TODO: determine when the NetworkExtension is completely disconnected
tunnelState = .disabled
// Instructs the service to stop the VPN and then quit once the stop event
// is read over XPC.
// MUST only be called from `NSApplicationDelegate.applicationShouldTerminate`
// MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
func quit() async {
guard tunnelState == .connected else {
NSApp.reply(toApplicationShouldTerminate: true)
return
}
defer { stopTask = nil }
awaitstopTask?.value
terminating = true
awaitstop()
}

func configureTunnelProviderProtocol(proto: NETunnelProviderProtocol?) {
Task {
ifproto != nil {
await configureNetworkExtension(proto: proto!)
iflet proto {
await configureNetworkExtension(proto: proto)
// this just configures the VPN, it doesn't enable it
tunnelState = .disabled
} else {
Expand All@@ -119,10 +123,39 @@ final class CoderVPNService: NSObject, VPNService {
neState = .unconfigured
tunnelState = .disabled
} catch {
logger.error("failed toremoing network extension: \(error)")
logger.error("failed toremove network extension: \(error)")
neState = .failed(error.localizedDescription)
}
}
}
}

func onExtensionPeerUpdate(_ data: Data) {
// TODO: handle peer update
logger.info("network extension peer update")
do {
let msg = try Vpn_TunnelMessage(serializedBytes: data)
debugPrint(msg)
} catch {
logger.error("failed to decode peer update \(error)")
}
}

func onExtensionStart() {
logger.info("network extension reported started")
tunnelState = .connected
}

func onExtensionStop() {
logger.info("network extension reported stopped")
tunnelState = .disabled
if terminating {
NSApp.reply(toApplicationShouldTerminate: true)
}
}

func onExtensionError(_ error: NSError) {
logger.error("network extension reported error: \(error)")
tunnelState = .failed(.internalError(error.localizedDescription))
}
}
70 changes: 70 additions & 0 deletionsCoder Desktop/Coder Desktop/XPCInterface.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
import Foundation
import os
import VPNXPC

@objc final class VPNXPCInterface: NSObject, VPNXPCClientCallbackProtocol, @unchecked Sendable {
private var svc: CoderVPNService
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "VPNXPCInterface")
private let xpc: VPNXPCProtocol

init(vpn: CoderVPNService) {
svc = vpn

let networkExtDict = Bundle.main.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any]
let machServiceName = networkExtDict?["NEMachServiceName"] as? String
let xpcConn = NSXPCConnection(machServiceName: machServiceName!)
xpcConn.remoteObjectInterface = NSXPCInterface(with: VPNXPCProtocol.self)
xpcConn.exportedInterface = NSXPCInterface(with: VPNXPCClientCallbackProtocol.self)
guard let proxy = xpcConn.remoteObjectProxy as? VPNXPCProtocol else {
fatalError("invalid xpc cast")
}
xpc = proxy

super.init()

xpcConn.exportedObject = self
xpcConn.invalidationHandler = { [logger] in
Task { @MainActor in
logger.error("XPC connection invalidated.")
}
}
xpcConn.interruptionHandler = { [logger] in
Task { @MainActor in
logger.error("XPC connection interrupted.")
}
}
xpcConn.resume()
}

func ping() {
xpc.ping {
Task { @MainActor in
self.logger.info("Connected to NE over XPC")
}
}
}

func onPeerUpdate(_ data: Data) {
Task { @MainActor in
svc.onExtensionPeerUpdate(data)
}
}

func onStart() {
Task { @MainActor in
svc.onExtensionStart()
}
}

func onStop() {
Task { @MainActor in
svc.onExtensionStop()
}
}

func onError(_ err: NSError) {
Task { @MainActor in
svc.onExtensionError(err)
}
}
}
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp