Skip to content

Commit

Permalink
Merge pull request #96 from trilemma-dev/connection-descriptor
Browse files Browse the repository at this point in the history
Adds public connection descriptor, exposed by client, server, and endpoint
  • Loading branch information
jakaplan committed May 12, 2022
2 parents d38a4f1 + 7b2ca79 commit b198606
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 53 deletions.
4 changes: 3 additions & 1 deletion Sources/SecureXPC/Client/XPCAnonymousClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import Foundation
///
/// In the case of this framework, the anonymous listener connection is expected to be represented by an `XPCAnonymousServer`.
internal class XPCAnonymousClient: XPCClient {
override var serviceName: String? { nil }
public override var connectionDescriptor: XPCConnectionDescriptor {
.anonymous
}

// Anonymous clients *must* be created from an existing connection.
init(connection: xpc_connection_t) {
Expand Down
18 changes: 8 additions & 10 deletions Sources/SecureXPC/Client/XPCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ import Foundation
/// let client = XPCClient.forMachService(named: "com.example.service")
/// ```
///
/// The name of the service is defined differently depending on the type of bundle or binary:
/// The sservice's name is defined differently depending on its type:
/// - For an `SMJobBless` helper tool this must be a key in the `MachServices` entry of the tool's launchd property list
/// - For a `SMLoginItemSetEnabled` login item it is the bundle's identifier
/// - For a Launch Agent or Launch Daemon, it's defined in the property list used when registering with `launchd`
/// - For an `SMLoginItemSetEnabled` login item it is the bundle's identifier
/// - For a Launch Agent or Launch Daemon it's defined in the property list used when registering with launchd
///
/// The service itself must retrieve and configure an ``XPCServer`` by calling ``XPCServer/forThisMachService(named:clientRequirements:)``,
/// ``XPCServer/forThisBlessedHelperTool()``, or ``XPCServer/forThisLoginItem()`` in order for this client to be able to communicate with it.
Expand Down Expand Up @@ -153,7 +153,7 @@ import Foundation
/// - ``XPCResponseHandler``
/// - ``XPCSequentialResponseHandler``
/// ### Client Information
/// - ``serviceName``
/// - ``connectionDescriptor``
public class XPCClient {

// MARK: Public factories
Expand Down Expand Up @@ -202,7 +202,7 @@ public class XPCClient {
})
xpc_connection_resume(connection)

switch endpoint.serviceDescriptor {
switch endpoint.connectionDescriptor {
case .anonymous:
return XPCAnonymousClient(connection: connection)
case .xpcService(name: let name):
Expand Down Expand Up @@ -642,11 +642,9 @@ public class XPCClient {
}

// MARK: Abstract methods & properties

/// The name of the service this client is configured to communicate with.
///
/// If this is configured to talk to an anonymous server then there is no service and therefore the service name will always be `nil`.
public var serviceName: String? {

/// The type of connection created by this client.
public var connectionDescriptor: XPCConnectionDescriptor {
fatalError("Abstract Property")
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/SecureXPC/Client/XPCMachClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import Foundation
internal class XPCMachClient: XPCClient {
private let machServiceName: String

override var serviceName: String? { machServiceName }
public override var connectionDescriptor: XPCConnectionDescriptor {
.machService(name: machServiceName)
}

internal init(machServiceName: String, connection: xpc_connection_t? = nil) {
self.machServiceName = machServiceName
Expand Down
6 changes: 4 additions & 2 deletions Sources/SecureXPC/Client/XPCServiceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import Foundation
internal class XPCServiceClient: XPCClient {
private let xpcServiceName: String

override var serviceName: String? { xpcServiceName }
public override var connectionDescriptor: XPCConnectionDescriptor {
.xpcService(name: xpcServiceName)
}

internal init(xpcServiceName: String, connection: xpc_connection_t? = nil) {
self.xpcServiceName = xpcServiceName
Expand All @@ -22,6 +24,6 @@ internal class XPCServiceClient: XPCClient {

/// Creates and returns a connection for the XPC service represented by this client.
internal override func createConnection() -> xpc_connection_t {
xpc_connection_create(self.serviceName, nil)
xpc_connection_create(self.xpcServiceName, nil)
}
}
1 change: 1 addition & 0 deletions Sources/SecureXPC/SecureXPC.docc/SecureXPC.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ See ``XPCClient`` for more on how to retrieve a client and send requests.
- ``XPCServer``
- ``XPCNonBlockingServer``
- ``XPCServerEndpoint``
- ``XPCConnectionDescriptor``

### Routes
- ``XPCRoute``
Expand Down
6 changes: 3 additions & 3 deletions Sources/SecureXPC/Server/XPCAnonymousServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ internal class XPCAnonymousServer: XPCServer {
dispatchMain()
}

public override var serviceName: String? {
nil
public override var connectionDescriptor: XPCConnectionDescriptor {
.anonymous
}
}

Expand All @@ -56,7 +56,7 @@ extension XPCAnonymousServer: XPCNonBlockingServer {

public var endpoint: XPCServerEndpoint {
XPCServerEndpoint(
serviceDescriptor: .anonymous,
connectionDescriptor: .anonymous,
endpoint: xpc_endpoint_create(self.anonymousListenerConnection)
)
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/SecureXPC/Server/XPCMachServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ internal class XPCMachServer: XPCServer {
dispatchMain()
}

public override var serviceName: String? {
machServiceName
public override var connectionDescriptor: XPCConnectionDescriptor {
.machService(name: machServiceName)
}
}

Expand All @@ -69,7 +69,7 @@ extension XPCMachServer: XPCNonBlockingServer {

public var endpoint: XPCServerEndpoint {
XPCServerEndpoint(
serviceDescriptor: .machService(name: self.machServiceName),
connectionDescriptor: .machService(name: self.machServiceName),
endpoint: xpc_endpoint_create(self.listenerConnection)
)
}
Expand Down
11 changes: 5 additions & 6 deletions Sources/SecureXPC/Server/XPCServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ import Foundation
/// - ``startAndBlock()``
/// - ``XPCNonBlockingServer/start()``
/// ### Server State
/// - ``serviceName``
/// - ``connectionDescriptor``
/// - ``XPCNonBlockingServer/endpoint``
public class XPCServer {

Expand Down Expand Up @@ -478,10 +478,8 @@ public class XPCServer {
fatalError("Abstract Method")
}

/// The name of the service this server is bound to.
///
/// Anonymous servers do not represent a service and therefore will always have a `nil` service name.
public var serviceName: String? {
/// The type of connections serviced by this server.
public var connectionDescriptor: XPCConnectionDescriptor {
fatalError("Abstract Property")
}
}
Expand Down Expand Up @@ -592,7 +590,8 @@ extension XPCServer {
///
/// > Important: No requests will be processed until ``startAndBlock()`` or ``XPCNonBlockingServer/start()`` is called.
///
/// - Throws: ``XPCError/misconfiguredBlessedHelperTool(_:)`` if the configuration does not match this function's requirements.
/// - Throws: ``XPCError/misconfiguredBlessedHelperTool(description:)`` if the configuration does not match this function's
/// requirements.
/// - Returns: A server instance configured with the embedded property list entries.
public static func forThisBlessedHelperTool() throws -> XPCServer & XPCNonBlockingServer {
try XPCMachServer._forThisBlessedHelperTool()
Expand Down
6 changes: 4 additions & 2 deletions Sources/SecureXPC/Server/XPCServiceServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ internal class XPCServiceServer: XPCServer {
}
}

public override var serviceName: String? {
public override var connectionDescriptor: XPCConnectionDescriptor {
// This is safe to unwrap because it was already checked in `_forThisXPCService()`.
Bundle.main.bundleIdentifier!
let serviceName = Bundle.main.bundleIdentifier!

return .xpcService(name: serviceName)
}
}
26 changes: 26 additions & 0 deletions Sources/SecureXPC/XPCConnectionDescriptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// XPCConnectionDescriptor.swift
// SecureXPC
//
// Created by Alexander Momchilov on 2021-11-28.
//

import Foundation

/// The type of connections serviced by an ``XPCServer`` or created by an ``XPCClient``.
public enum XPCConnectionDescriptor {
/// An anonymous connection which has no associated service.
case anonymous

/// A connection for an XPC service.
///
/// The associated value is the name of the service.
case xpcService(name: String)

/// A connection for an XPC Mach service.
///
/// The associated value is the name of the service.
case machService(name: String)
}

extension XPCConnectionDescriptor: Codable { }
25 changes: 14 additions & 11 deletions Sources/SecureXPC/XPCServerEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ import Foundation

/// An endpoint is used to create clients which can communicate with their associated server.
///
/// Endpoints are retrieved from a server's ``XPCNonBlockingServer/endpoint`` property. They can be used in the same process or sent across an existing XPC
/// connection.
/// Endpoints are retrieved from a server's ``XPCNonBlockingServer/endpoint`` property. They can be used in the same process or sent across an existing
/// XPC connection.
///
/// > Warning: While ``XPCServerEndpoint`` conforms to `Codable` it can only be encoded and decoded by the `SecureXPC` framework.
public struct XPCServerEndpoint {
// Technically, an `xpc_endpoint_t` is sufficient to create a new connection, on its own. However, it's useful to
// be able to communicate the kind of connection, and its name, so we also store those, separately.
internal let serviceDescriptor: XPCServiceDescriptor
/// The type of connections serviced by the ``XPCServer`` which created this endpoint.
public let connectionDescriptor: XPCConnectionDescriptor

// The underlying XPC C API endpoint needed to create connections to the listener connection it came from
internal let endpoint: xpc_endpoint_t

internal init(serviceDescriptor: XPCServiceDescriptor, endpoint: xpc_endpoint_t) {
self.serviceDescriptor = serviceDescriptor
internal init(connectionDescriptor: XPCConnectionDescriptor, endpoint: xpc_endpoint_t) {
self.connectionDescriptor = connectionDescriptor
self.endpoint = endpoint
}
}
Expand All @@ -33,15 +34,16 @@ extension XPCServerEndpoint: Hashable {

extension XPCServerEndpoint: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
// No need to compare service descriptors as the endpoint is guaranteed to be unique
// No need to compare connection descriptors as the endpoint is guaranteed to be unique (also all anonymous
// connection descriptors are equivalent to one another because they don't have names).
xpc_equal(lhs.endpoint, rhs.endpoint)
}
}

// MARK: Codable

private enum CodingKeys: String, CodingKey {
case serviceDescriptor
case connectionDescriptor
case endpoint
}

Expand All @@ -52,7 +54,7 @@ extension XPCServerEndpoint: Encodable {
}

let container = xpcEncoder.xpcContainer(keyedBy: CodingKeys.self)
try container.encode(self.serviceDescriptor, forKey: CodingKeys.serviceDescriptor)
try container.encode(self.connectionDescriptor, forKey: CodingKeys.connectionDescriptor)
container.encode(self.endpoint, forKey: CodingKeys.endpoint)
}
}
Expand All @@ -65,6 +67,7 @@ extension XPCServerEndpoint: Decodable {

let container = try xpcDecoder.xpcContainer(keyedBy: CodingKeys.self)
self.endpoint = try container.decodeEndpoint(forKey: CodingKeys.endpoint)
self.serviceDescriptor = try container.decode(XPCServiceDescriptor.self, forKey: CodingKeys.serviceDescriptor)
self.connectionDescriptor = try container.decode(XPCConnectionDescriptor.self,
forKey: CodingKeys.connectionDescriptor)
}
}
14 changes: 0 additions & 14 deletions Sources/SecureXPC/XPCServiceDescriptor.swift

This file was deleted.

0 comments on commit b198606

Please sign in to comment.