parent
df549d48e6
commit
abf1101484
43 changed files with 988 additions and 325 deletions
|
|
@ -7,13 +7,13 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
|
||||
0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; };
|
||||
0BA6D73B2BA638D900BD4B55 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; };
|
||||
0BA6D73C2BA6393200BD4B55 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; };
|
||||
0BA6D73D2BA6393B00BD4B55 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; };
|
||||
0BA6D73E2BA6394B00BD4B55 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
|
||||
43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */; };
|
||||
D000363D2BB8928E00E582EC /* NetworkCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000363C2BB8928E00E582EC /* NetworkCarouselView.swift */; };
|
||||
D000363F2BB895FB00E582EC /* OAuth2.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000363E2BB895FB00E582EC /* OAuth2.swift */; };
|
||||
D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; };
|
||||
D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; };
|
||||
D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; };
|
||||
D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
||||
D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
||||
|
|
@ -158,6 +158,10 @@
|
|||
D00117392B30341C00D87C25 /* Shared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0B28F1552ABF463A000D44B0 /* DataTypes.swift */,
|
||||
D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */,
|
||||
D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */,
|
||||
0B46E8DF2AC918CA00BA2A3C /* Client.swift */,
|
||||
D001173A2B30341C00D87C25 /* Logging.swift */,
|
||||
D08252752B5C9FC4005DA378 /* Constants.swift */,
|
||||
D00117422B30348D00D87C25 /* Shared.xcconfig */,
|
||||
|
|
@ -199,10 +203,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */,
|
||||
0B46E8DF2AC918CA00BA2A3C /* Client.swift */,
|
||||
0B28F1552ABF463A000D44B0 /* DataTypes.swift */,
|
||||
D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */,
|
||||
D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */,
|
||||
D020F65929E4A697002790F6 /* Info.plist */,
|
||||
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */,
|
||||
D020F66629E4A95D002790F6 /* NetworkExtension-macOS.entitlements */,
|
||||
|
|
@ -456,7 +456,11 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D001173B2B30341C00D87C25 /* Logging.swift in Sources */,
|
||||
0BA6D73C2BA6393200BD4B55 /* NWConnection+Async.swift in Sources */,
|
||||
D08252762B5C9FC4005DA378 /* Constants.swift in Sources */,
|
||||
0BA6D73E2BA6394B00BD4B55 /* DataTypes.swift in Sources */,
|
||||
0BA6D73B2BA638D900BD4B55 /* Client.swift in Sources */,
|
||||
0BA6D73D2BA6393B00BD4B55 /* NewlineProtocolFramer.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -464,10 +468,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */,
|
||||
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */,
|
||||
D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */,
|
||||
0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */,
|
||||
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
import BurrowShared
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
final class Client {
|
||||
let connection: NWConnection
|
||||
|
||||
private let logger = Logger.logger(for: Client.self)
|
||||
private var generator = SystemRandomNumberGenerator()
|
||||
|
||||
convenience init() throws {
|
||||
self.init(url: try Constants.socketURL)
|
||||
}
|
||||
|
||||
init(url: URL) {
|
||||
let endpoint: NWEndpoint
|
||||
if url.isFileURL {
|
||||
endpoint = .unix(path: url.path(percentEncoded: false))
|
||||
} else {
|
||||
endpoint = .url(url)
|
||||
}
|
||||
|
||||
let parameters = NWParameters.tcp
|
||||
parameters.defaultProtocolStack
|
||||
.applicationProtocols
|
||||
.insert(NWProtocolFramer.Options(definition: NewlineProtocolFramer.definition), at: 0)
|
||||
connection = NWConnection(to: endpoint, using: parameters)
|
||||
connection.start(queue: .global())
|
||||
}
|
||||
|
||||
func request<U: Decodable>(_ request: any Request, type: U.Type = U.self) async throws -> U {
|
||||
do {
|
||||
var copy = request
|
||||
copy.id = generator.next(upperBound: UInt.max)
|
||||
let content = try JSONEncoder().encode(copy)
|
||||
logger.debug("> \(String(decoding: content, as: UTF8.self))")
|
||||
|
||||
try await self.connection.send(content: content)
|
||||
let (response, _, _) = try await connection.receiveMessage()
|
||||
|
||||
logger.debug("< \(String(decoding: response, as: UTF8.self))")
|
||||
return try JSONDecoder().decode(U.self, from: response)
|
||||
} catch {
|
||||
logger.error("\(error, privacy: .public)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
connection.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
extension Constants {
|
||||
static var socketURL: URL {
|
||||
get throws {
|
||||
try groupContainerURL.appending(component: "burrow.sock", directoryHint: .notDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
// swiftlint:disable identifier_name
|
||||
enum BurrowError: Error {
|
||||
case addrDoesntExist
|
||||
case resultIsError
|
||||
case cantParseResult
|
||||
case resultIsNone
|
||||
}
|
||||
|
||||
protocol Request: Codable where Command: Codable {
|
||||
associatedtype Command
|
||||
|
||||
var id: UInt { get set }
|
||||
var command: Command { get set }
|
||||
}
|
||||
|
||||
struct BurrowSingleCommand: Request {
|
||||
var id: UInt
|
||||
var command: String
|
||||
}
|
||||
|
||||
struct BurrowRequest<T>: Request where T: Codable {
|
||||
var id: UInt
|
||||
var command: T
|
||||
}
|
||||
|
||||
struct BurrowStartRequest: Codable {
|
||||
struct TunOptions: Codable {
|
||||
let name: String?
|
||||
let no_pi: Bool
|
||||
let tun_excl: Bool
|
||||
let tun_retrieve: Bool
|
||||
let address: [String]
|
||||
}
|
||||
struct StartOptions: Codable {
|
||||
let tun: TunOptions
|
||||
}
|
||||
let Start: StartOptions
|
||||
}
|
||||
|
||||
struct Response<T>: Decodable where T: Decodable {
|
||||
var id: UInt
|
||||
var result: T
|
||||
}
|
||||
|
||||
struct BurrowResult<T>: Codable where T: Codable {
|
||||
var Ok: T?
|
||||
var Err: String?
|
||||
}
|
||||
|
||||
struct ServerConfigData: Codable {
|
||||
struct InternalConfig: Codable {
|
||||
let address: [String]
|
||||
let name: String?
|
||||
let mtu: Int32?
|
||||
}
|
||||
let ServerConfig: InternalConfig
|
||||
}
|
||||
|
||||
// swiftlint:enable identifier_name
|
||||
|
|
@ -5,10 +5,14 @@ import os
|
|||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
private let logger = Logger.logger(for: PacketTunnelProvider.self)
|
||||
private var client: Client?
|
||||
|
||||
override init() {
|
||||
do {
|
||||
libburrow.spawnInProcess(socketPath: try Constants.socketURL.path)
|
||||
libburrow.spawnInProcess(
|
||||
socketPath: try Constants.socketURL.path(percentEncoded: false),
|
||||
dbPath: try Constants.dbURL.path(percentEncoded: false)
|
||||
)
|
||||
} catch {
|
||||
logger.error("Failed to spawn: \(error)")
|
||||
}
|
||||
|
|
@ -17,33 +21,17 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||
do {
|
||||
let client = try Client()
|
||||
self.client = client
|
||||
register_events(client)
|
||||
|
||||
let command = BurrowRequest(id: 0, command: "ServerConfig")
|
||||
let data = try await client.request(command, type: Response<BurrowResult<ServerConfigData>>.self)
|
||||
|
||||
let encoded = try JSONEncoder().encode(data.result)
|
||||
self.logger.log("Received final data: \(String(decoding: encoded, as: UTF8.self))")
|
||||
guard let serverconfig = data.result.Ok else {
|
||||
throw BurrowError.resultIsError
|
||||
}
|
||||
guard let tunNs = generateTunSettings(from: serverconfig) else {
|
||||
throw BurrowError.addrDoesntExist
|
||||
}
|
||||
try await self.setTunnelNetworkSettings(tunNs)
|
||||
self.logger.info("Set remote tunnel address to \(tunNs.tunnelRemoteAddress)")
|
||||
|
||||
let startRequest = BurrowRequest(
|
||||
id: .random(in: (.min)..<(.max)),
|
||||
command: BurrowStartRequest(
|
||||
Start: BurrowStartRequest.StartOptions(
|
||||
tun: BurrowStartRequest.TunOptions(
|
||||
name: nil, no_pi: false, tun_excl: false, tun_retrieve: true, address: []
|
||||
)
|
||||
)
|
||||
_ = try await self.loadTunSettings()
|
||||
let startRequest = Start(
|
||||
tun: Start.TunOptions(
|
||||
name: nil, no_pi: false, tun_excl: false, tun_retrieve: true, address: []
|
||||
)
|
||||
)
|
||||
let response = try await client.request(startRequest, type: Response<BurrowResult<String>>.self)
|
||||
self.logger.log("Received start server response: \(String(describing: response.result))")
|
||||
let response = try await client.request(startRequest, type: BurrowResult<AnyResponseData>.self)
|
||||
self.logger.log("Received start server response: \(String(describing: response))")
|
||||
} catch {
|
||||
self.logger.error("Failed to start tunnel: \(error)")
|
||||
throw error
|
||||
|
|
@ -53,20 +41,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
override func stopTunnel(with reason: NEProviderStopReason) async {
|
||||
do {
|
||||
let client = try Client()
|
||||
let command = BurrowRequest(id: 0, command: "Stop")
|
||||
let data = try await client.request(command, type: Response<BurrowResult<String>>.self)
|
||||
_ = try await client.single_request("Stop", type: BurrowResult<AnyResponseData>.self)
|
||||
self.logger.log("Stopped client.")
|
||||
} catch {
|
||||
self.logger.error("Failed to stop tunnel: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func generateTunSettings(from: ServerConfigData) -> NETunnelNetworkSettings? {
|
||||
let cfig = from.ServerConfig
|
||||
func loadTunSettings() async throws -> ServerConfig {
|
||||
guard let client = self.client else {
|
||||
throw BurrowError.noClient
|
||||
}
|
||||
let srvConfig = try await client.single_request("ServerConfig", type: BurrowResult<ServerConfig>.self)
|
||||
guard let serverconfig = srvConfig.Ok else {
|
||||
throw BurrowError.resultIsError
|
||||
}
|
||||
guard let tunNs = generateTunSettings(from: serverconfig) else {
|
||||
throw BurrowError.addrDoesntExist
|
||||
}
|
||||
try await self.setTunnelNetworkSettings(tunNs)
|
||||
self.logger.info("Set remote tunnel address to \(tunNs.tunnelRemoteAddress)")
|
||||
return serverconfig
|
||||
}
|
||||
private func generateTunSettings(from: ServerConfig) -> NETunnelNetworkSettings? {
|
||||
// Using a makeshift remote tunnel address
|
||||
let nst = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
|
||||
var v4Addresses = [String]()
|
||||
var v6Addresses = [String]()
|
||||
for addr in cfig.address {
|
||||
for addr in from.address {
|
||||
if IPv4Address(addr) != nil {
|
||||
v6Addresses.append(addr)
|
||||
}
|
||||
|
|
@ -81,4 +82,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||
logger.log("Initialized ipv4 settings: \(nst.ipv4Settings)")
|
||||
return nst
|
||||
}
|
||||
func register_events(_ client: Client) {
|
||||
client.on_event(.ConfigChange) { (cfig: ServerConfig) in
|
||||
self.logger.info("Config Change Notification: \(String(describing: cfig))")
|
||||
self.setTunnelNetworkSettings(self.generateTunSettings(from: cfig))
|
||||
self.logger.info("Updated Tunnel Network Settings.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
__attribute__((__swift_name__("spawnInProcess(socketPath:)")))
|
||||
extern void spawn_in_process(const char * __nullable path);
|
||||
__attribute__((__swift_name__("spawnInProcess(socketPath:dbPath:)")))
|
||||
extern void spawn_in_process(const char * __nullable socket_path, const char * __nullable db_path);
|
||||
|
|
|
|||
106
Apple/Shared/Client.swift
Normal file
106
Apple/Shared/Client.swift
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import Foundation
|
||||
import Network
|
||||
|
||||
public final class Client {
|
||||
let connection: NWConnection
|
||||
|
||||
private let logger = Logger.logger(for: Client.self)
|
||||
private var generator = SystemRandomNumberGenerator()
|
||||
private var continuations: [UInt: UnsafeContinuation<Data, Error>] = [:]
|
||||
private var eventMap: [NotificationType: [(Data) throws -> Void]] = [:]
|
||||
private var task: Task<Void, Error>?
|
||||
|
||||
public convenience init() throws {
|
||||
self.init(url: try Constants.socketURL)
|
||||
}
|
||||
|
||||
public init(url: URL) {
|
||||
let endpoint: NWEndpoint
|
||||
if url.isFileURL {
|
||||
endpoint = .unix(path: url.path(percentEncoded: false))
|
||||
} else {
|
||||
endpoint = .url(url)
|
||||
}
|
||||
|
||||
let parameters = NWParameters.tcp
|
||||
parameters.defaultProtocolStack
|
||||
.applicationProtocols
|
||||
.insert(NWProtocolFramer.Options(definition: NewlineProtocolFramer.definition), at: 0)
|
||||
let connection = NWConnection(to: endpoint, using: parameters)
|
||||
connection.start(queue: .global())
|
||||
self.connection = connection
|
||||
self.task = Task { [weak self] in
|
||||
while true {
|
||||
let (data, _, _) = try await connection.receiveMessage()
|
||||
let peek = try JSONDecoder().decode(MessagePeek.self, from: data)
|
||||
switch peek.type {
|
||||
case .Response:
|
||||
let response = try JSONDecoder().decode(ResponsePeek.self, from: data)
|
||||
self?.logger.info("Received response for \(response.id)")
|
||||
guard let continuations = self?.continuations else {return}
|
||||
self?.logger.debug("All keys in continuation table: \(continuations.keys)")
|
||||
guard let continuation = self?.continuations[response.id] else { return }
|
||||
self?.logger.debug("Got matching continuation")
|
||||
continuation.resume(returning: data)
|
||||
case .Notification:
|
||||
let peek = try JSONDecoder().decode(NotificationPeek.self, from: data)
|
||||
guard let handlers = self?.eventMap[peek.method] else { continue }
|
||||
_ = try handlers.map { try $0(data) }
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private func send<T: Request, U: Decodable>(_ request: T) async throws -> U {
|
||||
let data: Data = try await withUnsafeThrowingContinuation { continuation in
|
||||
continuations[request.id] = continuation
|
||||
do {
|
||||
let data = try JSONEncoder().encode(request)
|
||||
let completion: NWConnection.SendCompletion = .contentProcessed { error in
|
||||
guard let error = error else {
|
||||
return
|
||||
}
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
connection.send(content: data, completion: completion)
|
||||
} catch {
|
||||
continuation.resume(throwing: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
self.logger.debug("Got response data: \(String(describing: data.base64EncodedString()))")
|
||||
let res = try JSONDecoder().decode(Response<U>.self, from: data)
|
||||
self.logger.debug("Got response data decoded: \(String(describing: res))")
|
||||
return res.result
|
||||
}
|
||||
public func request<T: Codable, U: Decodable>(_ request: T, type: U.Type = U.self) async throws -> U {
|
||||
let req = BurrowRequest(
|
||||
id: generator.next(upperBound: UInt.max),
|
||||
command: request
|
||||
)
|
||||
return try await send(req)
|
||||
}
|
||||
public func single_request<U: Decodable>(_ request: String, type: U.Type = U.self) async throws -> U {
|
||||
let req = BurrowSimpleRequest(
|
||||
id: generator.next(upperBound: UInt.max),
|
||||
command: request
|
||||
)
|
||||
return try await send(req)
|
||||
}
|
||||
public func on_event<T: Codable>(_ event: NotificationType, callable: @escaping (T) throws -> Void) {
|
||||
let action = { data in
|
||||
let decoded = try JSONDecoder().decode(Notification<T>.self, from: data)
|
||||
try callable(decoded.params)
|
||||
}
|
||||
if eventMap[event] != nil {
|
||||
eventMap[event]?.append(action)
|
||||
} else {
|
||||
eventMap[event] = [action]
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
connection.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -20,4 +20,14 @@ public enum Constants {
|
|||
}
|
||||
return .success(groupContainerURL)
|
||||
}()
|
||||
public static var socketURL: URL {
|
||||
get throws {
|
||||
try groupContainerURL.appending(component: "burrow.sock", directoryHint: .notDirectory)
|
||||
}
|
||||
}
|
||||
public static var dbURL: URL {
|
||||
get throws {
|
||||
try groupContainerURL.appending(component: "burrow.db", directoryHint: .notDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
139
Apple/Shared/DataTypes.swift
Normal file
139
Apple/Shared/DataTypes.swift
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import Foundation
|
||||
|
||||
// swiftlint:disable identifier_name raw_value_for_camel_cased_codable_enum
|
||||
public enum BurrowError: Error {
|
||||
case addrDoesntExist
|
||||
case resultIsError
|
||||
case cantParseResult
|
||||
case resultIsNone
|
||||
case noClient
|
||||
}
|
||||
|
||||
public protocol Request: Codable where Params: Codable {
|
||||
associatedtype Params
|
||||
|
||||
var id: UInt { get set }
|
||||
var method: String { get set }
|
||||
var params: Params? { get set }
|
||||
}
|
||||
|
||||
public enum MessageType: String, Codable {
|
||||
case Request
|
||||
case Response
|
||||
case Notification
|
||||
}
|
||||
|
||||
public struct MessagePeek: Codable {
|
||||
public var type: MessageType
|
||||
public init(type: MessageType) {
|
||||
self.type = type
|
||||
}
|
||||
}
|
||||
|
||||
public struct BurrowSimpleRequest: Request {
|
||||
public var id: UInt
|
||||
public var method: String
|
||||
public var params: String?
|
||||
public init(id: UInt, command: String, params: String? = nil) {
|
||||
self.id = id
|
||||
self.method = command
|
||||
self.params = params
|
||||
}
|
||||
}
|
||||
|
||||
public struct BurrowRequest<T>: Request where T: Codable {
|
||||
public var id: UInt
|
||||
public var method: String
|
||||
public var params: T?
|
||||
public init(id: UInt, command: T) {
|
||||
self.id = id
|
||||
self.method = "\(T.self)"
|
||||
self.params = command
|
||||
}
|
||||
}
|
||||
|
||||
public struct Response<T>: Decodable where T: Decodable {
|
||||
public var id: UInt
|
||||
public var result: T
|
||||
public init(id: UInt, result: T) {
|
||||
self.id = id
|
||||
self.result = result
|
||||
}
|
||||
}
|
||||
|
||||
public struct ResponsePeek: Codable {
|
||||
public var id: UInt
|
||||
public init(id: UInt) {
|
||||
self.id = id
|
||||
}
|
||||
}
|
||||
|
||||
public enum NotificationType: String, Codable {
|
||||
case ConfigChange
|
||||
}
|
||||
|
||||
public struct Notification<T>: Codable where T: Codable {
|
||||
public var method: NotificationType
|
||||
public var params: T
|
||||
public init(method: NotificationType, params: T) {
|
||||
self.method = method
|
||||
self.params = params
|
||||
}
|
||||
}
|
||||
|
||||
public struct NotificationPeek: Codable {
|
||||
public var method: NotificationType
|
||||
public init(method: NotificationType) {
|
||||
self.method = method
|
||||
}
|
||||
}
|
||||
|
||||
public struct AnyResponseData: Codable {
|
||||
public var type: String
|
||||
public init(type: String) {
|
||||
self.type = type
|
||||
}
|
||||
}
|
||||
|
||||
public struct BurrowResult<T>: Codable where T: Codable {
|
||||
public var Ok: T?
|
||||
public var Err: String?
|
||||
public init(Ok: T, Err: String? = nil) {
|
||||
self.Ok = Ok
|
||||
self.Err = Err
|
||||
}
|
||||
}
|
||||
|
||||
public struct ServerConfig: Codable {
|
||||
public let address: [String]
|
||||
public let name: String?
|
||||
public let mtu: Int32?
|
||||
public init(address: [String], name: String?, mtu: Int32?) {
|
||||
self.address = address
|
||||
self.name = name
|
||||
self.mtu = mtu
|
||||
}
|
||||
}
|
||||
|
||||
public struct Start: Codable {
|
||||
public struct TunOptions: Codable {
|
||||
public let name: String?
|
||||
public let no_pi: Bool
|
||||
public let tun_excl: Bool
|
||||
public let tun_retrieve: Bool
|
||||
public let address: [String]
|
||||
public init(name: String?, no_pi: Bool, tun_excl: Bool, tun_retrieve: Bool, address: [String]) {
|
||||
self.name = name
|
||||
self.no_pi = no_pi
|
||||
self.tun_excl = tun_excl
|
||||
self.tun_retrieve = tun_retrieve
|
||||
self.address = address
|
||||
}
|
||||
}
|
||||
public let tun: TunOptions
|
||||
public init(tun: TunOptions) {
|
||||
self.tun = tun
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:enable identifier_name raw_value_for_camel_cased_codable_enum
|
||||
Loading…
Add table
Add a link
Reference in a new issue