Add missing documentation
This commit is contained in:
parent
72ce14b676
commit
21eee24e7c
|
@ -36,30 +36,69 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Receives events from a `GenericSocket`.
|
||||||
public protocol GenericSocketDelegate: class {
|
public protocol GenericSocketDelegate: class {
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket timed out.
|
||||||
|
**/
|
||||||
func socketDidTimeout(_ socket: GenericSocket)
|
func socketDidTimeout(_ socket: GenericSocket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket became active.
|
||||||
|
**/
|
||||||
func socketDidBecomeActive(_ socket: GenericSocket)
|
func socketDidBecomeActive(_ socket: GenericSocket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket shut down.
|
||||||
|
|
||||||
|
- Parameter failure: `true` if the shutdown was caused by a failure.
|
||||||
|
**/
|
||||||
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool)
|
func socket(_ socket: GenericSocket, didShutdownWithFailure failure: Bool)
|
||||||
|
|
||||||
|
/**
|
||||||
|
The socket has a better path.
|
||||||
|
**/
|
||||||
func socketHasBetterPath(_ socket: GenericSocket)
|
func socketHasBetterPath(_ socket: GenericSocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An opaque socket implementation.
|
||||||
public protocol GenericSocket {
|
public protocol GenericSocket {
|
||||||
|
|
||||||
|
/// The address of the remote endpoint.
|
||||||
var remoteAddress: String? { get }
|
var remoteAddress: String? { get }
|
||||||
|
|
||||||
|
/// `true` if the socket has a better path.
|
||||||
var hasBetterPath: Bool { get }
|
var hasBetterPath: Bool { get }
|
||||||
|
|
||||||
|
/// `true` if the socket was shut down.
|
||||||
var isShutdown: Bool { get }
|
var isShutdown: Bool { get }
|
||||||
|
|
||||||
|
/// The optional delegate for events.
|
||||||
var delegate: GenericSocketDelegate? { get set }
|
var delegate: GenericSocketDelegate? { get set }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Observes socket events.
|
||||||
|
|
||||||
|
- Parameter queue: The queue to observe events in.
|
||||||
|
- Parameter activeTimeout: The timeout in milliseconds for socket activity.
|
||||||
|
**/
|
||||||
func observe(queue: DispatchQueue, activeTimeout: Int)
|
func observe(queue: DispatchQueue, activeTimeout: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Stops observing socket events.
|
||||||
|
**/
|
||||||
func unobserve()
|
func unobserve()
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shuts down the socket
|
||||||
|
**/
|
||||||
func shutdown()
|
func shutdown()
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns an upgraded socket if available (e.g. when a better path exists).
|
||||||
|
|
||||||
|
- Returns: An upgraded socket if any.
|
||||||
|
**/
|
||||||
func upgraded() -> GenericSocket?
|
func upgraded() -> GenericSocket?
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,12 @@ import SwiftyBeaver
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension NSNotification.Name {
|
extension NSNotification.Name {
|
||||||
static let __InterfaceObserverDidDetectWifiChange = NSNotification.Name("__InterfaceObserverDidDetectWifiChange")
|
|
||||||
|
/// A change in Wi-Fi state occurred.
|
||||||
|
public static let InterfaceObserverDidDetectWifiChange = NSNotification.Name("InterfaceObserverDidDetectWifiChange")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Observes changes in the current Wi-Fi network.
|
||||||
public class InterfaceObserver: NSObject {
|
public class InterfaceObserver: NSObject {
|
||||||
private var queue: DispatchQueue?
|
private var queue: DispatchQueue?
|
||||||
|
|
||||||
|
@ -51,6 +54,11 @@ public class InterfaceObserver: NSObject {
|
||||||
|
|
||||||
private var lastWifiName: String?
|
private var lastWifiName: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
Starts observing Wi-Fi updates.
|
||||||
|
|
||||||
|
- Parameter queue: The `DispatchQueue` to deliver notifications to.
|
||||||
|
**/
|
||||||
public func start(queue: DispatchQueue) {
|
public func start(queue: DispatchQueue) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
|
@ -64,6 +72,9 @@ public class InterfaceObserver: NSObject {
|
||||||
self.timer = timer
|
self.timer = timer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Stops observing Wi-Fi updates.
|
||||||
|
**/
|
||||||
public func stop() {
|
public func stop() {
|
||||||
timer?.cancel()
|
timer?.cancel()
|
||||||
timer = nil
|
timer = nil
|
||||||
|
@ -77,7 +88,7 @@ public class InterfaceObserver: NSObject {
|
||||||
log.debug("SSID is now '\(current.maskedDescription)'")
|
log.debug("SSID is now '\(current.maskedDescription)'")
|
||||||
if let last = lastWifiName, (current != last) {
|
if let last = lastWifiName, (current != last) {
|
||||||
queue?.async {
|
queue?.async {
|
||||||
NotificationCenter.default.post(name: .__InterfaceObserverDidDetectWifiChange, object: nil)
|
NotificationCenter.default.post(name: .InterfaceObserverDidDetectWifiChange, object: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,6 +98,11 @@ public class InterfaceObserver: NSObject {
|
||||||
lastWifiName = currentWifiName
|
lastWifiName = currentWifiName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the current Wi-Fi SSID if any.
|
||||||
|
|
||||||
|
- Returns: The current Wi-Fi SSID if any.
|
||||||
|
**/
|
||||||
public func currentWifiNetworkName() -> String? {
|
public func currentWifiNetworkName() -> String? {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {
|
guard let interfaceNames = CNCopySupportedInterfaces() as? [CFString] else {
|
||||||
|
|
|
@ -36,29 +36,49 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Error raised by `Keychain` methods.
|
||||||
public enum KeychainError: Error {
|
public enum KeychainError: Error {
|
||||||
|
|
||||||
|
/// Unable to add.
|
||||||
case add
|
case add
|
||||||
|
|
||||||
|
/// Item not found.
|
||||||
case notFound
|
case notFound
|
||||||
|
|
||||||
case typeMismatch
|
// /// Unexpected item type returned.
|
||||||
|
// case typeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper for easy keychain access and modification.
|
||||||
public class Keychain {
|
public class Keychain {
|
||||||
private let service: String?
|
private let service: String?
|
||||||
|
|
||||||
private let accessGroup: String?
|
private let accessGroup: String?
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public init() {
|
public init() {
|
||||||
service = Bundle.main.bundleIdentifier
|
service = Bundle.main.bundleIdentifier
|
||||||
accessGroup = nil
|
accessGroup = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a keychain in an App Group.
|
||||||
|
|
||||||
|
- Parameter group: The App Group.
|
||||||
|
- Precondition: Proper App Group entitlements.
|
||||||
|
**/
|
||||||
public init(group: String) {
|
public init(group: String) {
|
||||||
service = nil
|
service = nil
|
||||||
accessGroup = group
|
accessGroup = group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a keychain in an App Group and a Team ID prefix.
|
||||||
|
|
||||||
|
- Parameter team: The Team ID prefix.
|
||||||
|
- Parameter group: The App Group.
|
||||||
|
- Precondition: Proper App Group entitlements.
|
||||||
|
**/
|
||||||
public init(team: String, group: String) {
|
public init(team: String, group: String) {
|
||||||
service = nil
|
service = nil
|
||||||
accessGroup = "\(team).\(group)"
|
accessGroup = "\(team).\(group)"
|
||||||
|
@ -66,6 +86,14 @@ public class Keychain {
|
||||||
|
|
||||||
// MARK: Password
|
// MARK: Password
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets a password.
|
||||||
|
|
||||||
|
- Parameter password: The password to set.
|
||||||
|
- Parameter username: The username to set the password for.
|
||||||
|
- Parameter label: An optional label.
|
||||||
|
- Throws: `KeychainError.add` if unable to add the password to the keychain.
|
||||||
|
**/
|
||||||
public func set(password: String, for username: String, label: String? = nil) throws {
|
public func set(password: String, for username: String, label: String? = nil) throws {
|
||||||
do {
|
do {
|
||||||
let currentPassword = try self.password(for: username)
|
let currentPassword = try self.password(for: username)
|
||||||
|
@ -94,6 +122,12 @@ public class Keychain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a password.
|
||||||
|
|
||||||
|
- Parameter username: The username to remove the password for.
|
||||||
|
- Returns: `true` if the password was successfully removed.
|
||||||
|
**/
|
||||||
@discardableResult public func removePassword(for username: String) -> Bool {
|
@discardableResult public func removePassword(for username: String) -> Bool {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -104,6 +138,13 @@ public class Keychain {
|
||||||
return (status == errSecSuccess)
|
return (status == errSecSuccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Returns: The password for the input username.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public func password(for username: String) throws -> String {
|
public func password(for username: String) throws -> String {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -126,6 +167,13 @@ public class Keychain {
|
||||||
return password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password reference.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Returns: The password reference for the input username.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public func passwordReference(for username: String) throws -> Data {
|
public func passwordReference(for username: String) throws -> Data {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
setScope(query: &query)
|
setScope(query: &query)
|
||||||
|
@ -145,6 +193,14 @@ public class Keychain {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a password associated with a password reference.
|
||||||
|
|
||||||
|
- Parameter username: The username to get the password for.
|
||||||
|
- Parameter reference: The password reference.
|
||||||
|
- Returns: The password for the input username and reference.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the password in the keychain.
|
||||||
|
**/
|
||||||
public static func password(for username: String, reference: Data) throws -> String {
|
public static func password(for username: String, reference: Data) throws -> String {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassGenericPassword
|
query[kSecClass as String] = kSecClassGenericPassword
|
||||||
|
@ -170,6 +226,14 @@ public class Keychain {
|
||||||
|
|
||||||
// https://forums.developer.apple.com/thread/13748
|
// https://forums.developer.apple.com/thread/13748
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Parameter data: The public key data.
|
||||||
|
- Returns: The `SecKey` object representing the public key.
|
||||||
|
- Throws: `KeychainError.add` if unable to add the public key to the keychain.
|
||||||
|
**/
|
||||||
public func add(publicKeyWithIdentifier identifier: String, data: Data) throws -> SecKey {
|
public func add(publicKeyWithIdentifier identifier: String, data: Data) throws -> SecKey {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
@ -188,6 +252,13 @@ public class Keychain {
|
||||||
return try publicKey(withIdentifier: identifier)
|
return try publicKey(withIdentifier: identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Returns: The `SecKey` object representing the public key.
|
||||||
|
- Throws: `KeychainError.notFound` if unable to find the public key in the keychain.
|
||||||
|
**/
|
||||||
public func publicKey(withIdentifier identifier: String) throws -> SecKey {
|
public func publicKey(withIdentifier identifier: String) throws -> SecKey {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
@ -211,6 +282,12 @@ public class Keychain {
|
||||||
return result as! SecKey
|
return result as! SecKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a public key.
|
||||||
|
|
||||||
|
- Parameter identifier: The unique identifier.
|
||||||
|
- Returns: `true` if the public key was successfully removed.
|
||||||
|
**/
|
||||||
@discardableResult public func remove(publicKeyWithIdentifier identifier: String) -> Bool {
|
@discardableResult public func remove(publicKeyWithIdentifier identifier: String) -> Bool {
|
||||||
var query = [String: Any]()
|
var query = [String: Any]()
|
||||||
query[kSecClass as String] = kSecClassKey
|
query[kSecClass as String] = kSecClassKey
|
||||||
|
|
|
@ -25,6 +25,13 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Entity able to produce a `LinkInterface`.
|
||||||
public protocol LinkProducer {
|
public protocol LinkProducer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a `LinkInterface`.
|
||||||
|
|
||||||
|
- Parameter mtu: The MTU value.
|
||||||
|
**/
|
||||||
func link(withMTU mtu: Int) -> LinkInterface
|
func link(withMTU mtu: Int) -> LinkInterface
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,22 +37,35 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftyBeaver
|
import SwiftyBeaver
|
||||||
|
|
||||||
|
/// Implements a `SwiftyBeaver.BaseDestination` logging to a memory buffer.
|
||||||
public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
private var buffer: [String] = []
|
private var buffer: [String] = []
|
||||||
|
|
||||||
|
/// Max number of retained lines.
|
||||||
public var maxLines: Int?
|
public var maxLines: Int?
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public override init() {
|
public override init() {
|
||||||
super.init()
|
super.init()
|
||||||
asynchronously = false
|
asynchronously = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func start(with existing: [String]) {
|
/**
|
||||||
|
Starts logging. Optionally prepend an array of lines.
|
||||||
|
|
||||||
|
- Parameter existing: The optional lines to prepend (none by default).
|
||||||
|
**/
|
||||||
|
public func start(with existing: [String] = []) {
|
||||||
execute(synchronously: true) {
|
execute(synchronously: true) {
|
||||||
self.buffer = existing
|
self.buffer = existing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Flushes the log content to an URL.
|
||||||
|
|
||||||
|
- Parameter url: The URL to write the log content to.
|
||||||
|
**/
|
||||||
public func flush(to url: URL) {
|
public func flush(to url: URL) {
|
||||||
execute(synchronously: true) {
|
execute(synchronously: true) {
|
||||||
let content = self.buffer.joined(separator: "\n")
|
let content = self.buffer.joined(separator: "\n")
|
||||||
|
@ -63,6 +76,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
// MARK: BaseDestination
|
// MARK: BaseDestination
|
||||||
|
|
||||||
// XXX: executed in SwiftyBeaver queue. DO NOT invoke execute* here (sync in sync would crash otherwise)
|
// XXX: executed in SwiftyBeaver queue. DO NOT invoke execute* here (sync in sync would crash otherwise)
|
||||||
|
/// :nodoc:
|
||||||
public override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
|
public override func send(_ level: SwiftyBeaver.Level, msg: String, thread: String, file: String, function: String, line: Int, context: Any?) -> String? {
|
||||||
guard let message = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) else {
|
guard let message = super.send(level, msg: msg, thread: thread, file: file, function: function, line: line) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -78,6 +92,7 @@ public class MemoryDestination: BaseDestination, CustomStringConvertible {
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var description: String {
|
public var description: String {
|
||||||
return executeSynchronously {
|
return executeSynchronously {
|
||||||
return self.buffer.joined(separator: "\n")
|
return self.buffer.joined(separator: "\n")
|
||||||
|
|
|
@ -40,11 +40,14 @@ import SwiftyBeaver
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
/// TCP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
public class NETCPSocket: NSObject, GenericSocket {
|
public class NETCPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public let impl: NWTCPConnection
|
public let impl: NWTCPConnection
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public init(impl: NWTCPConnection) {
|
public init(impl: NWTCPConnection) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
isActive = false
|
isActive = false
|
||||||
|
@ -57,18 +60,23 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public private(set) var isShutdown: Bool
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var remoteAddress: String? {
|
public var remoteAddress: String? {
|
||||||
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
return (impl.remoteAddress as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var hasBetterPath: Bool {
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public weak var delegate: GenericSocketDelegate?
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
|
@ -86,16 +94,19 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), options: .new, context: &NETCPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func unobserve() {
|
public func unobserve() {
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), context: &NETCPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.state), context: &NETCPSocket.linkContext)
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), context: &NETCPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWTCPConnection.hasBetterPath), context: &NETCPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func shutdown() {
|
public func shutdown() {
|
||||||
impl.writeClose()
|
impl.writeClose()
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func upgraded() -> GenericSocket? {
|
public func upgraded() -> GenericSocket? {
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -105,6 +116,7 @@ public class NETCPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
guard (context == &NETCPSocket.linkContext) else {
|
guard (context == &NETCPSocket.linkContext) else {
|
||||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
|
|
|
@ -37,11 +37,13 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
|
/// `TunnelInterface` implementation via NetworkExtension.
|
||||||
public class NETunnelInterface: TunnelInterface {
|
public class NETunnelInterface: TunnelInterface {
|
||||||
private weak var impl: NEPacketTunnelFlow?
|
private weak var impl: NEPacketTunnelFlow?
|
||||||
|
|
||||||
private let protocolNumber: NSNumber
|
private let protocolNumber: NSNumber
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public init(impl: NEPacketTunnelFlow, isIPv6: Bool) {
|
public init(impl: NEPacketTunnelFlow, isIPv6: Bool) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
protocolNumber = (isIPv6 ? AF_INET6 : AF_INET) as NSNumber
|
protocolNumber = (isIPv6 ? AF_INET6 : AF_INET) as NSNumber
|
||||||
|
@ -49,12 +51,14 @@ public class NETunnelInterface: TunnelInterface {
|
||||||
|
|
||||||
// MARK: TunnelInterface
|
// MARK: TunnelInterface
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var isPersistent: Bool {
|
public var isPersistent: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: IOInterface
|
// MARK: IOInterface
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
public func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
|
||||||
loopReadPackets(queue, handler)
|
loopReadPackets(queue, handler)
|
||||||
}
|
}
|
||||||
|
@ -70,11 +74,13 @@ public class NETunnelInterface: TunnelInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
public func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
impl?.writePackets([packet], withProtocols: [protocolNumber])
|
||||||
completionHandler?(nil)
|
completionHandler?(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
public func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let protocols = [NSNumber](repeating: protocolNumber, count: packets.count)
|
let protocols = [NSNumber](repeating: protocolNumber, count: packets.count)
|
||||||
impl?.writePackets(packets, withProtocols: protocols)
|
impl?.writePackets(packets, withProtocols: protocols)
|
||||||
|
|
|
@ -40,11 +40,14 @@ import SwiftyBeaver
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
/// UDP implementation of a `GenericSocket` via NetworkExtension.
|
||||||
public class NEUDPSocket: NSObject, GenericSocket {
|
public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
private static var linkContext = 0
|
private static var linkContext = 0
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public let impl: NWUDPSession
|
public let impl: NWUDPSession
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public init(impl: NWUDPSession) {
|
public init(impl: NWUDPSession) {
|
||||||
self.impl = impl
|
self.impl = impl
|
||||||
|
|
||||||
|
@ -58,18 +61,23 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
private var isActive: Bool
|
private var isActive: Bool
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public private(set) var isShutdown: Bool
|
public private(set) var isShutdown: Bool
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var remoteAddress: String? {
|
public var remoteAddress: String? {
|
||||||
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
return (impl.resolvedEndpoint as? NWHostEndpoint)?.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public var hasBetterPath: Bool {
|
public var hasBetterPath: Bool {
|
||||||
return impl.hasBetterPath
|
return impl.hasBetterPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public weak var delegate: GenericSocketDelegate?
|
public weak var delegate: GenericSocketDelegate?
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
public func observe(queue: DispatchQueue, activeTimeout: Int) {
|
||||||
isActive = false
|
isActive = false
|
||||||
|
|
||||||
|
@ -87,15 +95,18 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
impl.addObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), options: .new, context: &NEUDPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func unobserve() {
|
public func unobserve() {
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state), context: &NEUDPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state), context: &NEUDPSocket.linkContext)
|
||||||
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), context: &NEUDPSocket.linkContext)
|
impl.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.hasBetterPath), context: &NEUDPSocket.linkContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func shutdown() {
|
public func shutdown() {
|
||||||
impl.cancel()
|
impl.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public func upgraded() -> GenericSocket? {
|
public func upgraded() -> GenericSocket? {
|
||||||
guard impl.hasBetterPath else {
|
guard impl.hasBetterPath else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -105,6 +116,7 @@ public class NEUDPSocket: NSObject, GenericSocket {
|
||||||
|
|
||||||
// MARK: Connection KVO (any queue)
|
// MARK: Connection KVO (any queue)
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
guard (context == &NEUDPSocket.linkContext) else {
|
guard (context == &NEUDPSocket.linkContext) else {
|
||||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
|
|
|
@ -36,9 +36,18 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Convenient methods for DNS resolution.
|
||||||
public class DNSResolver {
|
public class DNSResolver {
|
||||||
private static let queue = DispatchQueue(label: "DNSResolver")
|
private static let queue = DispatchQueue(label: "DNSResolver")
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resolves a hostname asynchronously.
|
||||||
|
|
||||||
|
- Parameter hostname: The hostname to resolve.
|
||||||
|
- Parameter timeout: The timeout in milliseconds.
|
||||||
|
- Parameter queue: The queue to execute the `completionHandler` in.
|
||||||
|
- Parameter completionHandler: The completion handler with the resolved addresses and an optional error.
|
||||||
|
*/
|
||||||
public static func resolve(_ hostname: String, timeout: Int, queue: DispatchQueue, completionHandler: @escaping ([String]?, Error?) -> Void) {
|
public static func resolve(_ hostname: String, timeout: Int, queue: DispatchQueue, completionHandler: @escaping ([String]?, Error?) -> Void) {
|
||||||
var pendingHandler: (([String]?, Error?) -> Void)? = completionHandler
|
var pendingHandler: (([String]?, Error?) -> Void)? = completionHandler
|
||||||
let host = CFHostCreateWithName(nil, hostname as CFString).takeRetainedValue()
|
let host = CFHostCreateWithName(nil, hostname as CFString).takeRetainedValue()
|
||||||
|
@ -94,6 +103,12 @@ public class DNSResolver {
|
||||||
completionHandler(ipAddresses, nil)
|
completionHandler(ipAddresses, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a `String` representation from a numeric IPv4 address.
|
||||||
|
|
||||||
|
- Parameter ipv4: The IPv4 address as a 32-bit number.
|
||||||
|
- Returns: The string representation of `ipv4`.
|
||||||
|
*/
|
||||||
public static func string(fromIPv4 ipv4: UInt32) -> String {
|
public static func string(fromIPv4 ipv4: UInt32) -> String {
|
||||||
var addr = in_addr(s_addr: CFSwapInt32HostToBig(ipv4))
|
var addr = in_addr(s_addr: CFSwapInt32HostToBig(ipv4))
|
||||||
var buf = Data(count: Int(INET_ADDRSTRLEN))
|
var buf = Data(count: Int(INET_ADDRSTRLEN))
|
||||||
|
@ -110,6 +125,12 @@ public class DNSResolver {
|
||||||
return String(cString: result)
|
return String(cString: result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a numeric representation from an IPv4 address.
|
||||||
|
|
||||||
|
- Parameter string: The IPv4 address as a string.
|
||||||
|
- Returns: The numeric representation of `string`.
|
||||||
|
*/
|
||||||
public static func ipv4(fromString string: String) -> UInt32? {
|
public static func ipv4(fromString string: String) -> UInt32? {
|
||||||
var addr = in_addr()
|
var addr = in_addr()
|
||||||
let result = string.withCString {
|
let result = string.withCString {
|
||||||
|
|
Loading…
Reference in New Issue