mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
[AltDaemon] Replaces local socket communication with XPC
Allows AltDaemon to be launched on demand + reject any connections not made from AltStore.
This commit is contained in:
@@ -10,6 +10,23 @@
|
||||
#import "NSError+ALTServerError.h"
|
||||
#import "CFNotificationName+AltStore.h"
|
||||
|
||||
// libproc
|
||||
int proc_pidpath(int pid, void * buffer, uint32_t buffersize);
|
||||
|
||||
// Security.framework
|
||||
CF_ENUM(uint32_t) {
|
||||
kSecCSInternalInformation = 1 << 0,
|
||||
kSecCSSigningInformation = 1 << 1,
|
||||
kSecCSRequirementInformation = 1 << 2,
|
||||
kSecCSDynamicInformation = 1 << 3,
|
||||
kSecCSContentInformation = 1 << 4,
|
||||
kSecCSSkipResourceDirectory = 1 << 5,
|
||||
kSecCSCalculateCMSDigest = 1 << 6,
|
||||
};
|
||||
|
||||
OSStatus SecStaticCodeCreateWithPath(CFURLRef path, uint32_t flags, void ** __nonnull CF_RETURNS_RETAINED staticCode);
|
||||
OSStatus SecCodeCopySigningInformation(void *code, uint32_t flags, CFDictionaryRef * __nonnull CF_RETURNS_RETAINED information);
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AKDevice : NSObject
|
||||
|
||||
@@ -75,7 +75,8 @@ struct AppManager
|
||||
// Remove all inactive profiles (if active profiles are provided), and the previous profiles.
|
||||
for fileURL in profileURLs
|
||||
{
|
||||
guard let profile = ALTProvisioningProfile(url: fileURL) else { continue }
|
||||
// Use memory mapping to reduce peak memory usage and stay within limit.
|
||||
guard let profile = try? ALTProvisioningProfile(url: fileURL, options: [.mappedIfSafe]) else { continue }
|
||||
|
||||
if installingBundleIDs.contains(profile.bundleIdentifier) || (activeProfiles?.contains(profile.bundleIdentifier) == false && profile.isFreeProvisioningProfile)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// ConnectionManager.swift
|
||||
// AltServer
|
||||
// DaemonRequestHandler.swift
|
||||
// AltDaemon
|
||||
//
|
||||
// Created by Riley Testut on 6/1/20.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
@@ -11,7 +11,7 @@ import Foundation
|
||||
typealias DaemonConnectionManager = ConnectionManager<DaemonRequestHandler>
|
||||
|
||||
private let connectionManager = ConnectionManager(requestHandler: DaemonRequestHandler(),
|
||||
connectionHandlers: [LocalConnectionHandler()])
|
||||
connectionHandlers: [XPCConnectionHandler()])
|
||||
|
||||
extension DaemonConnectionManager
|
||||
{
|
||||
@@ -1,108 +0,0 @@
|
||||
//
|
||||
// LocalConnectionHandler.swift
|
||||
// AltDaemon
|
||||
//
|
||||
// Created by Riley Testut on 6/2/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
private let ReceivedLocalServerConnectionRequest: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
|
||||
{ (center, observer, name, object, userInfo) in
|
||||
guard let name = name, let observer = observer else { return }
|
||||
|
||||
let connection = unsafeBitCast(observer, to: LocalConnectionHandler.self)
|
||||
connection.handle(name)
|
||||
}
|
||||
|
||||
class LocalConnectionHandler: ConnectionHandler
|
||||
{
|
||||
var connectionHandler: ((Connection) -> Void)?
|
||||
var disconnectionHandler: ((Connection) -> Void)?
|
||||
|
||||
private let dispatchQueue = DispatchQueue(label: "io.altstore.LocalConnectionListener", qos: .utility)
|
||||
|
||||
deinit
|
||||
{
|
||||
self.stopListening()
|
||||
}
|
||||
|
||||
func startListening()
|
||||
{
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
let observer = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
CFNotificationCenterAddObserver(notificationCenter, observer, ReceivedLocalServerConnectionRequest, CFNotificationName.localServerConnectionAvailableRequest.rawValue, nil, .deliverImmediately)
|
||||
CFNotificationCenterAddObserver(notificationCenter, observer, ReceivedLocalServerConnectionRequest, CFNotificationName.localServerConnectionStartRequest.rawValue, nil, .deliverImmediately)
|
||||
}
|
||||
|
||||
func stopListening()
|
||||
{
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
let observer = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
CFNotificationCenterRemoveObserver(notificationCenter, observer, .localServerConnectionAvailableRequest, nil)
|
||||
CFNotificationCenterRemoveObserver(notificationCenter, observer, .localServerConnectionStartRequest, nil)
|
||||
}
|
||||
|
||||
fileprivate func handle(_ notification: CFNotificationName)
|
||||
{
|
||||
switch notification
|
||||
{
|
||||
case .localServerConnectionAvailableRequest:
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
CFNotificationCenterPostNotification(notificationCenter, .localServerConnectionAvailableResponse, nil, nil, true)
|
||||
|
||||
case .localServerConnectionStartRequest:
|
||||
let connection = NWConnection(host: "localhost", port: NWEndpoint.Port(rawValue: ALTDeviceListeningSocket)!, using: .tcp)
|
||||
self.start(connection)
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension LocalConnectionHandler
|
||||
{
|
||||
func start(_ nwConnection: NWConnection)
|
||||
{
|
||||
print("Starting connection to:", nwConnection)
|
||||
|
||||
// Use same instance for all callbacks.
|
||||
let connection = NetworkConnection(nwConnection)
|
||||
|
||||
nwConnection.stateUpdateHandler = { [weak self] (state) in
|
||||
switch state
|
||||
{
|
||||
case .setup, .preparing: break
|
||||
|
||||
case .ready:
|
||||
print("Connected to client:", nwConnection.endpoint)
|
||||
self?.connectionHandler?(connection)
|
||||
|
||||
case .waiting:
|
||||
print("Waiting for connection...")
|
||||
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(nwConnection.endpoint).", error)
|
||||
self?.disconnect(connection)
|
||||
|
||||
case .cancelled:
|
||||
self?.disconnect(connection)
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
nwConnection.start(queue: self.dispatchQueue)
|
||||
}
|
||||
|
||||
func disconnect(_ connection: Connection)
|
||||
{
|
||||
connection.disconnect()
|
||||
|
||||
self.disconnectionHandler?(connection)
|
||||
}
|
||||
}
|
||||
90
AltDaemon/XPCConnectionHandler.swift
Normal file
90
AltDaemon/XPCConnectionHandler.swift
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// XPCConnectionHandler.swift
|
||||
// AltDaemon
|
||||
//
|
||||
// Created by Riley Testut on 9/14/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
class XPCConnectionHandler: NSObject, ConnectionHandler
|
||||
{
|
||||
var connectionHandler: ((Connection) -> Void)?
|
||||
var disconnectionHandler: ((Connection) -> Void)?
|
||||
|
||||
private let dispatchQueue = DispatchQueue(label: "io.altstore.XPCConnectionListener", qos: .utility)
|
||||
private let listener = NSXPCListener.makeListener(machServiceName: XPCConnection.machServiceName)
|
||||
|
||||
deinit
|
||||
{
|
||||
self.stopListening()
|
||||
}
|
||||
|
||||
func startListening()
|
||||
{
|
||||
self.listener.delegate = self
|
||||
self.listener.resume()
|
||||
}
|
||||
|
||||
func stopListening()
|
||||
{
|
||||
self.listener.suspend()
|
||||
}
|
||||
}
|
||||
|
||||
private extension XPCConnectionHandler
|
||||
{
|
||||
func disconnect(_ connection: Connection)
|
||||
{
|
||||
connection.disconnect()
|
||||
|
||||
self.disconnectionHandler?(connection)
|
||||
}
|
||||
}
|
||||
|
||||
extension XPCConnectionHandler: NSXPCListenerDelegate
|
||||
{
|
||||
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool
|
||||
{
|
||||
let maximumPathLength = 4 * UInt32(MAXPATHLEN)
|
||||
|
||||
let pathBuffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(maximumPathLength))
|
||||
defer { pathBuffer.deallocate() }
|
||||
|
||||
proc_pidpath(newConnection.processIdentifier, pathBuffer, maximumPathLength)
|
||||
|
||||
let path = String(cString: pathBuffer)
|
||||
let fileURL = URL(fileURLWithPath: path)
|
||||
|
||||
var code: UnsafeMutableRawPointer?
|
||||
defer { code.map { Unmanaged<AnyObject>.fromOpaque($0).release() } }
|
||||
|
||||
var status = SecStaticCodeCreateWithPath(fileURL as CFURL, 0, &code)
|
||||
guard status == 0 else { return false }
|
||||
|
||||
var signingInfo: CFDictionary?
|
||||
defer { signingInfo.map { Unmanaged<AnyObject>.passUnretained($0).release() } }
|
||||
|
||||
status = SecCodeCopySigningInformation(code, kSecCSInternalInformation | kSecCSSigningInformation, &signingInfo)
|
||||
guard status == 0 else { return false }
|
||||
|
||||
// Only accept connections from AltStore.
|
||||
guard
|
||||
let codeSigningInfo = signingInfo as? [String: Any],
|
||||
let bundleIdentifier = codeSigningInfo["identifier"] as? String,
|
||||
bundleIdentifier.contains("com.rileytestut.AltStore")
|
||||
else { return false }
|
||||
|
||||
let connection = XPCConnection(newConnection)
|
||||
newConnection.invalidationHandler = { [weak self, weak connection] in
|
||||
guard let self = self, let connection = connection else { return }
|
||||
self.disconnect(connection)
|
||||
}
|
||||
|
||||
self.connectionHandler?(connection)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,13 @@
|
||||
<key>UserName</key>
|
||||
<string>mobile</string>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<false/>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>cy:io.altstore.altdaemon</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B0F022E25DF9005C4CF5 /* ToastView.swift */; };
|
||||
BF18BFFD2485A1E400DD5981 /* WiredConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFFC2485A1E400DD5981 /* WiredConnectionHandler.swift */; };
|
||||
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; };
|
||||
BF1FE358251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; };
|
||||
BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */; };
|
||||
BF29012F2318F6B100D88A45 /* AppBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF29012E2318F6B100D88A45 /* AppBannerView.xib */; };
|
||||
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2901302318F7A800D88A45 /* AppBannerView.swift */; };
|
||||
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; };
|
||||
@@ -190,7 +192,7 @@
|
||||
BF8B17EB250AC40000F8157F /* FileManager+SharedDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6A531F246DC1B0004F59C8 /* FileManager+SharedDirectories.swift */; };
|
||||
BF8CAE452489E772004D6CCE /* AnisetteDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */; };
|
||||
BF8CAE462489E772004D6CCE /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE432489E772004D6CCE /* AppManager.swift */; };
|
||||
BF8CAE472489E772004D6CCE /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE442489E772004D6CCE /* RequestHandler.swift */; };
|
||||
BF8CAE472489E772004D6CCE /* DaemonRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */; };
|
||||
BF8CAE4C2489F637004D6CCE /* AltDaemon.deb in Resources */ = {isa = PBXBuildFile; fileRef = BF8CAE4A2489F5A0004D6CCE /* AltDaemon.deb */; };
|
||||
BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */; };
|
||||
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; };
|
||||
@@ -235,6 +237,9 @@
|
||||
BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */; };
|
||||
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; };
|
||||
BFC57A702416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */; };
|
||||
BFC712C32512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; };
|
||||
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C12512D5F100AB5EBE /* XPCConnection.swift */; };
|
||||
BFC712C52512D5F100AB5EBE /* XPCConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */; };
|
||||
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC84A4C2421A19100853474 /* SourcesViewController.swift */; };
|
||||
BFCB9207250AB2120057B44E /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFCB9206250AB2120057B44E /* Colors.xcassets */; };
|
||||
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCB519245E3401001853EA /* VerifyAppOperation.swift */; };
|
||||
@@ -307,7 +312,6 @@
|
||||
BFECAC8924FD950E0077C41F /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF22485828200DD5981 /* ConnectionManager.swift */; };
|
||||
BFECAC8A24FD950E0077C41F /* ALTServerError+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */; };
|
||||
BFECAC8B24FD950E0077C41F /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; };
|
||||
BFECAC8C24FD950E0077C41F /* NetworkConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767CD2489ABE90097E58C /* NetworkConnection.swift */; };
|
||||
BFECAC8D24FD950E0077C41F /* ALTConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = BF718BD723C93DB700A89F2D /* ALTConstants.m */; };
|
||||
BFECAC8E24FD950E0077C41F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18BFF624858BDE00DD5981 /* Connection.swift */; };
|
||||
BFECAC8F24FD950E0077C41F /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; };
|
||||
@@ -328,7 +332,6 @@
|
||||
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; };
|
||||
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
|
||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
|
||||
BFFCFA582488648D0077BFCE /* LocalConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF10EB3124870B3F0055E6DB /* LocalConnectionHandler.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -432,7 +435,6 @@
|
||||
BF088D322501A4FF008082D9 /* OpenSSL.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
|
||||
BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
|
||||
BF0DCA652433BDF500E3A595 /* AnalyticsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsManager.swift; sourceTree = "<group>"; };
|
||||
BF10EB3124870B3F0055E6DB /* LocalConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
BF10EB33248730750055E6DB /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
BF18B0F022E25DF9005C4CF5 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||
BF18BFE724857D7900DD5981 /* AltDaemon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = AltDaemon; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -447,6 +449,7 @@
|
||||
BF1E314722A060F300370A3C /* AltStore-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AltStore-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
BF1E314822A060F400370A3C /* NSError+ALTServerError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSError+ALTServerError.h"; sourceTree = "<group>"; };
|
||||
BF1E314922A060F400370A3C /* NSError+ALTServerError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSError+ALTServerError.m"; sourceTree = "<group>"; };
|
||||
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSXPCConnection+MachServices.swift"; sourceTree = "<group>"; };
|
||||
BF219A7E22CAC431007676A6 /* AltStore.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltStore.entitlements; sourceTree = "<group>"; };
|
||||
BF29012E2318F6B100D88A45 /* AppBannerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AppBannerView.xib; sourceTree = "<group>"; };
|
||||
BF2901302318F7A800D88A45 /* AppBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBannerView.swift; sourceTree = "<group>"; };
|
||||
@@ -630,7 +633,7 @@
|
||||
BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltWidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||
BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = "<group>"; };
|
||||
BF8CAE432489E772004D6CCE /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
|
||||
BF8CAE442489E772004D6CCE /* RequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = "<group>"; };
|
||||
BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DaemonRequestHandler.swift; sourceTree = "<group>"; };
|
||||
BF8CAE4A2489F5A0004D6CCE /* AltDaemon.deb */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = AltDaemon.deb; sourceTree = "<group>"; };
|
||||
BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Jailbreak.swift"; sourceTree = "<group>"; };
|
||||
BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -662,6 +665,8 @@
|
||||
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeactivateAppOperation.swift; sourceTree = "<group>"; };
|
||||
BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledAppsCollectionHeaderView.swift; sourceTree = "<group>"; };
|
||||
BFC57A6F2416FC7600EB891E /* InstalledAppsCollectionHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InstalledAppsCollectionHeaderView.xib; sourceTree = "<group>"; };
|
||||
BFC712C12512D5F100AB5EBE /* XPCConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnection.swift; sourceTree = "<group>"; };
|
||||
BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XPCConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
BFC84A4C2421A19100853474 /* SourcesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesViewController.swift; sourceTree = "<group>"; };
|
||||
BFCB9206250AB2120057B44E /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
|
||||
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifyAppOperation.swift; sourceTree = "<group>"; };
|
||||
@@ -858,8 +863,8 @@
|
||||
children = (
|
||||
BFFCFA45248835530077BFCE /* AltDaemon.entitlements */,
|
||||
BF10EB33248730750055E6DB /* main.swift */,
|
||||
BF8CAE442489E772004D6CCE /* RequestHandler.swift */,
|
||||
BF10EB3124870B3F0055E6DB /* LocalConnectionHandler.swift */,
|
||||
BFC712C22512D5F100AB5EBE /* XPCConnectionHandler.swift */,
|
||||
BF8CAE442489E772004D6CCE /* DaemonRequestHandler.swift */,
|
||||
BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */,
|
||||
BF8CAE432489E772004D6CCE /* AppManager.swift */,
|
||||
BF18C0032485B4DE00DD5981 /* AltDaemon-Bridging-Header.h */,
|
||||
@@ -1580,6 +1585,7 @@
|
||||
BFBAC8852295C90300587369 /* Result+Conveniences.swift */,
|
||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
|
||||
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -1602,6 +1608,7 @@
|
||||
BF18BFFE2485A42800DD5981 /* ALTConnection.h */,
|
||||
BF18BFF624858BDE00DD5981 /* Connection.swift */,
|
||||
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */,
|
||||
BFC712C12512D5F100AB5EBE /* XPCConnection.swift */,
|
||||
);
|
||||
path = Connections;
|
||||
sourceTree = "<group>";
|
||||
@@ -2126,10 +2133,12 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BF8CAE452489E772004D6CCE /* AnisetteDataManager.swift in Sources */,
|
||||
BF1FE358251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */,
|
||||
BFECAC8F24FD950E0077C41F /* Result+Conveniences.swift in Sources */,
|
||||
BF8CAE472489E772004D6CCE /* RequestHandler.swift in Sources */,
|
||||
BFECAC8C24FD950E0077C41F /* NetworkConnection.swift in Sources */,
|
||||
BF8CAE472489E772004D6CCE /* DaemonRequestHandler.swift in Sources */,
|
||||
BFECAC8824FD950E0077C41F /* CodableServerError.swift in Sources */,
|
||||
BFC712C32512D5F100AB5EBE /* XPCConnection.swift in Sources */,
|
||||
BFC712C52512D5F100AB5EBE /* XPCConnectionHandler.swift in Sources */,
|
||||
BFECAC8A24FD950E0077C41F /* ALTServerError+Conveniences.swift in Sources */,
|
||||
BFECAC8D24FD950E0077C41F /* ALTConstants.m in Sources */,
|
||||
BFECAC8924FD950E0077C41F /* ConnectionManager.swift in Sources */,
|
||||
@@ -2138,7 +2147,6 @@
|
||||
BFECAC8B24FD950E0077C41F /* ServerProtocol.swift in Sources */,
|
||||
BFECAC9624FD98BB0077C41F /* NSError+ALTServerError.m in Sources */,
|
||||
BF10EB34248730750055E6DB /* main.swift in Sources */,
|
||||
BFFCFA582488648D0077BFCE /* LocalConnectionHandler.swift in Sources */,
|
||||
BF8CAE462489E772004D6CCE /* AppManager.swift in Sources */,
|
||||
BFECAC9024FD950E0077C41F /* Bundle+AltStore.swift in Sources */,
|
||||
);
|
||||
@@ -2271,6 +2279,7 @@
|
||||
BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */,
|
||||
BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */,
|
||||
BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */,
|
||||
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */,
|
||||
BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */,
|
||||
BF66EED42501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel in Sources */,
|
||||
BF66EE972501AEBC007EE018 /* ALTAppPermission.m in Sources */,
|
||||
@@ -2303,6 +2312,7 @@
|
||||
BF66EECC2501AECA007EE018 /* Source.swift in Sources */,
|
||||
BF66EED72501AECA007EE018 /* InstalledApp.swift in Sources */,
|
||||
BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */,
|
||||
BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */,
|
||||
BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */,
|
||||
BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */,
|
||||
BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
import AltStoreCore
|
||||
import Roxas
|
||||
|
||||
private let ReceivedServerConnectionResponse: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
|
||||
@@ -52,11 +53,11 @@ class FindServerOperation: ResultOperation<Server>
|
||||
|
||||
// Prepare observers to receive callback from wired connection or background daemon (if available).
|
||||
CFNotificationCenterAddObserver(notificationCenter, observer, ReceivedServerConnectionResponse, CFNotificationName.wiredServerConnectionAvailableResponse.rawValue, nil, .deliverImmediately)
|
||||
CFNotificationCenterAddObserver(notificationCenter, observer, ReceivedServerConnectionResponse, CFNotificationName.localServerConnectionAvailableResponse.rawValue, nil, .deliverImmediately)
|
||||
|
||||
// Post notifications.
|
||||
CFNotificationCenterPostNotification(notificationCenter, .wiredServerConnectionAvailableRequest, nil, nil, true)
|
||||
CFNotificationCenterPostNotification(notificationCenter, .localServerConnectionAvailableRequest, nil, nil, true)
|
||||
|
||||
self.discoverLocalServer()
|
||||
|
||||
// Wait for either callback or timeout.
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
|
||||
@@ -97,18 +98,28 @@ class FindServerOperation: ResultOperation<Server>
|
||||
let observer = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
CFNotificationCenterRemoveObserver(notificationCenter, observer, .wiredServerConnectionAvailableResponse, nil)
|
||||
CFNotificationCenterRemoveObserver(notificationCenter, observer, .localServerConnectionAvailableResponse, nil)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension FindServerOperation
|
||||
{
|
||||
func discoverLocalServer()
|
||||
{
|
||||
let connection = XPCConnection()
|
||||
connection.connect { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): print("Could not connect to AltDaemon XPC service.", error)
|
||||
case .success: self.isLocalServerConnectionAvailable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handle(_ notification: CFNotificationName)
|
||||
{
|
||||
switch notification
|
||||
{
|
||||
case .wiredServerConnectionAvailableResponse: self.isWiredServerConnectionAvailable = true
|
||||
case .localServerConnectionAvailableResponse: self.isLocalServerConnectionAvailable = true
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import AltStoreCore
|
||||
class ServerConnection
|
||||
{
|
||||
var server: Server
|
||||
var connection: NWConnection
|
||||
var connection: Connection
|
||||
|
||||
init(server: Server, connection: NWConnection)
|
||||
init(server: Server, connection: Connection)
|
||||
{
|
||||
self.server = server
|
||||
self.connection = connection
|
||||
@@ -37,17 +37,15 @@ class ServerConnection
|
||||
data = try JSONEncoder().encode(payload)
|
||||
}
|
||||
|
||||
func process(_ error: Error?) -> Bool
|
||||
func process<T>(_ result: Result<T, ALTServerError>) -> Bool
|
||||
{
|
||||
if error != nil
|
||||
switch result
|
||||
{
|
||||
completionHandler(.failure(ConnectionError.connectionDropped))
|
||||
case .success: return true
|
||||
case .failure(let error):
|
||||
completionHandler(.failure(error))
|
||||
return false
|
||||
}
|
||||
else
|
||||
{
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if prependSize
|
||||
@@ -55,22 +53,21 @@ class ServerConnection
|
||||
let requestSize = Int32(data.count)
|
||||
let requestSizeData = withUnsafeBytes(of: requestSize) { Data($0) }
|
||||
|
||||
self.connection.send(content: requestSizeData, completion: .contentProcessed { (error) in
|
||||
guard process(error) else { return }
|
||||
self.connection.send(requestSizeData) { (result) in
|
||||
guard process(result) else { return }
|
||||
|
||||
self.connection.send(content: data, completion: .contentProcessed { (error) in
|
||||
guard process(error) else { return }
|
||||
self.connection.send(data) { (result) in
|
||||
guard process(result) else { return }
|
||||
completionHandler(.success(()))
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send(content: data, completion: .contentProcessed { (error) in
|
||||
guard process(error) else { return }
|
||||
self.connection.send(data) { (result) in
|
||||
guard process(result) else { return }
|
||||
completionHandler(.success(()))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -84,16 +81,16 @@ class ServerConnection
|
||||
{
|
||||
let size = MemoryLayout<Int32>.size
|
||||
|
||||
self.connection.receive(minimumIncompleteLength: size, maximumLength: size) { (data, _, _, error) in
|
||||
self.connection.receiveData(expectedSize: size) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try self.process(data: data, error: error)
|
||||
let data = try result.get()
|
||||
|
||||
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
||||
self.connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
|
||||
self.connection.receiveData(expectedSize: expectedBytes) { (result) in
|
||||
do
|
||||
{
|
||||
let data = try self.process(data: data, error: error)
|
||||
let data = try result.get()
|
||||
|
||||
let response = try AltStoreCore.JSONDecoder().decode(ServerResponse.self, from: data)
|
||||
completionHandler(.success(response))
|
||||
@@ -111,36 +108,3 @@ class ServerConnection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension ServerConnection
|
||||
{
|
||||
func process(data: Data?, error: NWError?) throws -> Data
|
||||
{
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
guard let data = data else { throw error ?? ALTServerError(.unknown) }
|
||||
return data
|
||||
}
|
||||
catch let error as NWError
|
||||
{
|
||||
print("Error receiving data from connection \(connection)", error)
|
||||
|
||||
throw ALTServerError(.lostConnection)
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw error
|
||||
}
|
||||
}
|
||||
catch let error as ALTServerError
|
||||
{
|
||||
throw error
|
||||
}
|
||||
catch
|
||||
{
|
||||
preconditionFailure("A non-ALTServerError should never be thrown from this method.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,39 +63,23 @@ extension ServerManager
|
||||
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
|
||||
{
|
||||
DispatchQueue.global().async {
|
||||
func finish(_ result: Result<ServerConnection, Error>)
|
||||
func finish(_ result: Result<Connection, Error>)
|
||||
{
|
||||
completion(result)
|
||||
}
|
||||
|
||||
func start(_ connection: NWConnection)
|
||||
{
|
||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
||||
switch state
|
||||
{
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(server.service?.name ?? "").", error)
|
||||
finish(.failure(ConnectionError.connectionFailed))
|
||||
|
||||
case .cancelled:
|
||||
finish(.failure(OperationError.cancelled))
|
||||
|
||||
case .ready:
|
||||
let connection = ServerConnection(server: server, connection: connection)
|
||||
finish(.success(connection))
|
||||
|
||||
case .waiting: break
|
||||
case .setup: break
|
||||
case .preparing: break
|
||||
@unknown default: break
|
||||
}
|
||||
switch result
|
||||
{
|
||||
case .failure(let error): completion(.failure(error))
|
||||
case .success(let connection):
|
||||
let serverConnection = ServerConnection(server: server, connection: connection)
|
||||
completion(.success(serverConnection))
|
||||
}
|
||||
|
||||
connection.start(queue: self.dispatchQueue)
|
||||
}
|
||||
|
||||
if let incomingConnectionsSemaphore = self.incomingConnectionsSemaphore, server.connectionType != .wireless
|
||||
switch server.connectionType
|
||||
{
|
||||
case .local: self.connectToLocalServer(completion: finish(_:))
|
||||
case .wired:
|
||||
guard let incomingConnectionsSemaphore = self.incomingConnectionsSemaphore else { return finish(.failure(ALTServerError(.connectionFailed))) }
|
||||
|
||||
print("Waiting for incoming connection...")
|
||||
|
||||
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
@@ -103,39 +87,27 @@ extension ServerManager
|
||||
switch server.connectionType
|
||||
{
|
||||
case .wired: CFNotificationCenterPostNotification(notificationCenter, .wiredServerConnectionStartRequest, nil, nil, true)
|
||||
case .local: CFNotificationCenterPostNotification(notificationCenter, .localServerConnectionStartRequest, nil, nil, true)
|
||||
case .wireless: break
|
||||
case .local, .wireless: break
|
||||
}
|
||||
|
||||
_ = incomingConnectionsSemaphore.wait(timeout: .now() + 10.0)
|
||||
|
||||
if let connection = self.incomingConnections?.popLast()
|
||||
{
|
||||
start(connection)
|
||||
self.connectToRemoteServer(server, connection: connection, completion: finish(_:))
|
||||
}
|
||||
else
|
||||
{
|
||||
finish(.failure(ALTServerError(.connectionFailed)))
|
||||
}
|
||||
}
|
||||
else if let service = server.service
|
||||
{
|
||||
|
||||
case .wireless:
|
||||
guard let service = server.service else { return finish(.failure(ALTServerError(.connectionFailed))) }
|
||||
|
||||
print("Connecting to service:", service)
|
||||
|
||||
let parameters = NWParameters.tcp
|
||||
|
||||
if server.connectionType == .local
|
||||
{
|
||||
// Prevent AltStore from initiating connections over multiple interfaces simultaneously 🤷♂️
|
||||
parameters.requiredInterfaceType = .loopback
|
||||
}
|
||||
|
||||
let connection = NWConnection(to: .service(name: service.name, type: service.type, domain: service.domain, interface: nil), using: parameters)
|
||||
start(connection)
|
||||
}
|
||||
else
|
||||
{
|
||||
finish(.failure(ALTServerError(.connectionFailed)))
|
||||
let connection = NWConnection(to: .service(name: service.name, type: service.type, domain: service.domain, interface: nil), using: .tcp)
|
||||
self.connectToRemoteServer(server, connection: connection, completion: finish(_:))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +163,47 @@ private extension ServerManager
|
||||
self.incomingConnections = nil
|
||||
self.incomingConnectionsSemaphore = nil
|
||||
}
|
||||
|
||||
func connectToRemoteServer(_ server: Server, connection: NWConnection, completion: @escaping (Result<Connection, Error>) -> Void)
|
||||
{
|
||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
||||
switch state
|
||||
{
|
||||
case .failed(let error):
|
||||
print("Failed to connect to service \(server.service?.name ?? "").", error)
|
||||
completion(.failure(ConnectionError.connectionFailed))
|
||||
|
||||
case .cancelled:
|
||||
completion(.failure(OperationError.cancelled))
|
||||
|
||||
case .ready:
|
||||
let connection = NetworkConnection(connection)
|
||||
completion(.success(connection))
|
||||
|
||||
case .waiting: break
|
||||
case .setup: break
|
||||
case .preparing: break
|
||||
@unknown default: break
|
||||
}
|
||||
}
|
||||
|
||||
connection.start(queue: self.dispatchQueue)
|
||||
}
|
||||
|
||||
func connectToLocalServer(completion: @escaping (Result<Connection, Error>) -> Void)
|
||||
{
|
||||
let connection = XPCConnection()
|
||||
connection.connect { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
print("Could not connect to AltDaemon XPC service.", error)
|
||||
completion(.failure(error))
|
||||
|
||||
case .success: completion(.success(connection))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ServerManager: NetServiceBrowserDelegate
|
||||
|
||||
2
Dependencies/AltSign
vendored
2
Dependencies/AltSign
vendored
Submodule Dependencies/AltSign updated: 508abc4cdf...229e7d5751
@@ -14,8 +14,4 @@ extern CFNotificationName const ALTWiredServerConnectionAvailableRequest NS_SWIF
|
||||
extern CFNotificationName const ALTWiredServerConnectionAvailableResponse NS_SWIFT_NAME(wiredServerConnectionAvailableResponse);
|
||||
extern CFNotificationName const ALTWiredServerConnectionStartRequest NS_SWIFT_NAME(wiredServerConnectionStartRequest);
|
||||
|
||||
extern CFNotificationName const ALTLocalServerConnectionAvailableRequest NS_SWIFT_NAME(localServerConnectionAvailableRequest);
|
||||
extern CFNotificationName const ALTLocalServerConnectionAvailableResponse NS_SWIFT_NAME(localServerConnectionAvailableResponse);
|
||||
extern CFNotificationName const ALTLocalServerConnectionStartRequest NS_SWIFT_NAME(localServerConnectionStartRequest);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -11,7 +11,3 @@
|
||||
CFNotificationName const ALTWiredServerConnectionAvailableRequest = CFSTR("io.altstore.Request.WiredServerConnectionAvailable");
|
||||
CFNotificationName const ALTWiredServerConnectionAvailableResponse = CFSTR("io.altstore.Response.WiredServerConnectionAvailable");
|
||||
CFNotificationName const ALTWiredServerConnectionStartRequest = CFSTR("io.altstore.Request.WiredServerConnectionStart");
|
||||
|
||||
CFNotificationName const ALTLocalServerConnectionAvailableRequest = CFSTR("io.altstore.Request.LocalServerConnectionAvailable");
|
||||
CFNotificationName const ALTLocalServerConnectionAvailableResponse = CFSTR("io.altstore.Response.LocalServerConnectionAvailable");
|
||||
CFNotificationName const ALTLocalServerConnectionStartRequest = CFSTR("io.altstore.Request.LocalServerConnectionStart");
|
||||
|
||||
150
Shared/Connections/XPCConnection.swift
Normal file
150
Shared/Connections/XPCConnection.swift
Normal file
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// XPCConnection.swift
|
||||
// AltKit
|
||||
//
|
||||
// Created by Riley Testut on 6/15/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc private protocol XPCConnectionProxy
|
||||
{
|
||||
func ping(completionHandler: @escaping () -> Void)
|
||||
func receive(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
|
||||
}
|
||||
|
||||
extension XPCConnection
|
||||
{
|
||||
public static let machServiceName = "cy:io.altstore.altdaemon"
|
||||
}
|
||||
|
||||
public class XPCConnection: NSObject, Connection
|
||||
{
|
||||
public let xpcConnection: NSXPCConnection
|
||||
|
||||
private let queue = DispatchQueue(label: "io.altstore.XPCConnection")
|
||||
private let dispatchGroup = DispatchGroup()
|
||||
private var semaphore: DispatchSemaphore?
|
||||
private var buffer = Data(capacity: 1024)
|
||||
|
||||
private var error: Error?
|
||||
|
||||
public init(_ xpcConnection: NSXPCConnection = .makeConnection(machServiceName: XPCConnection.machServiceName))
|
||||
{
|
||||
let proxyInterface = NSXPCInterface(with: XPCConnectionProxy.self)
|
||||
xpcConnection.remoteObjectInterface = proxyInterface
|
||||
xpcConnection.exportedInterface = proxyInterface
|
||||
|
||||
self.xpcConnection = xpcConnection
|
||||
|
||||
super.init()
|
||||
|
||||
xpcConnection.interruptionHandler = {
|
||||
self.error = ALTServerError(.lostConnection)
|
||||
}
|
||||
|
||||
xpcConnection.exportedObject = self
|
||||
xpcConnection.resume()
|
||||
}
|
||||
|
||||
deinit
|
||||
{
|
||||
self.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
private extension XPCConnection
|
||||
{
|
||||
func makeProxy(errorHandler: @escaping (Error) -> Void) -> XPCConnectionProxy
|
||||
{
|
||||
let proxy = self.xpcConnection.remoteObjectProxyWithErrorHandler { (error) in
|
||||
print("Error messaging remote object proxy:", error)
|
||||
self.error = error
|
||||
errorHandler(error)
|
||||
} as! XPCConnectionProxy
|
||||
|
||||
return proxy
|
||||
}
|
||||
}
|
||||
|
||||
public extension XPCConnection
|
||||
{
|
||||
func connect(completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||
{
|
||||
let proxy = self.makeProxy { (error) in
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
|
||||
proxy.ping {
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
func disconnect()
|
||||
{
|
||||
self.xpcConnection.invalidate()
|
||||
}
|
||||
|
||||
func __send(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
|
||||
{
|
||||
guard self.error == nil else { return completionHandler(false, self.error) }
|
||||
|
||||
let proxy = self.makeProxy { (error) in
|
||||
completionHandler(false, error)
|
||||
}
|
||||
|
||||
proxy.receive(data) { (success, error) in
|
||||
completionHandler(success, error)
|
||||
}
|
||||
}
|
||||
|
||||
func __receiveData(expectedSize: Int, completionHandler: @escaping (Data?, Error?) -> Void)
|
||||
{
|
||||
guard self.error == nil else { return completionHandler(nil, self.error) }
|
||||
|
||||
self.queue.async {
|
||||
let copiedBuffer = self.buffer // Copy buffer to prevent runtime crashes.
|
||||
guard copiedBuffer.count >= expectedSize else {
|
||||
self.semaphore = DispatchSemaphore(value: 0)
|
||||
DispatchQueue.global().async {
|
||||
_ = self.semaphore?.wait(timeout: .now() + 1.0)
|
||||
self.__receiveData(expectedSize: expectedSize, completionHandler: completionHandler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let data = copiedBuffer.prefix(expectedSize)
|
||||
self.buffer = copiedBuffer.dropFirst(expectedSize)
|
||||
|
||||
completionHandler(data, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension XPCConnection
|
||||
{
|
||||
override public var description: String {
|
||||
return "\(self.xpcConnection.endpoint) (XPC)"
|
||||
}
|
||||
}
|
||||
|
||||
extension XPCConnection: XPCConnectionProxy
|
||||
{
|
||||
fileprivate func ping(completionHandler: @escaping () -> Void)
|
||||
{
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
fileprivate func receive(_ data: Data, completionHandler: @escaping (Bool, Error?) -> Void)
|
||||
{
|
||||
self.queue.async {
|
||||
self.buffer.append(data)
|
||||
|
||||
self.semaphore?.signal()
|
||||
self.semaphore = nil
|
||||
|
||||
completionHandler(true, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Shared/Extensions/NSXPCConnection+MachServices.swift
Normal file
33
Shared/Extensions/NSXPCConnection+MachServices.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// NSXPCConnection+MachServices.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 9/22/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc private protocol XPCPrivateAPI
|
||||
{
|
||||
init(machServiceName: String)
|
||||
init(machServiceName: String, options: NSXPCConnection.Options)
|
||||
}
|
||||
|
||||
public extension NSXPCConnection
|
||||
{
|
||||
class func makeConnection(machServiceName: String) -> NSXPCConnection
|
||||
{
|
||||
let connection = unsafeBitCast(self, to: XPCPrivateAPI.Type.self).init(machServiceName: machServiceName, options: .privileged)
|
||||
return unsafeBitCast(connection, to: NSXPCConnection.self)
|
||||
}
|
||||
}
|
||||
|
||||
public extension NSXPCListener
|
||||
{
|
||||
class func makeListener(machServiceName: String) -> NSXPCListener
|
||||
{
|
||||
let listener = unsafeBitCast(self, to: XPCPrivateAPI.Type.self).init(machServiceName: machServiceName)
|
||||
return unsafeBitCast(listener, to: NSXPCListener.self)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user